Last active
January 12, 2022 00:23
-
-
Save jgard/17262e0fc073c82bc7930db2f5603446 to your computer and use it in GitHub Desktop.
Powershell: Get AWS temporary credentials via ADFS including support for RSA SecurID MFA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function Get-AWSTempCred { | |
[CmdletBinding()] | |
param ( | |
[string]$ADFSHost='adfs.domain.com', ##Change for environment-appropriate default if desired | |
[string]$RelyingParty = 'urn:amazon:webservices', | |
[pscredential]$Credential | |
) | |
$WebRequestParams=@{ #Initialize parameters object | |
Uri = "https://$ADFSHost/adfs/ls/IdpInitiatedSignon.aspx?LoginToRP=$RelyingParty" | |
Method = 'POST' | |
ContentType = 'application/x-www-form-urlencoded' | |
SessionVariable = 'WebSession' | |
UseBasicParsing = $true | |
} | |
if ($Credential) { | |
$WebRequestParams.Add('Body',@{UserName=$Credential.UserName;Password=$Credential.GetNetworkCredential().Password}) | |
} else { | |
$WebRequestParams.Add('UseDefaultCredentials',$true) | |
} | |
#Initial post to ADFS | |
$InitialResponse=Invoke-WebRequest @WebRequestParams | |
$SAMLResponseEncoded=$InitialResponse.InputFields.FindByName('SAMLResponse').value | |
if (!$SAMLResponseEncoded) { #Initial result from ADFS didn't have assertion | |
if ($InitialResponse.InputFields.FindByName('AuthMethod').value -eq 'SecurIDv2Authentication') { #Handle RSA SecurID | |
$SAMLResponseEncoded = Read-SecurIDv2Authentication -WebRequestParams $WebRequestParams -InitialResponse $InitialResponse | |
} | |
} | |
if (!$SAMLResponseEncoded) { | |
Throw "No valid ADFS assertion received. Suggestion: Supply alternate credentials, use different ADFSHost, or a new MFA method is not yet supported by this module." | |
} | |
#Evaluate SAML Response | |
$SAMLResponseDecoded=[xml]([System.Text.Encoding]::utf8.GetString([System.Convert]::FromBase64String($SAMLResponseEncoded))) | select -ExpandProperty response | |
$AvailableRoles = $SAMLResponseDecoded.Assertion.AttributeStatement.Attribute |?{$_.name -eq 'https://aws.amazon.com/SAML/Attributes/Role'} | %{ | |
$_.AttributeValue |%{ | |
[PSCustomObject]@{"SAMLProvider" = ($_ -split ",")[0];"Role" = ($_ -split ",")[1]} | |
} | |
} | |
$AvailableRoles = @($AvailableRoles) #Force to be an array to simplfy role count assessment | |
if ($AvailableRoles.count -eq 0) { | |
Throw "No available AWS roles found in ADFS response." | |
} | |
If ($AvailableRoles.count -gt 1) { #Choose role logic | |
$ChosenRole= $AvailableRoles | Get-Choice -Message "Choose which role to assume" -Property 'Role' | |
} else { | |
$ChosenRole=$AvailableRoles[0] | |
} | |
#Send token to AWS STS | |
try { | |
Write-Host "Using role $($ChosenRole.Role)" | |
$AssumedRole=Use-STSRoleWithSAML -SAMLAssertion $SAMLResponseEncoded -PrincipalArn $ChosenRole.SAMLProvider -RoleArn $ChosenRole.Role -ErrorAction Stop | |
} catch{ | |
Write-Warning "STS error: $($_.Exception.Message)" | |
Throw "Failure calling AWS STS service. Likely issues: a) Outgoing internet connectivity or proxy issue, or b) Problem with ADFS trust, claims, or role." | |
} | |
Write-Host "New access key: $($AssumedRole.Credentials.AccessKeyId), expires $($AssumedRole.Credentials.Expiration)" | |
Write-Host "Setting as default AWSCredential for future AWSPowershell usage, by exporting to `$Global:StoredAWSCredentials" | |
#Commented out printing of sensitive information: | |
#Write-host "AccessKey=$($AssumedRole.Credentials.AccessKeyId)" | |
#Write-host "SecretKey=$($AssumedRole.Credentials.SecretAccessKey)" | |
#Write-host "SessionToken=$($AssumedRole.Credentials.SessionToken)" | |
Set-AWSCredential -AccessKey $AssumedRole.Credentials.AccessKeyId -SecretKey $AssumedRole.Credentials.SecretAccessKey -SessionToken $AssumedRole.Credentials.SessionToken | |
$Global:StoredAWSCredentials = $StoredAWSCredentials | |
} | |
function Read-SecurIDv2Authentication { | |
[CmdletBinding()] | |
param ( | |
[hashtable]$WebRequestParams, | |
$InitialResponse | |
) | |
$WebRequestParams.Remove('SessionVariable') | |
$WebRequestParams.Add('WebSession',$WebSession) | |
$WebRequestParams['Body'] = @{AuthMethod=$InitialResponse.InputFields.FindByName('AuthMethod').value;Context=$InitialResponse.InputFields.FindByName('Context').value;InitStatus='true'} | |
if ($InitialResponse.BaseResponse.ResponseUri.AbsoluteUri) { | |
$WebRequestParams['Uri'] = $InitialResponse.BaseResponse.ResponseUri.AbsoluteUri | |
} else { | |
$WebRequestParams['Uri'] = $InitialResponse.BaseResponse.RequestMessage.RequestUri.AbsoluteUri | |
} | |
$RSAInitResponse=Invoke-WebRequest @WebRequestParams | |
if ($RSAInitResponse.InputFields.FindById('passcodeInput')) { | |
$passcode = Read-Host 'Enter RSA SecurID passcode' -AsSecureString | |
$WebRequestParams['Body'] = @{AuthMethod=$RSAInitResponse.InputFields.FindByName('AuthMethod').value;Context=$RSAInitResponse.InputFields.FindByName('Context').value;Passcode=(New-Object PSCredential "user",$passcode).GetNetworkCredential().Password} | |
if ($RSAInitResponse.BaseResponse.ResponseUri.AbsoluteUri) { | |
$WebRequestParams['Uri'] = $RSAInitResponse.BaseResponse.ResponseUri.AbsoluteUri | |
} else { | |
$WebRequestParams['Uri'] = $RSAInitResponse.BaseResponse.RequestMessage.RequestUri.AbsoluteUri | |
} | |
$RSAResponse = Invoke-WebRequest @WebRequestParams | |
$RSAResponse.InputFields.FindByName('SAMLResponse').value | |
} | |
} | |
Function Get-Choice { | |
[cmdletbinding()] | |
param( | |
[parameter( | |
Mandatory = $true, | |
ValueFromPipeline = $true)] | |
[string[]]$Options, | |
[string]$Property, | |
[string]$Message="Make a selection" | |
) | |
Begin { | |
$i=0 | |
$array = @() | |
} | |
Process { | |
$i++ | |
$array += $_ | |
write-host " $i. " -NoNewline -ForegroundColor 'Green' | |
if ($Property) { | |
write-host $_.$Property | |
} else { | |
write-host $_ | |
} | |
} | |
End { | |
Do { | |
$answer=Read-Host -Prompt $message | |
try{ | |
$Chosen = $array[[int]$answer-1] | |
} catch { | |
} | |
if (!$Chosen) { | |
Write-Host "Invalid choice '$answer'. Please try again or press Ctrl+C to quit." -ForegroundColor Yellow | |
} else { | |
$Chosen | |
} | |
} While (!$Chosen) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks a ton !! works like wonder...