AWS Single Account (SSO) with Azure AD as External Identity Provider (IdP)

In this blog post, I would like to demonstrate how to use Azure AD as external identity provider (IdP) to sign-in to AWS accounts. For this exercise, we will configure two (2) AWS accounts but you can replicate the pattern on every account you own in AWS. This pattern is decentralized and it does not require any syncing for data between Azure and AWS. If you prefer centralized pattern, you may read my earlier blog post, AWS Single Sign-On (SSO) with Azure AD as External Identity Provider (IdP). Regardless which pattern you like- centralized or decentralized- you must automate the creation of IAM provider and roles in AWS, and creation of AD group and role mapping in Azure AD.

Azure AD SAML Application

While I was researching on how to enable single sign-on for an enterprise application in Azure AD, I jumped on Quickstart: Enable single sign-on for an enterprise application and followed Azure AD SAML Toolkit example to quickly realize it will not work for enterprise use and you have to create your app from scratch without unnecessary dependencies. I thought I would share my experience with you in case you are searching for Federated Sign-in to AWS Accounts and you happened to use Azure/O365 for your workplace capability. Enough of introduction, let’s get started on what we need to do to make it happen!

Login to Azure Active Directory Admin Center as Global Administrator and create your own application-

Enterprise applications
>> All Applications
>> New application
>> Create your own application (use non-gallery option), we named the application – “AWS SSO Single Account Azure AD IdP by Aspnet4you”.

Enterprise Applications
Browse Azure AD Gallery
Create your own application

Once the app is created, you can configure SAML on Azure Active Directory Admin Center or on Azure Portal.

Enterprise applications
>> All Applications
>> AWS SSO Single Account Azure AD IdP by Aspnet4you
>> Single Sign-on
>> Basic SAML Configuration
>> Edit

Configure Single sign-on with SAML

Update Basic SAML Configuration- entity id must be unique in Azure AD. I have multiple entity id with same name and they are differentiated by #number. Be sure to change the directory id to your AAD id!

  • Identifier (Entity ID): https://signin.aws.amazon.com/saml#4
  • Reply URL (Assertion Consumer Service URL): https://signin.aws.amazon.com/saml
  • Logout Url (Optional): https://login.microsoftonline.com/c33386cf-6e11-484c-a983-b49975ce571a/saml2
Basic SAML Configuration
SAML Configuration Details

Next, configure Attributes and Claims for the application. Here are pre configured claims and you don’t have to change them-

  • Name:nameidentifier Namespace:http://schemas.xmlsoap.org/ws/2005/05/identity/claims Value:user.userprincipalname
  • Name:emailaddress Namespace:http://schemas.xmlsoap.org/ws/2005/05/identity/claims Value:user.mail
  • Name:givenname Namespace:http://schemas.xmlsoap.org/ws/2005/05/identity/claims Value:user.givenname
  • Name:name Namespace:http://schemas.xmlsoap.org/ws/2005/05/identity/claims Value:user.userprincipalname
  • Name:surname Namespace:http://schemas.xmlsoap.org/ws/2005/05/identity/claims Value:user.surname
Attributes and Claims

Add new claims to the app as required by AWS-

  • Name:SessionDuration Namespace:https://aws.amazon.com/SAML/Attributes Value:3600
  • Name:RoleSessionName Namespace:https://aws.amazon.com/SAML/Attributes Value:user.mail
  • Name:Role Namespace:https://aws.amazon.com/SAML/Attributes Value:user.assignedroles

Download the Federation metadata and save it for later use. We will need this metadata to create IAM Provider in AWS.

Login to AWS Console of an account with an Admin privilege. Go to IAM >> IAM Providers. Add provider of type SAML type and upload the Federation metadata xml you downloaded from Azure AD. We will name this provider as Azure-AD-SAML-SSO.

List of IAM Providers
Create a new IAM provider

If you changed the metadata or singing certificate in Azure AD SAML application, you can update IAM provider to replace the metadata.

IAM provider, replace metadata

Once IAM provider is created, we can create new IAM roles using the newly created provider. Let’s create two IAM roles- AWS-ADMIN and AWS-ANALYST. You can decide what policies you would like to assign but for this exercise, we are using aws managed policy.

AWS role with Azure-AD-SAML-SSO as SAML provider (trust)
IAM roles with Azure-AD-SAML-SSO as SAML provider

How does the trust policy look like for AWS-ADMIN and AWS-ANALYST roles?

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::862832033224:saml-provider/Azure-AD-SAML-SSO"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml"
}
}
}
]
}

Let’s go back to Azure AD in Azure Portal and create the AD group and assign members to the groups as per job function. In enterprise environment, these groups typically created in on-premise AD and synced with Azure AD. We are going to create four (4) AD groups.

AD Groups in Azure AD

In Azure Portal, go to Azure Active Directory >> App Registrations >> All applications >> Select “AWS SSO Single Account Azure AD IdP by Aspnet4you” >> App roles.

Create new app role(s) with value to match AWS IAM Provider and IAM role. Example: AWS-862832033224-ANALYST role with value (aws role arn,aws iam provider arn) arn:aws:iam::862832033224:role/AWS-ANALYST,arn:aws:iam::862832033224:saml-provider/Azure-AD-SAML-SSO. Repeat the role for each AWS account.

App roles in Azure AD Application

In Azure Portal, go to Azure Active Directory >> Enterprise applications >> All applications >> Select “AWS SSO Single Account Azure AD IdP by Aspnet4you” >> Users and Groups

Add user/group and assign role.

We will use previously created AD group to assign role based on naming convention. For this exercise, we are doing manually but this step
should be automated via Azure Graph API – when a new federated role is added in AWS account(s). These roles would be passed to AWS sign-in page as claims (user.assignedroles). Note that you can assign only one role to a group/user. This is not a problem since user can be member of multiple groups.

Users and groups and role assignment in Azure AD app

Okay, this concludes our configurations in Azure AD and AWS. It’s time to test to make sure configurations do work!

In Azure Portal, go to Azure Active Directory >> Enterprise applications >> All applications >> Select “AWS SSO Single Account Azure AD IdP by Aspnet4you” >> Single Sign-on

Click the Test button at the bottom of the Single Sign-on screen. A side pane will be visible to select sign-in as current user or sign-in as someone else. Click Test Sign-in and it will popup a new window. Follow the prompt and select a user who is member of one ore more AD groups assigned in the single sing-on application.

Test sign-in from SSO application in Azure AD

If user is part of multiple groups, AWS sign-in page will display all the roles user can use to login to AWS account. Pick a role and click sign-in. You will be successfully signed-in to the selected account/role. Wow, it works!

AWS sign-in page to pick a role to sign-in to an AWS account
Successful sign-in to AWS using federated authentication

Test sign-in is for development and debugging. This test page will auto generate AuthNRequest which looks like-

Sample AuthNRequest

A complete IdP initiated sign-in URL to Azure AD would look like – https://login.microsoftonline.com/c33386cf-6e11-484c-a983-b49975ce571a/saml2?SAMLRequest=jZFBS8NAEIXvgv8hrOcm203UuCSFxKRQqFBa9eBtiKNdyO7GnU0Vf73bFA9e1Otj3sz73hQEuh9kNfq92eLbiOSjD90bKtnojLRAiqQBjSR9J3fV3VqKmEuNHp7BA4tWTcmWTdtm6bxOU15ldX5T5XlVX7WiFU1W8%2bs5ix7RkbKmZMEcPEQjrgx5MD5IXIgZv5yJ%2fF6kMuOSp0%2fHmQ0QqQOW7AV6QhZVROh82HJrDY0a3Q7dQXX4sF2XbO%2f9QDJJSL0aZWJ4pxg0fFoTd1YnR0h24pIT8O90g7PedjZYltZ1OJXzHWNxflZM%2bd1%2fegoIp8xs8XfCi6xITqvDkaiYtJ%2bvCfoX

SAMLRequest query parameter contains AuthNRequest xml which is Deflated + Base64 encoded + Url Encoded. For development purpose, you can use online saml tool to deflate + base64 encode and then url encode the AuthNRequest.

<samlp:AuthnRequest xmlns="urn:oasis:names:tc:SAML:2.0:metadata" ID="FDEE431B330A4B89A88AB6E2E2D4B071" Version="2.0" IssueInstant="2022-05-28T23:40:03Z" IsPassive="false" AssertionConsumerServiceURL="https://signin.aws.amazon.com/saml" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ForceAuthn="false">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://signin.aws.amazon.com/saml#4</Issuer>
 </samlp:AuthnRequest>

For production application, we will need to build an application to generate Azure AD IdP initiated sign-in url with SAMLRequest parameter. For simplicity, I created an Azure Function (server-less app) which will Deflate + Base64 encode + Url Encode AuthNRequest and redirect to Azure sign-in url with encoded SAMLRequest parameter. Try it! https://function.aspnet4you.com/api/AADSamlAuthnRequest?aadid=c33386cf-6e11-484c-a983-b49975ce571a&acs=https://signin.aws.amazon.com/saml&issuer=https://signin.aws.amazon.com/saml%234

Of course, it would not work for you since you are not a valid user in my Azure tenant!

Currently, Azure Function is not protected by Azure AD authentication and authorization. In production, this function would be protected by Azure AD authentication and authorization. Federated Login to AWS is IdP initiated and you can create any url generator application with any language and deploy internally. Regardless of AuthN and AuthZ of url generator application, Azure AD will validate the user before sending saml response to AWS.

In case you are searching for how to deflate + base64 encode + url encode – all in sequence, here is the sample code (in C#) that I am using in dotnetcore project-

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using System.Text;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using System;
using System.Collections.Generic;
using System.Web;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.IO.Compression;

namespace AzureFunctionAppsCore
{
    /// <summary>
    /// References:
    /// 
    /// </summary>
    public static class AADSamlAuthnRequest
    {
        [FunctionName("AADSamlAuthnRequest")]
        public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route =null)] HttpRequestMessage req, ILogger log)
        {
            string authNrequest = null;
            var body = req.Content.ReadAsStringAsync().GetAwaiter().GetResult();

            string uuid = Guid.NewGuid().ToString("N").ToUpper();
            uuid = $"F{uuid.Substring(1, uuid.Length - 1)}";

            var query = HttpUtility.ParseQueryString(req.RequestUri.Query);
            string aadId = query.Get("aadid");
            string issuerUrl = query.Get("issuer");
            string acsUrl = query.Get("acs");

            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"<samlp:AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" ID=\"{uuid}\" Version=\"2.0\" IssueInstant=\"{ DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ") }\" IsPassive=\"false\" AssertionConsumerServiceURL=\"{acsUrl}\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ForceAuthn=\"false\">");
            sb.AppendLine($"<Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">{issuerUrl}</Issuer>");
            sb.AppendLine(" </samlp:AuthnRequest>");

            string authnrequest = sb.ToString();
            authNrequest = Utils.DeflateAndEncodeSaml(authnrequest);

            string loginRequet = $"https://login.microsoftonline.com/{aadId}/saml2?SAMLRequest={authNrequest}";

            HttpResponseMessage response = req.CreateResponse(HttpStatusCode.Moved);
            response.Headers.Location = new Uri(loginRequet);
            return response;

        }   
    }
	
	private static string DeflateAndEncodeSaml(string authnRequest)
	{
		var bytes = Encoding.UTF8.GetBytes(authnRequest);
		using (var output = new MemoryStream())
		{
			using (var zip = new DeflateStream(output, CompressionMode.Compress))
			{
				zip.Write(bytes, 0, bytes.Length);
			}
			var base64 = Convert.ToBase64String(output.ToArray());
			return HttpUtility.UrlEncode(base64);
		}
	}
}

AWS Single Account (SSO) with Azure AD as External Identity Provider (IdP) is a decentralized pattern. That means you have to add IAM provider and IAM roles in each AWS account. You have to map those roles in Azure AD group and assign the role to the group. Automation, automation and automation- as long as you automate those process, your solution will work without hiccups.

Like this or any of my blog post and you would like to reach out? Feel free to connect with me via LinkedIn profile.

Leave a Reply