AWS Security Blog
How to Set Up SSO to the AWS Management Console for Multiple Accounts by Using AD FS and SAML 2.0
AWS supports Security Assertion Markup Language (SAML) 2.0, an open standard for identity federation used by many identity providers (IdPs). SAML enables federated single sign-on (SSO), which enables your users to sign in to the AWS Management Console or to make programmatic calls to AWS APIs by using assertions from a SAML-compliant IdP. Many of you maintain multiple AWS accounts (for example, production, development, and test accounts), and have asked how to use SAML to enable identity federation to those accounts. Therefore, in this blog post I will demonstrate how you can enable federated users to access the AWS Management Console with multiple AWS accounts and SAML.
If you use Microsoft Active Directory for corporate directories, you may already be familiar with how Active Directory and AD FS work together to enable federation, as described in the AWS Security Blog post, Enabling Federation to AWS Using Windows Active Directory, AD FS, and SAML 2.0. As a result, I decided to use Active Directory with AD FS as the example IdP in this post.
To automate both the installation and configuration of AD FS and Active Directory, I will use Windows PowerShell in this post. By leveraging Windows PowerShell, you eliminate the manual installation and configuration steps, and allow yourself to focus on the high-level process.
If you want to manage access to all your AWS accounts with Active Directory and AD FS, you’ve come to the right place!
Background
To set up your Windows Active Directory domain, you have many options. You can use an Amazon EC2 instance and set up your own domain with dcpromo or by installing the Active Directory role (if using Windows Server 2012 and later). You can automate this process by using an AWS CloudFormation template that creates a Windows instance and sets up a domain for you. Alternatively, you may want to create a Simple AD with AWS Directory Service. Information about how to manage these directories, join EC2 instances to the domain, and create users and groups is in our documentation.
First things first
With SAML federation, AWS requires the IdP to issue a SAML assertion with some mandatory attributes (known as claims). This AWS documentation explains how to configure the SAML assertion. In short, you need the assertion to contain:
- An attribute of name https://aws.amazon.com/SAML/Attributes/Role (note this is not a URL to a resource, but a custom attribute for our AWS Security Token Service [STS]). Its value must be at least one role/provider pair as a comma-separated list of their Amazon Resource Names (ARNs). Because the ARNs are unique per AWS account, this information tells AWS to which account you want to federate.
- An attribute of name https://aws.amazon.com/SAML/Attributes/RoleSessionName (again, this is just a definition of type, not an actual URL) with a string value. This is the federated user’s friendly name in AWS.
- A name identifier (NameId) that is used to identify the subject of a SAML assertion.
AWS has recently published troubleshooting steps in our documentation about how to debug a SAML response from your IdP. In my experience, the problem usually is related to the three attributes mentioned above: they are either missing, misspelled (remember the names are cAsE sEnSitiVe!), or they don’t contain the expected values. If you are struggling with SAML federation, you should always start by first collecting a copy of the SAML response you are sending to AWS.
Don’t know how to collect a copy of the SAML response? Check our documentation, and then decode and troubleshoot the response.
Use case
A company, Example Corp., wants:
- Federated identity access for specific groups of users in its organization.
- To manage federation across multiple AWS accounts.
- To deal with three populations of users:
- Users that will access 1 account with 1 role (1:1).
- Users that will access multiple accounts with 1 role (N:1).
- Users that will access multiple accounts with multiple roles (N:M).
Example Corp. is using Active Directory, and they want to use AD FS to manage federation centrally. Example Corp. wants to federate to these two AWS accounts: 123456789012 and 111122223333.
Prepare your environment
The blog post, Enabling Federation to AWS Using Windows Active Directory, AD FS, and SAML 2.0, shows how to prepare Active Directory and install AD FS 2.0 on Windows Server 2008 R2. In this blog post, we will install AD FS 3.0 on Windows Server 2012 R2. AD FS 3.0 cannot be installed on Windows Server 2008 R2 and earlier, so make sure you pick the right version of Windows Server. The AD FS 3.0 and AD FS 2.0 installations and configurations are very similar; therefore, I decided to use this blog post as a chance to show how to do the same with Windows PowerShell. I will report the steps from the GUI as well, but more as additional reading at the end of the blog post.
I like how Windows PowerShell can make the configuration steps easy. To make things even easier for you here, I have kept the same naming convention from Jeff Wierer’s blog post for the claim rules, Active Directory groups, and IAM entities.
The .zip file with the collection of related scripts contains:
- Two folders: Logs (where log files are stored) and Utilities (where this PowerShell script is saved).
- The following scripts:
- 00-Configure-AD.ps1 – It simplifies Active Directory group and user creation as well as the configuration required to leverage the federation solution explained in this post.
- 01-Install-ADFS.ps1 – It installs AD FS 3.0 on Windows Server 2012 R2 and downloads the federation metadata.
- 02-Configure-IAM.ps1 – It creates an identity provider and two IAM roles in the AWS account you choose.
- 03-Configure-ADFS.ps1 – It creates a relying party trust to AWS by using the following templates:
- auth.txt
- claims.txt
Extract the file on the Windows Server 2012 R2 computer you designated for the AD FS 3.0 installation. Also, install AWS Tools for Windows PowerShell on that computer, because this is required to complete the IAM configuration from the command line. You don’t need to configure a credential profile at this time.
General workflow
These are the steps of the general workflow:
- The user goes to the AD FS sign-in page to authenticate.
- AD FS authenticates the user against Active Directory.
- Active Directory returns the user’s information.
- AD FS dynamically builds ARNs by using Active Directory group memberships for the IAM roles and user attributes for the AWS account IDs, and sends a signed assertion to AWS STS.
- The user gets to the AWS role selection page, where he can choose which account to access and which role to assume.
AWS STS is the single point of access for all SAML-federated access. The ARNs in the SAML response are used to identify your SAML provider and IAM role in your destination account. The following section explains how to simplify administration for the ARNs in your AD FS server, providing custom claim rule code examples.
The solution in action
I want to start from the end to show you how the user experience will look.
I have a user called Bob who is a member of two Active Directory groups:
- AWS-Dev
- AWS-Production
Note: You need to enable View > Advanced Features in Active Directory Users and Computers to see the Attribute editor tab.
This user has two AWS account IDs in the url attribute, as shown in the following images.
Bob then connects to https://adfs.example.com/adfs/ls/idpinitiatedsignon.aspx, where he can pick Amazon Web Services as the destination application after he has authenticated, as shown in the following image.
When Bob gets to the AWS role selection page, he gets 4 possible choices (2 choices for each of the 2 accounts displayed) as the combination of the groups he belongs to and the AWS account IDs from the url attribute, as shown in the following image. Thanks to the new role selection for SAML-based single sign-on, it is easier for the user to understand the destination account he would access.
This workflow is summarized in the following diagram.
Let’s now see how to use my Windows PowerShell scripts to set up this solution.
Active Directory configuration (Windows PowerShell: 00-Configure-AD.ps1)
The first script, 00-Configure-AD.ps1, can be used to create two Active Directory groups (AWS-Production and AWS-Dev) and a user with a password of your choice. The script asks you many questions so that you can either create new users or assign permissions to already existing users. Let’s see how it works in more detail.
To run the scripts, I launch Windows PowerShell with administrative privileges (see the following screenshot) from the server where I will install AD FS 3.0. The machine is already joined to the example.com domain, so I connect using my Domain Administrator user, Alessandro.
I download the scripts to my desktop, and after unzipping the file, I launch the script located in my AD FS folder on my desktop:
PS C:UsersalessandroDesktopADFS> .0-Configure-AD.ps1
The script will ask some questions about what you want to do. In order, it will ask for (based on your answers):
- Active Directory AWS groups creation.
- Do you want to create two AD groups called AWS-Production and AWS-Dev?
- AD FS service account creation.
- Do you want to create an AD FS service account? A user name and password will be requested.
- How many new Active Directory users do you want to create?
- List the AWS account IDs you want this user to access (for example, 123456789012, 111122223333).
- Active Directory group membership for AWS access.
- What level of access do you want to grant?
- How many existing Active Directory users do you want to grant access to AWS?
- Type the user name of the user you want to manage.
- AWS account association.
- Do you want to keep the existing AWS account associations?
- Check the current Active Directory group membership for AWS access.
- Do you want to keep [GROUP MEMBERSHIP]?
- Active Directory group membership for AWS access.
- What level of access do you want to grant?
The following screenshot shows the workflow for the creation of user Bob, which is assigned to the AWS accounts 123456789012 and 111122223333; he also is a member of AWS-Production.
My answers to the questions of the script are in red:
- Active Directory AWS groups creation
- Do you want to create two AD groups called AWS-Production and AWS-Dev? Y
- AD FS service account creation
- Do you want to create an AD FS service account? User name and password will be requested. Y
- A credential request window allows me to type the user name and password for the user creation.
- Do you want to create an AD FS service account? User name and password will be requested. Y
- How many new Active Directory users do you want to create? 1
- A credential request window allows me to type the user name and password for the user creation.
- List the AWS account IDs you want this user to access (such as 123456789012,111122223333) 123456789012,111122223333
- Active Directory group membership for AWS access
- What level of access do you want to grant? P
- How many existing Active Directory users do you want to grant access to AWS? 0
If you don’t need to create the Active Directory groups AWS-Production and AWS-Dev, you can simply type N when asked. You can do the same thing for the AD FS service account.
The provided script and the steps just outlined do the following in your domain:
- Create two Active Directory Groups named AWS-Production and AWS-Dev.
- Create the AD FS service account ADFSSVC. This account will be used as the AD FS service account later on. This account is not associated with any Active Directory group because this is a service account.
- Create a user named Bob.
- Give Bob an email address (bob@example.com). This is automatically done by the script by combining the user name and the domain name.
- Associate Bob with two AWS account IDs: 123456789012 and 111122223333
- Add Bob to the AWS-Production group.
If you have an existing user you want to manage, you can run the script again and tell the script how many existing users you want to manage: How many existing Active Directory users do you want to grant access to AWS?
In the following example, I don’t need to create the Active Directory groups and AD FS service account again, but I manage Bob (which now already exists) and add him to the AWS-Dev group.
These are my answers to the questions:
- Active Directory AWS groups creation
- Do you want to create two AD groups called AWS-Production and AWS-Dev? N
- AD FS service account creation
- Do you want to create an AD FS service account? User name and password will be requested. N
- How many new Active Directory users do you want to create? 0
- How many existing Active Directory users do you want to grant access to AWS? 1
- Enter the user name of the user you want to manage. Bob
- AWS account association.
- Do you want to keep the existing AWS account associations? Y
- Check the current Active Directory group membership for AWS access.
- Do you want to keep [GROUP MEMBERSHIP]? Y
- Active Directory group membership for AWS access.
- What level of access do you want to grant? D
The code is available to you, and it can be adjusted to run in noninteractive mode and to accept parameters to run in a batch script.
AD FS installation (Windows PowerShell: 01-Install-ADFS.ps1)
The script I will use now is 01-Install-ADFS.ps1. This script will install the AD FS 3.0 Windows role, create a self-signed certificate, and configure AD FS for the first use. The configuration will ask for the credentials of the service account you have created before (ADFSSVC). Please note you should provide the user with the NETBIOS (for example, EXAMPLEadfssvc).
Before you can move to the next step and create a SAML provider in IAM, you need the federation metadata document for your AD FS federation server, which you can download from https://<yourservername>/FederationMetadata/2007-06/FederationMetadata.xml. The federation metadata is automatically downloaded in the same folder as the script (the downloaded file is called federationmetadata.xml).
The warning about the SPN is a known issue that can be fixed by running the following command at the command line (make sure you run the command line as an administrator):
setspn -a host/localhost adfssvc
Note that adfssvc is the name of the service account I used.
If the command is successful, you will see output like this:
Registering ServicePrincipalNames for CN=ADFSSVC,CN=Users, DC=example,DC=com host/localhost
IAM configuration (Windows PowerShell: 02-Configure-IAM.ps1)
The next script, 02-Configure-IAM.ps1, will create an identity provider and 2 IAM roles in a specified AWS account. The SAML provider name that is created is ADFS, and the IAM roles are called ADFS-Production and ADFS-Dev. The roles trust the SAML provider ADFS.
The script will first ask how many AWS accounts you want to configure. Here is the question and my answer (in red): How many AWS accounts do you want to configure? 1
You will now be asked for the IAM access key and secret access keys for each of the AWS accounts you want to configure. The IAM user is used to create the required IAM entities, and it must have enough permissions to create an identity provider and a role.
Note: The script creates a SAML provider called ADFS and 2 IAM roles called ADFS-Dev and ADFS-Production. If you are creating these IAM objects manually, remember that you need to use the same names that I use in this blog post. This solution assumes the SAML provider and the IAM role names are the same across all the AWS accounts.
Here is the output that I got, which includes the IAM roles information.
I can then run the script again for the other AWS account, 111122223333.
AD FS configuration (Windows PowerShell: 03-Configure-ADFS.ps1)
The last script (03-Configure-ADFS.ps1) configures AD FS by creating the AWS relying party trust. All the required claim rules are added. You can see the rules in the .txt files that are in the .zip file you downloaded at the beginning of this process). If you are interested about the logic behind the code and how I came up with it, see the “Under the hood” section near the end of this blog post
auth.txt
@RuleTemplate = "AllowAllAuthzRule" => issue(Type = "http://schemas.microsoft.com/authorization/claims/permit", Value = "true");
claims.txt
@RuleTemplate = "MapClaims" @RuleName = "NameId" 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"); @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 = ";mail;{0}", param = c.Value); @RuleName = "Get AD Groups" 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); @RuleName = "Get AWS Accounts from User attributes" c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => add(store = "Active Directory", types = ("http://temp/AWSAccountsFromUser"), query = ";url;{0}", param = c.Value); @RuleName = "Dynamic ARN - Adding AWS Accounts" c:[Type == "http://temp/AWSAccountsFromUser"] => add(Type = "http://temp/AWSAccountsFromUser2", Value = RegExReplace("arn:aws:iam::AWSACCOUNT:saml-provider/ADFS,arn:aws:iam::AWSACCOUNT:role/ADFS-", "AWSACCOUNT", c.Value)); @RuleName = "Dynamic ARN - Adding Roles" c1:[Type == "http://temp/AWSAccountsFromUser2"] && c2:[Type == "http://temp/variable", Value =~ "(?i)^AWS-"] => issue(Type = "https://aws.amazon.com/SAML/Attributes/Role", Value = RegExReplace(c2.Value, "AWS-", c1.Value));
Run the script in a Windows PowerShell window launched as administrator, as shown in the following image.
The browser should be automatically launched with the AD FS sign-in page.
You will notice that an application is already set up (Amazon Web Services). You can now authenticate with the user Bob (or whoever you have previously configured with the Windows PowerShell script 00-Configure-AD.ps1), and you should be able to federate to AWS.
If you can’t access your AWS accounts, start troubleshooting by first collecting a copy of the SAML response you are sending to AWS (this is explained in our documentation). You can then decode and troubleshoot it.
How to handle exceptions
The solution presented so far works well if you have users with the same permissions across different accounts. However, what if a user must have Production access in one account and only Dev access in a second account? I will now show a few additional claim rules you can add at the end of the claim rules chain. Based on your needs, you can pick the claim rule code for the exception that you need. Because this is a case-by-case choice, I will show how to manage the exceptions in the UI. First, you need to open the AD FS Microsoft Management Console on your AD FS server.
Expand Trust Relationships, click Relying Party Trusts, right-click the relying party trust Amazon Web Services, and then click Edit Claim Rules.
You then should see the 6 rules shown in the following image.
Each of the following paragraphs will explain how to add a seventh rule. You can add as many rules as needed to manage multiple exceptions at the same time.
Exception—Static ARNs for DOMAINuser
With the following custom claim rule, we check the Windows account name of the authenticated user. If the user matches our condition, we issue specific ARNs for him.
You can place this additional custom claim rule after all the other claim rules you have already created, as shown in the following image.
The next rule—Exception – Static ARNs for DOMAINuser—is another custom claim rule. Follow these steps to create it.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Exception – Static ARNs for DOMAINuser, and then in Custom rule, enter the following:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Value == "DOMAINusername"] => issue(Type = "https://aws.amazon.com/SAML/Attributes/Role", Value = "arn:aws:iam::YOURACCOUNTID:saml-provider/ADFS,arn:aws:iam::YOURACCOUNTID:role/ADFS-Dev");
Code explanation
If the user is DOMAINusername, issue a claim of type https://aws.amazon.com/SAML/Attributes/Role with value:
"arn:aws:iam::YOURACCOUNTID:saml-provider/ADFS,arn:aws:iam::YOURACCOUNTID:role/ADFS-Dev”
In this example, user EXAMPLEBob would be granted access to ADFS-Dev in the specified account.
Now, Bob will be able to pick ADFS-Dev from account 444455556666 as well.
This workflow is summarized in the following diagram.
- EXAMPLEBob goes to the AD FS sign-in page to authenticate.
- AD FS authenticates the user against Active Directory.
- Active Directory returns the user’s information. EXAMPLEBob belongs to two AD groups (AWS-Production and AWS-Dev) and his user object attribute refers to two AWS accounts (123456789012 and 111122223333).
- AD FS dynamically builds four ARNs by using Active Directory group memberships for the IAM roles and user attributes for the AWS account IDs. Additionally, AD FS adds the ARNs for a third AWS account (444455556666) with a single IAM role (ADFS-Dev) and sends a signed assertion to STS.
- EXAMPLEBob gets to the AWS role selection page, where he can choose between accounts 123456789012, 111122223333, and 444455556666. In the first two accounts, he can choose between ADFS-Production and ADFS-Dev. For account 444455556666, only ADFS-Dev is available. All of these are IAM roles created in the specific accounts.
Exception—Static ARNs for anyone
This exception is to grant access to certain AWS accounts and IAM roles to any authenticated user. Again, you can place this exception at the end of the claim rules chain you have defined so far.
Follow these steps to create this customer claim rule:
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Exception – Static ARNs for anyone, and then in Custom rule, enter the following code. Make sure to change the parts in red to your AWS account ID and the required IAM role name.
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => issue(Type = "https://aws.amazon.com/SAML/Attributes/Role", Value = "arn:aws:iam::YOURACCOUNTID:saml-provider/ADFS,arn:aws:iam::YOURACCOUNTID:role/ADFS-Dev");
You can iterate the same logic for as many accounts as you need. The result is that anyone has been granted access to the specified account (444455556666) with the specified IAM role (ADFS-Dev).
Code explanation
If there is an incoming claim for an authenticated user (http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname), you issue a claim of type https://aws.amazon.com/SAML/Attributes/Role with value “arn:aws:iam::YOURACCOUNTID:saml-provider/ADFS,arn:aws:iam::YOURACCOUNTID:role/ADFS-Dev”. In this example, any authenticated user will be granted access to ADFS-Dev in the specified account.
This workflow is summarized in the following diagram.
- A domain user goes to the AD FS login page to authenticate.
- AD FS authenticates the user against Active Directory.
- Active Directory returns the user’s information. Let’s assume this domain user does not belong to any Active Directory group that starts with AWS- (in other words, AWS-Production, AWS-Dev), and his user object attribute contains no AWS account.
- AD FS has no information to build dynamic ARNs from Active Directory group memberships and user attributes. AD FS has a rule to generate static ARNs for any authenticated user for a specific AWS account (444455556666) with a single IAM role (ADFS-Dev), and sends a signed assertion to AWS STS.
- The domain user can only assume one role in one AWS account. Therefore, the AWS role selection page is automatically skipped. The domain user will get access to account 444455556666 with the ADFS-Dev role, which must be created in the AWS account before any access is attempted.
Handling exceptions with an ad hoc attribute store (Microsoft SQL Server)
AD FS can retrieve information from Active Directory. Additionally, AD FS provides built-in capabilities to read information from a SQL database. For convenience, I am using Microsoft SQL Server.
I launched an EC2 instance with SQL Server installed, and I joined it to my Active Directory Domain as SQL.example.com. Because I need a domain-joined SQL server, I am not using Amazon RDS, but you can decide to use License Mobility, or use an Amazon-provided Amazon Machine Image.
The idea is to create a new database that AD FS can access to read information from a specific table. I store, for each Active Directory user that I want to grant access to AWS, the values for the role attribute. This attribute must be the comma-separated list of the SAML provider and IAM role ARNs, like: arn:aws:iam::YOURACCOUNTID:saml-provider/SAMLPROVIDERNAME,arn:aws:iam::YOURACCOUNTID:role/ROLENAME“. You can then customize the parts in red and store the resulting strings in the database so that you can retrieve them when the user authenticates.
The downside of using SQL Server is that you are using a third system that you need to manage for your federation (Active Directory, AD FS, and SQL Server). High availability and fault tolerance for a SQL database are challenges for DBAs. On the other hand, you won’t have to change any claim rules in AD FS in case a new exception needs to be defined for a user. An update in the DB table would only be required because AD FS queries it during the claim rules chain evaluation.
SQL configuration
On the database EC2 instance, I opened the SQL Server Management Studio and connected with a user that has enough privileges to create a new database there. Then I:
- Created a new database named ADFS.
- Created a new table named AWS with columns UserId and RoleARN. Here is a query example to create this table:
CREATE TABLE AWS ( UserId varchar(100) NOT NULL, RoleARN varchar(200) NOT NULL, CONSTRAINT AWS_pk PRIMARY KEY (UserId,RoleARN) );
- Added values to the new table. For example, if I want to give Bob access to the IAM role ADFS-Dev to two other AWS accounts (777788889999 and 444455556666), here are the values to add:
UserId: EXAMPLEBob
RoleARN: arn:aws:iam::777788889999:saml-provider/ADFS,arn:aws:iam::777788889999:role/ADFS-Dev
UserId: EXAMPLEBob
RoleARN: arn:aws:iam::444455556666:saml-provider/ADFS,arn:aws:iam::444455556666:role/ADFS-Dev
Note: The primary key of the table is the UserId and the RoleARN together, so you can define multiple RoleARNs for the same user. Please change the parts in red to your AWS account information.
- Make sure the AD FS account has read access to the SQL database and table.
AD FS configuration
To configure a new attribute store, you first need to open the AD FS Microsoft Management Console.
Under Trust Relationships, right-click Attribute Stores, and then click Add Attribute Store.
Type the following values:
- Display name: SQL
- Attribute store type: SQL
- Connection string: Server=SQL;Database=ADFS;Integrated Security=True
Click OK. Right-click the relying party Amazon Web Services, and then click Edit Claim Rules.
This rule—Exception – ARNs from SQL—is again a custom claim rule.
In order to create this rule, follow these steps.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Exception – ARNs from SQL, and then in Custom rule, enter the following:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
=> issue(store = "SQL", types = ("https://aws.amazon.com/SAML/Attributes/Role"), query = "SELECT RoleARN from dbo.AWS where UserId= {0}", param = c.Value);
Code explanation
If there is an incoming claim that shows you are authenticated (http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname), you then issue a claim of type https://aws.amazon.com/SAML/Attributes/Role. This is the result of the SQL query on the AWS table in the database AD FS where the column UserId is equal to the Windows account name of the current user. Because RoleARN in the database already contains the comma-separated list of SAML providers and IAM role ARNs, the returned value is already a valid role claim.
The entire workflow is summarized in the following diagram.
- EXAMPLEBob goes to the AD FS login page to authenticate.
- AD FS authenticates the user against Active Directory.
- Active Directory returns the user’s information. EXAMPLEBob belongs to two AD groups (AWS-Production and AWS-Dev), and his user object attribute refers to two AWS accounts (123456789012 and 111122223333).
- AD FS queries the SQL server to get possible exceptions defined for EXAMPLEBob.
- SQL returns two Role attributes that refer to account 777788889999, IAM role ADFS-Dev, and account 444455556666, IAM role ADFS-Dev.
- AD FS dynamically builds four ARNs by using Active Directory group memberships for the IAM roles and user attributes for the AWS account IDs. Additionally, AD FS adds the ARNs for two additional AWS accounts (444455556666 and 777788889999) with a single IAM role (ADFS-Dev) and sends a signed assertion to AWS STS.
- EXAMPLEBob gets to the AWS role selection page, where he can choose among accounts 123456789012, 111122223333, 444455556666, and 777788889999. In the first two accounts, he can choose between ADFS-Production and ADFS-Dev. For accounts 444455556666 and 777788889999, only ADFS-Dev is available. All of these are IAM roles created in each specific accounts.
Under the hood—AD FS claim rule explanation (from the GUI)
I will start from the initial AD FS configuration so that you can understand exactly what the provided Windows PowerShell scripts do.
In these steps, I will add the claim rules so that the elements AWS requires and AD FS doesn’t provide by default (NameId, RoleSessionName, and Roles) are added to the SAML authentication response. When you’re ready, open the AD FS Microsoft Management Console (MMC).
Under Trust Relationships, click Relying Party Trusts, right-click the relying party (in this case Amazon Web Services), and then click Edit Claim Rules (see the following screenshot).
Follow the subsequent procedures to create the claim rules for NameId, RoleSessionName, and Roles, which are three mandatory attributes for the SAML response that AD FS will send to AWS STS.
Adding NameId
A name identifier, represented by the NameID element in SAML 2.0, is generally used to identify the subject of a SAML assertion. One reason for including an identifier is to enable the relying party to refer to the subject later, such as in a query or a sign-out request. You will set this attribute of the Windows account name of the user as follows.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- Select Transform an Incoming Claim, and then click Next (see the following screenshot).
- Use the following settings:
- Claim rule name: NameId
- Incoming claim type: Windows account name
- Outgoing claim type: Name ID
- Outgoing name ID format: Persistent Identifier
- Pass through all claim values: Select this option
- Then click Finish.
Adding a RoleSessionName
You will use the email address of an authenticated user as the RoleSessionName. You can query Active Directory for this attribute as follows.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send LDAP Attributes as Claims (as shown in the following image).
- Use the following settings:
- Claim rule name: RoleSessionName
- Attribute store: Active Directory
- LDAP Attribute: E-Mail-Addresses
- Outgoing Claim Type: https://aws.amazon.com/SAML/Attributes/RoleSessionName
- Then click Finish.
Adding Roles
Unlike the two previous claims, here I use custom rules to send role attributes. The role must be a comma-separated list of two ARNs: the SAML provider and the IAM role you want to assume. Generate this string by retrieving all the authenticated user’s Active Directory groups and then matching the groups that start with IAM roles of a similar name. I used the names of these groups to create ARNs of IAM roles in the Example Corp. AWS accounts (those that start with AWS-). To know if the user can access one or more of my accounts, I query a user attribute. With few custom claim rules, you can use regular expressions to identify these special Active Directory groups, get the user attribute, and build these ARN strings.
Sending role attributes requires four custom rules. The first rule retrieves all the authenticated user’s Active Directory group memberships; the second rule retrieves the AWS accounts of the user; the third and fourth perform the transformation to the role’s claim.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Get AD Groups, and then in Custom rule, enter the following:
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);
- Click Finish.
This custom rule uses a script in the claim rule language that retrieves all the groups the authenticated user is a member of and places them into a temporary claim named http://temp/variable (a variable you can access later). I use this in the next rule to transform the groups into IAM role ARNs.
Dynamically generate multi-account role attributes
These AD FS claim rules that you will have at the end of the configuration in the AD FS MMC—NameId, RoleSessionName, and Get AD Groups—are the ones just defined (see the following screenshot). Get AWS Accounts from User attributes, Dynamic ARN – Adding AWS Accounts, and Dynamic ARN – Adding Roles are custom claim rules, and I will explain them in this section.
Get AWS accounts from user attributes
We now define a claim rule to get the AWS accounts a user can access from his Active Directory user object attributes. We will use the Active Directory user attribute url, because this is an attribute defined by default in Active Directory. No Active Directory schema extension is required then. You can use a different user attribute instead, if url is already in use in your organization.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Get AWS Accounts from User attributes, and then in Custom rule, enter the following:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => add(store = "Active Directory", types = ("http://temp/AWSAccountsFromUser"), query = ";url;{0}", param = c.Value);
Code explanation
Let’s analyze the code in this example:
- The “if statement” condition:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
- The special operator:
=>
- The add statement:
add(store = "Active Directory", types = ("http://temp/AWSAccountsFromUser"), query = ";url;{0}", param = c.Value);
For each rule defined, AD FS checks the input claims, evaluates them against the condition, and applies the statement to the claim if the condition is true. The variable c in the syntax is an incoming claim that you can check conditions against and use values from it in the following statement. In this example, you will check to see if there is an incoming claim that has a type that is http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname.
Then, add a claim. Using the add statement instead of the issue statement will add a claim to the incoming claim set. This will not add the claim to the outgoing token. It is like setting a temporary variable you can use in subsequent rules. In this example, add a claim of type http://temp/AWSAccountsFromUser. Its value is the result of the query = “;url;{0}” on the incoming claim http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname, which basically means, “Get the url attribute for the incoming user object.”
Note: You can read the AD FS 2.0 Claims Rule Language Primer to learn more about claims rule language.
Dynamic ARN—Adding AWS Accounts
Using a template for the ARN string, first replace the placeholder AWS account IDs with the AWS account IDs the incoming users have been granted access to.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Dynamic ARN – Adding AWS Accounts, and then in Custom rule, enter the following:
c:[Type == "http://temp/AWSAccountsFromUser"] => add(Type = "http://temp/AWSAccountsFromUser2", Value = RegExReplace("arn:aws:iam::AWSACCOUNT:saml-provider/ADFS,arn:aws:iam::AWSACCOUNT:role/ADFS-", "AWSACCOUNT", c.Value));
Note: Copy the code as it is and make no changes. In this case, AWSACCOUNT is a placeholder that will be automatically replaced by a real AWS account.
Code explanation
If there is an incoming claim of type “http://temp/AWSAccountsFromUser”, add another claim of type “http://temp/AWSAccountsFromUser2” as the result of the replacement of the string AWSACCOUNT in the string template “arn:aws:iam::AWSACCOUNT:saml-provider/ADFS,arn:aws:iam::AWSACCOUNT:role/ADFS-” with the values contained in the incoming claim. The incoming claim contains all the possible AWS account IDs the user can access. The output looks like the following (you would have your own AWS account IDs in place of the fictitious AWS account IDs):
“arn:aws:iam::123456789012:saml-provider/ADFS,arn:aws:iam::123456789012:role/ADFS-” … “arn:aws:iam:: 111122223333:saml-provider/ADFS,arn:aws:iam:: 111122223333:role/ADFS-”
Note: Because we are adding and not issuing a claim, you won’t actually see any http://temp/AWSAccountsFromUser2 claim in your SAML response.
Dynamic ARN—Adding Roles
I will now replace the IAM role name placeholder based on the Active Directory group membership of the user.
- In the Edit Claim Rules for Amazon Web Services dialog box, click Add Rule.
- In the Claim rule template list, select Send Claims Using a Custom Rule, and then click Next.
- For Claim rule name, type Dynamic ARN – Adding Roles, and then in Custom rule, enter the following:
c1:[Type == "http://temp/AWSAccountsFromUser2"] && c2:[Type == "http://temp/variable", Value =~ "(?i)^AWS-"] => issue(Type = "https://aws.amazon.com/SAML/Attributes/Role", Value = RegExReplace(c2.Value, "AWS-", c1.Value));
Code explanation
If there is an incoming claim of type “http://temp/AWSAccountsFromUser2” and an incoming claim of type “http://temp/variable” with a value that starts with “AWS-”, issue an outgoing claim of type “https://aws.amazon.com/SAML/Attributes/Role” as the result of the replacement of the string “AWS-” inside the value of the second condition claim (c2) with the value of the first condition claim (c1).
Claim c2 contains the groups that start with “AWS-” that the user belongs to, and claim c1 contains as many strings as the AWS account ID the user has access to in the following form (please note these are fictitious AWS account IDs):
“arn:aws:iam::12345678912:saml-provider/ADFS,arn:aws:iam::123456789012:role/ADFS-” … “arn:aws:iam:: 111122223333:saml-provider/ADFS,arn:aws:iam:: 111122223333:role/ADFS-”
The claim rule replaces the substring “AWS-” in the Active Directory group name and amends the aforementioned strings with ARNs. For example, with the groups mentioned at the beginning of this blog post—AWS-Production and AWS-Dev—the resulting ARNs would be:
“arn:aws:iam::123456789012:saml-provider/ADFS,arn:aws:iam::123456789012:role/ADFS-Production” “arn:aws:iam::123456789012:saml-provider/ADFS,arn:aws:iam::123456789012:role/ADFS-Dev” … “arn:aws:iam:: 111122223333:saml-provider/ADFS,arn:aws:iam:: 111122223333:role/ADFS-Production” “arn:aws:iam:: 111122223333:saml-provider/ADFS,arn:aws:iam:: 111122223333:role/ADFS-Dev”
Summing up
Your Active Directory users now can access multiple AWS accounts with their Active Directory credentials. They first log in by using the provided AD FS login page. After each user is authenticated, AD FS is configured to get the following information related to the user from Active Directory:
- The user object attribute url, which contains the AWS account the user can access.
- The user group membership, which contains the IAM roles the user can access in each account.
Bob can get production access to all the accounts defined in his user attribute url by belonging to the Active Directory group AWS-Production. To grant Bob access to a new AWS account, you can update Bob’s url attribute with the new AWS account, and AD FS will automatically combine this additional account with the Active Directory groups to which Bob belongs.
To change Bob’s access to his AWS accounts, Bob shouldn’t assume the Production role anymore, but the Dev one instead. You simply need to remove Bob from the AWS-Production Active Directory group and have him belong to the AWS-Dev Active Directory group. This change will propagate to all his AWS accounts.
Each AWS account you have in this configuration needs to be configured in the same way. You need to create a SAML provider called ADFS with your AD FS metadata and create IAM roles that trust this provider. The IAM roles must comply with the naming convention you have defined with AD FS (in other words, AWS-Production for the Active Directory group name and ADFS-Production as the related IAM role). To make the configuration easier, I have provided this Windows PowerShell script collection that will help you configure Active Directory, AD FS, and IAM.
When you don’t want a change in either the user attribute or group membership to affect multiple accounts at the same time, you must create an exception. You can define exceptions directly in the claim rule code in AD FS, or in an attribute store as a SQL database. The latter approach introduces a new system to manage and is therefore more complex, but if you need to add an exception for a specific user, you don’t need to change any claim rules in AD FS. AD FS can query the database to retrieve any exception for a specific user.
Note that the AWS Security Blog earlier this year published How to Implement Federated API and CLI Access Using SAML 2.0 and AD FS. By combining that blog post with this one, you can achieve multi-account federated API and CLI access!
I hope you found this post useful. If you have questions, please post them on the IAM forum or in the comments area below.
– Alessandro
Want more AWS Security how-to content, news, and feature announcements? Follow us on Twitter.