Get-sMFA-AWS-Assume_SAML_Role.ps1
<#
.SYNOPSIS Assume AWS Role from SAML. .DESCRIPTION Assumes AWS Role(s) from SAML asseration. Dependencies: * System which executes a script must have Microsoft Framework 4.6.2 and above installed. * SecureMFA_SupportTools.dll file must be present in script directory. * SecureMFA_SupportTools.json configuration file must be present in script directory. * Following latest AWS modules must be installed on the system AWS.Tools.SecurityToken and AWS.Tools.Common * Uses default system proxy settings Bellow is a sample of valid Json config file with minimal configuration required for script to work: { "aws_sts_saml_endpoint": "https://FQDN/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices", "ui_environment": "MyCompany", "proxy_server": "http://proxy.adatum.labnet:8080", "proxy_bypass_localaddresses": "false" } .PARAMETER AWS_ProfileName Specifies profile name in the credentials file. .PARAMETER AWS_SessionDurationSeconds Specifies how long the assumed role keys are valid after retrieval from AWS STS; the default value is one hour. .PARAMETER Region Specify defaul AWS region for CLI profile. .PARAMETER Output Specify defaul AWS output format for CLI profile. .NOTES Version: 1.0.1.8 Author: SecureMfa.com Creation Date: 11/11/2021 Purpose/Change: New release .EXAMPLE C:\PS> Get-sMFA-AWS-Assume_SAML_Role -AWS_ProfileName sMFA-SAML This command will assume available user roles from AWS STS SAML Endpoint and configure 'sMFA-SAML' profile in /.aws/credentials file with aws_access_key_id and aws_secret_access_key #> #> Function Get-sMFA-AWS-Assume_SAML_Role { Param ( [Parameter(Mandatory=$false,ParameterSetName="Default")] [String]$AWS_ProfileName = "sMFA-SAML", [Parameter(Mandatory=$false,ParameterSetName="Default")] [int]$AWS_SessionDurationSeconds = 3600, [Parameter(Mandatory=$false,ParameterSetName="Default")] [string]$region = 'eu-west-1', [Parameter(Mandatory=$false,ParameterSetName="Default")] [string]$output = 'json' ) DynamicParam { # create a dictionary to return, and collection of parameters $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] # create a new [string] parameter for all parameter sets, and decorate with a [ValidateSet] $dynParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Profile", [String], $attributeCollection) $attributes = New-Object System.Management.Automation.ParameterAttribute $fname = "Profiles.json" $paramOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList (ConvertFrom-Json (Get-Content (join-path $PSScriptRoot $fname) -Raw)) $attributeCollection.Add($attributes) $attributeCollection.Add($paramOptions) $paramDictionary.Add("Profile", $dynParam) return $paramDictionary } Process { #Static Parameters $AWS_Directory = "${env:\userprofile}\.aws\" $AWS_Directory_cred = "$AWS_Directory`credentials" $Event_Source = "SecureMFA_SupportTools" $SSO_auth_success = $false; $SSO_roles_success = $false; $SSO_profile_deleted = $false; #Checking Dependencies #EventLog source dependency $ErrMsg = "ResetOTP EventLog source is missing. Please execute following PS command 'New-EventLog -Source SecureMFA_SupportTools -LogName Application' on the system before using the app." if (((Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application).pschildname | where { $_ -eq $Event_Source} | measure).Count -eq 0) {write-host $ErrMsg -ForegroundColor red; pause; break} #PS Modules Dependencies if ((Get-InstalledModule -Name "AWS.Tools.SecurityToken" -MinimumVersion 4.1.15.0 -ErrorAction SilentlyContinue) -eq $null) { Throw "PowerShell module 'AWS.Tools.SecurityToken' is required for this command. Please install a module with the following command: Install-Module -Name 'AWS.Tools.SecurityToken' -Repository PSGallery -MinimumVersion 4.1.15.0"} if ((Get-InstalledModule -Name "AWS.Tools.Common" -MinimumVersion 4.1.15.0 -ErrorAction SilentlyContinue) -eq $null) { Throw "PowerShell module 'AWS.Tools.Common' is required for this command. Please install a module with the following command: Install-Module -Name 'AWS.Tools.Common' -Repository PSGallery -MinimumVersion 4.1.15.0"} #Config file dependency if ( $PSBoundParameters.Keys.Contains("Profile") ) { $configfile = (Join-Path -Path $PSScriptRoot -ChildPath ($PSBoundParameters.Profile + '_SecureMFA_SupportTools.json')) } else {$configfile = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFA_SupportTools.json)} #Test config file path. $ErrMsg = "$configfile file is missing. Please copy a file to script directory and try again." if (!(Test-Path $configfile)) { write-host $ErrMsg -ForegroundColor red; pause; break } #DLL file dependency $dllpath = (Join-Path -Path $PSScriptRoot -ChildPath SecureMFA_SupportTools.dll) $ErrMsg = "$configfile file is missing. Please copy a file to script directory and try again." if (!(Test-Path $dllpath)) { write-host $ErrMsg -ForegroundColor red; pause; break } #Read JSON file Configuration $json = Get-Content -Raw $configfile | ConvertFrom-Json $url = $json.aws_sts_saml_endpoint $environment = $json.ui_environment $webproxy = $json.proxy_server $bypassproxyonlocal; if($json.proxy_bypass_localaddresses -eq "true") {$bypassproxyonlocal = 1} else {$bypassproxyonlocal = 0} #Get user's input if required write-host " -- Assume AWS Role from SAML for $environment --" -ForegroundColor Green Write-host "aws_sts_saml_endpoint from JSON configuration file:" $url -ForegroundColor Cyan Try { [System.Reflection.Assembly]::LoadFile($dllpath) | Out-Null [string]$saml_asseration = "" #Retreve client token using Hasicorp Vaul OIDC auth flow $atoken = [SecureMFA_SupportTools.IDPAUTH]::RetrieveSAMLToken($Url,[ref]$saml_asseration) | Out-String if($saml_asseration.Length -ge 100) {$SSO_auth_success = $true; write-host "SSO authentication has been successful;"} else {write-host "An error occurred during SSO authentication; please check configuration and try again."} #Decode Base64 and extract Roles from SAMLResponse value if($SSO_auth_success) { [xml]$DecodedToken = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($saml_asseration)) $SessionName = ($DecodedToken.Response.Assertion.AttributeStatement.Attribute | where {$_.name -eq 'https://aws.amazon.com/SAML/Attributes/RoleSessionName'}).AttributeValue $SessionRoles = ($DecodedToken.Response.Assertion.AttributeStatement.Attribute | where {$_.name -eq 'https://aws.amazon.com/SAML/Attributes/Role'}).AttributeValue write-host "User: $SessionName has claim roles: $SessionRoles" } #Get SAML Roles if ($SessionRoles.Count -ge 2) { write-host "List of roles from SAML assertation" $i = 0 Foreach ($SessionRole in $SessionRoles) { write-host "[$i]" $SessionRole -ForegroundColor Cyan $i++ } #Select a SAML role Do { $SessionRoleNumber = Read-host "Select a role [Number]"} while (( $SessionRoleNumber -ge $SessionRoles.Count ) -or ( $SessionRoleNumber -lt 0 )); #Converts a positive or negative integer to a positive integer $SessionSelectedRole = $SessionRoles[[Math]::Abs($SessionRoleNumber)]; $SSO_roles_success = $true; } elseif ($SessionRoles.Count -eq 1) {$SessionSelectedRole = $SessionRoles ; $SSO_roles_success = $true;} else { write-host "No SAML roles exist for AWS servcie in asssertation." -ForegroundColor Red} #If SAML ROLES eixist if($SSO_roles_success) { #Convert selected role to role and principal arns $SessionSelectedRole $arns = ($SessionSelectedRole) -split ',' [string]$role_arn = ($arns | Select-String ":role/") -split ' ' [string]$principal_arn = ($arns | Select-String ":saml-provider/") -split ' ' # Get STS Credentials with SAML $sts = Use-STSRoleWithSAML -PrincipalArn $principal_arn -RoleArn $role_arn -SAMLAssertion $saml_asseration -DurationInSeconds $AWS_SessionDurationSeconds # Extract STS responce details [string]$aws_access_key_id = $sts.Credentials.AccessKeyId; [string]$aws_secret_access_key = $sts.Credentials.SecretAccessKey; [string]$aws_session_token = $sts.Credentials.SessionToken; [string]$aws_saml_accountid = ($sts.AssumedRoleUser.Arn | select-string -pattern "(?:sts::)(.*?)(?::)").Matches | % {$_.groups[1].Value} [string]$aws_saml_role = ($sts.AssumedRoleUser.Arn | select-string -pattern "(?:assumed-role/)(.*?)(?:/)").Matches | % {$_.groups[1].Value} [string]$aws_saml_duration_seconds = $AWS_SessionDurationSeconds [string]$aws_saml_expiration = $sts.Credentials.Expiration [string]$aws_saml_user_display = $sts.AssumedRoleUser.AssumedRoleId # Create the folder and credentials file if it doesn't exist if(!(Test-Path $AWS_Directory)){New-Item -ItemType Directory $AWS_Directory} if(!(Test-Path $AWS_Directory_cred)){New-Item -ItemType File $AWS_Directory_cred} #Delete if profile exist if (Get-AWSCredential -ProfileName $AWS_ProfileName) {Remove-AWSCredentialProfile -ProfileName $AWS_ProfileName -ProfileLocation $AWS_Directory_cred -Force; $SSO_profile_deleted = $true ; write-host "AWS credentilas profile [$AWS_ProfileName] has been deleted." -ForegroundColor Yellow} # Must set return type as array to handle null values [Array]$AWS_credentials = Get-Content -Path $AWS_Directory_cred -Raw # Add blank line if needed if(!($SSO_profile_deleted)) {if($AWS_credentials -and $AWS_credentials[-1] -ne ''){$AWS_credentials += '' }} $AWS_credentials += "[$AWS_ProfileName]" $AWS_credentials += "output = $output" $AWS_credentials += "region = $region" $AWS_credentials += "aws_access_key_id = $aws_access_key_id" $AWS_credentials += "aws_secret_access_key = $aws_secret_access_key" $AWS_credentials += "aws_session_token = $aws_session_token" $AWS_credentials += "aws_saml_accountid = $aws_saml_accountid" $AWS_credentials += "aws_saml_role = $aws_saml_role" $AWS_credentials += "aws_saml_duration = $aws_saml_duration_seconds" # Save Changes as UTF-8 without a BOM [System.IO.File]::WriteAllLines($AWS_Directory_cred, $AWS_credentials, [System.Text.UTF8Encoding]($False)) write-host "#############################################################################################" -ForegroundColor Green write-host "AWS credentilas profile [$AWS_ProfileName] has been created." -ForegroundColor Green write-host "Credentials file: $AWS_Directory_cred" -ForegroundColor Green write-host "User: $aws_saml_user_display" -ForegroundColor Green write-host "Role: $aws_saml_role" -ForegroundColor Green write-host "AWS token expires: $aws_saml_expiration" -ForegroundColor Green write-host "To renew AWS assumed role token run 'Get-sMFA-AWS-Assume_SAML_Role' PowerShell command again." -ForegroundColor Green write-host "#############################################################################################" -ForegroundColor Green } #NO SAML ROLES eixist else { Write-host "No AWS SAML roles were found to be extracted from SAML assertion using $url . Please make sure that your STS is sending ROLE attribute values using the following 'https://aws.amazon.com/SAML/Attributes/Role'" -ForegroundColor Yellow} #Cleanup $AWS_credentials = $null } #On error acction catch [System.Exception] { $completed = get-date $line = $_.InvocationInfo.ScriptLineNumber $msg = $_.Exception.Message Write-Host -ForegroundColor Red "Error: $msg" Write-EventLog –LogName Application –Source $Event_Source –EntryType Error –EventID 5559 –Message “$msg Executed by: $env:username Computer: $env:computername Line: $line” } } } |