AD Authentication for AWS console

When you get started with Amazon Web Services (AWS) one thing to do early is secure access to the web console.  Rather than manage another set of user accounts you can reuse your corporate directory (Microsoft Active Directory) to login to the AWS console.  You use AD Federation Services to do this.  Also, if you keep your ADFS server internal, then your AWS console is not accessible from the public Internet.

Start with this presentation:

https://www.youtube.com/watch?v=ZhvXW-ILyPs

Follow up with this one specifically about federation services

https://www.youtube.com/watch?v=debJ3o5w0MA

The original post on this is http://blogs.aws.amazon.com/security/post/Tx71TWXXJ3UI14/Enabling-Federation-to-AWS-using-Windows-Active-Directory-ADFS-and-SAML-2-0
It’s getting a little dated but gives you a walk though of the general process for setting this up.
The steps in the above guide can be automated with PowerShell. This guide will get you 90% of the way there: http://www.padisetty.com/2014/02/powershell-automation-to-give-aws.html
Extra steps required for production deployment:

You can even brand the ADFS logon page. http://thinketg.com/adfs-3-0-logon-page-customization

Update: See next post for the other half of this puzzle, getting AWS API keys via AD authentication.

Update: Switched from using mail attribute to userPrincipleName attribute so this works for test or service accounts that don’t have mailboxes.

NOTE: Switching between blog content management systems over the years has messed with the formatting for the code below. This post is very old and likely things have moved on. This is just for reference. Seek more up to date guidance. Believe it or not, in 2015 using corp directory to authenticate to AWS was quite novel!

 
# Remixed from http://www.padisetty.com/2014/02/powershell-automation-to-give-aws.html?m=1`

# Modules: Requires AWS Powershell tools  
Import-Module 'C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\AWSPowerShell.psd1'

# Environment specific Variables  
$hostname = $env:computername  
$hostname = $hostname.ToLower()  
$CurrentFolder = split-path -parent $MyInvocation.MyCommand.Definition

#location where makecert tool is present, you can find this tool part of Windows SDK  
$makecertpath = ("$CurrentFolder\makecert.exe")

$domainname = "companyname.com"  
$netbiosname = "companyname"  
$adfsname = "adfs.$domainname"  
#$SSLcertName=$adfsname  
$SSLcertName="\*.companyname.com"  
$adfsFarmName="Companyname ADFS"  
$adfsServiceAccountName="svc-adfs"  
# If you have a corp account check what your signin URL is  
$AWSAccountSAMLMetadata="https://companyname.signin.aws.amazon.com/static/saml-metadata.xml"  
#$AWSAccountSAMLMetadata="https://signin.aws.amazon.com/static/saml-metadata.xml"

# Decrypt service account password (can be a lowly domain user. Like most service accounts, does not require interactive logon rights)  
$key=(1..16) # lame 128bit key  
$Encryptedpassword = Get-Content "$CurrentFolder\encrypted.txt"  
$secure\_string = $Encryptedpassword | ConvertTo-SecureString -key $key

Write-host "Running prerequsite tests"  
# Use a cert from your public wildcard cert or your internal CA  
# makecert.exe comes from Windows SDK if you must use a self-signed cert (lab only)  
if ( !(Test-Path -Path $makecertpath))  
{  
'makecertpath should point to full path of makecert.exe'  
return  
}

if ((Get-Module AWSPowerShell) -eq $null)  
{  
'AWSPowerShell is not installed'  
return  
}

# I had to change this from Siva's code  
if ((Get-AWSCredentials -ListStoredCredentials | Select-String 'default') -eq $null)  
{  
'AWS PS Default is not set. Run Initialize-AWSDefaults'  
return  
}

if ((Get-ChildItem 'Cert:\LocalMachine\My') -eq $null)  
{  
'No certificate in local machine personal store'  
return  
}

# Requires AD powershell module to be installed  
Write-host "Creating AD groups and adding test user to them"  
#New-ADGroup AWS-Administrators -GroupScope Global -GroupCategory Security  
#New-ADGroup AWS-Infrastructure -GroupScope Global -GroupCategory Security  
#New-ADGroup AWS-Operations -GroupScope Global -GroupCategory Security  
#Write-host "Creating service account for ADFS service to run"  
#New-ADUser -Name $adfsServiceAccountName -AccountPassword $password -Enabled $true

Write-host "Installing ADFS (Active Directory Federation Service)"  
Install-WindowsFeature Web-Server, ADFS-Federation, Web-Scripting-Tools -IncludeManagementTools  
Import-Module WebAdministration

Write-host "Configuring service principle name: $adfsServiceAccountName"  
setspn.exe -s http/$adfsname $adfsServiceAccountName  
setspn.exe -s http/$hostname.$domainname $adfsServiceAccountName

# This step creates a self signed cert which is fine for testing but for prod we'll use our public trusted wildcard cert  
#Write-host "Generating self signed certificate in local machine store"  
#&$makecertpath -n "CN=$adfsname" -r -pe -sky exchange -ss My -sr LocalMachine -eku 1.3.6.1.5.5.7.3.1

# Find the 1st certificate (preinstalled) in the local machine store (note: cert store is like a directory structure cert:\)  
$sslcert = (Get-ChildItem 'Cert:\LocalMachine\My')\[0\]

Write-host "Installing ADFS"  
$cred = New-Object System.Management.Automation.PSCredential ("$netbiosname\$adfsServiceAccountName", $secure\_string)  
Install-AdfsFarm `  
    -CertificateThumbprint $sslcert.Thumbprint `  
    -FederationServiceDisplayName $adfsFarmName `  
    -FederationServiceName $adfsname `  
    -ServiceAccountCredential $cred `  
    -OverwriteConfiguration

write-host "Configuing proxy credentials"  
$webClient = new-object System.Net.WebClient  
$webClient.Proxy.Credentials = \[System.Net.CredentialCache\]::DefaultNetworkCredentials

# Establishing Two Way Trust AWS trusts company  
#Download and save the ADFS metadata to a tempfile  
#Because of self signed, need to disable the SSL Validation  
# otherwise WebClient.DownloadFile will fail.  
$adfsMetadataUrl = "https://localhost/FederationMetadata/2007-06/FederationMetadata.xml"  
$metadatapath = [IO.Path]::GetTempFileName()  
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

Write-host "Establishing Two Way Trust company trusts AWS"  
Add-ADFSRelyingPartyTrust -Name "Amazon" -MetadataUrl $AWSAccountSAMLMetadata

$webClient.DownloadFile( $adfsMetadataUrl, $metadatapath )  
write-host "Saved to: $metadatapath"

# Register a new SAML Provider with IAMS that has this ADFS information  
# Note, SSL interception on proxy will break this  
$role = New-IAMSAMLProvider -Name "ADFS" -SAMLMetadataDocument (cat $metadatapath)  
$account = $role.Substring(13,12)  
write-host "Updated AWS account number: $account"  
del $metadatapath

# Trust policy configures WHO can assume the role. Note the URL is always signin.aws.amazon.com even if your AWS account uses another URL to sign into AWS console.  
#custom replacement is used for $role, so don't have to deal with escape chars  
$trustPolicy = @'  
{  
"Version": "2012-10-17",  
"Statement": [  
{  
"Effect": "Allow",  
"Action": "sts:AssumeRoleWithSAML",  
"Principal": {"Federated": "$role"},  
"Condition": {  
"StringEquals": {"SAML:aud": "https://signin.aws.amazon.com/saml"}  
}  
}  
]  
}  
'@  
$trustPolicy = $trustPolicy.Replace('$role', $role)

# Access Policies: Note do you own homework to decide what access policies are appropriate for you  
# http://awspolicygen.s3.amazonaws.com/policygen.html  
$AdminAccessPolicy = @'  
{  
"Version": "2012-10-17",  
"Statement": [  
{  
"Sid": "Stmt1434192117145",  
"Action": "\*",  
"Effect": "Allow",  
"Resource": "\*"  
}  
]  
}  
'@

# Create Roles  
New-IAMRole -AssumeRolePolicyDocument $trustPolicy -RoleName "ADFS-Administrators"  
Write-IAMRolePolicy -RoleName "ADFS-Administrators" -PolicyName "Administrator-Access" -PolicyDocument $AdminAccessPolicy

#Configure Assertions for the SAML Authentication Response  
$nameId = @'  
@RuleTemplate = "MapClaims"  
@RuleName = "Name ID"  
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"]  
\=>  
issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",  
Issuer = c.Issuer,  
OriginalIssuer = c.OriginalIssuer,  
Value = c.Value,  
ValueType = c.ValueType,  
Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"]  
\= "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");  
'@

$roleSessionName = @'  
@RuleTemplate = "LdapClaims"  
@RuleName = "RoleSessionName"  
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",  
Issuer == "AD AUTHORITY"]  
\=>  
issue(store = "Active Directory",  
types = ("https://aws.amazon.com/SAML/Attributes/RoleSessionName"),  
query = ";userPrincipleName;{0}", param = c.Value);  
'@

#list of AD groups is first stored in a temporary variables  
$tempVariable = @'  
@RuleName = "Save AD Group Into http://temp/variable"  
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",  
Issuer == "AD AUTHORITY"]  
\=>  
add(store = "Active Directory",  
types = ("http://temp/variable"),  
query = ";tokenGroups;{0}",  
param = c.Value);  
'@

#RegEx here to replace AD AWS- group names with IAM ADFS- role names  
$roleMapping = @'  
@RuleName = "Role mapping"  
c:[Type == "http://temp/variable", Value =~ "(?i)^AWS-"]  
\=>  
issue(Type = "https://aws.amazon.com/SAML/Attributes/Role", Value =  
RegExReplace(c.Value, "AWS-",  
"arn:aws:iam::$account:saml-provider/ADFS,arn:aws:iam::$account:role/ADFS-"));  
'@

$roleMapping = $roleMapping.Replace('$account', $account)  
$ruleset = New-AdfsClaimRuleSet -ClaimRule $nameId,$roleSessionName, $tempVariable, $roleMapping

$issuanceAuthorizationRules = @'  
@RuleTemplate = "AllowAllAuthzRule"  
\=>  
issue(Type = "http://schemas.microsoft.com/authorization/claims/permit",  
Value = "true");  
'@

Set-ADFSRelyingPartyTrust -TargetName Amazon -IssuanceTransformRules $ruleset.ClaimRulesString -IssuanceAuthorizationRules $issuanceAuthorizationRules

Find more IT Infrastructure tips at blog.alexmags.com