Azure AD Meets AWS: A Developer’s Guide to SAML Federation in the Real World
When I first started working with AWS in a financial services environment, one of the first things I ran into was the credential problem. Our organization runs Azure Active Directory for everything — Office 365, internal apps, VPN, the works. But the moment I opened a terminal and needed to run an AWS CLI command, I was completely on my own. No SSO, no federation. Just a browser window, a manual copy-paste of temporary credentials, and a one-hour expiry ticking down while I tried to get work done.
Someone on our team pointed me to aws-azure-login — an npm package that bridges your Azure AD identity directly to AWS CLI access. I’ve been using it since and it genuinely changes how you work in an environment where Azure is the identity backbone and AWS is where you’re building. In this post I want to break down how it works, what it’s actually doing behind the scenes, and why it matters in enterprise environments like ours.
The Problem I Was Trying to Solve
Our environment is Azure-first. That’s true of a lot of large financial services companies — Azure AD was deployed years before anyone started seriously moving workloads to AWS, and it owns identity. But AWS has its own IAM system, and by default the two have nothing to do with each other.
The workarounds I kept hitting were all painful in different ways:
- Long-lived IAM access keys — fine for service accounts but a real security and rotation problem for individual developers
- Logging into the AWS console via our Azure SSO portal, navigating to the credentials page, copying three values manually into a terminal — every single hour
- Asking our cloud team to set up AWS IAM Identity Center — always on the roadmap, never quite done
What I actually wanted was simple: I already proved who I am to Azure AD when I logged into my machine this morning. Why can’t AWS just trust that? It turns out it can — through SAML federation — and aws-azure-login is the tool that automates the whole handshake from the command line.
The Foundation: SAML Federation and Why AWS Trusts Azure
Before I get into what the package does, it’s worth understanding the mechanism underneath it — because once you get it, the whole thing makes intuitive sense.
The protocol at work here is SAML 2.0 (Security Assertion Markup Language). It’s the standard that lets an Identity Provider vouch for a user to a Service Provider without the service provider ever seeing the user’s password. In our setup:
- Identity Provider (IdP) — Azure AD. It’s the source of truth for who I am.
- Service Provider (SP) — AWS. It decides what I’m allowed to do once my identity is confirmed.
The way I think about it: Azure AD hands AWS a sealed, signed envelope that says “we’ve verified this person is who they say they are, and they belong to these groups.” AWS opens the envelope, checks the signature, maps the groups to IAM roles, and issues temporary credentials. AWS never sees my password — it only trusts the signature on that envelope. Here’s what that flow looks like:
Provides credentials + MFA
request
Issues SAML assertion
assertion
Returns temp credentials
Secret Key
Session Token
credentials
The key thing I want to highlight here: AWS never verifies your password. It only ever sees a signed XML assertion from Azure AD. The actual authentication — password check, MFA challenge, Conditional Access policies — all of that happens entirely inside Azure. AWS is just trusting Azure’s signature. This is what makes the whole thing both secure and practical in environments where Azure owns identity.
What’s Actually Happening When You Run aws-azure-login
This is the part I found most interesting when I first looked into it. The Azure AD login page is a JavaScript-heavy single-page application — there’s no simple API endpoint you can POST credentials to. Microsoft doesn’t expose a stable programmatic login API for this flow.
So instead of trying to reverse-engineer it, aws-azure-login uses Puppeteer — a Node.js library that drives a real headless Chromium browser. It literally opens a browser window in the background, loads the Azure login page, types your credentials, handles the MFA prompt, and extracts the SAML assertion from the response. It’s browser automation in service of identity federation.
Here’s every step of what happens under the hood from the moment you hit enter:
~/.aws/config file for the profile you pass in. It’s looking for three things: your azure_tenant_id, your azure_app_id_uri (the App ID URI of the AWS Enterprise Application registered in your Azure AD tenant), and your azure_default_username. Your Azure federation admin should have these — they’re not secrets, just identifiers.--mode gui, but it’s a genuine browser — rendering JavaScript, handling cookies, the whole thing. This is what makes the solution work against a login page that would break any simple HTTP client.https://aws.amazon.com/SAML/Attributes/Role attributes — one per IAM role you’re authorized to assume. If you’re in multiple groups mapped to different roles (developer, read-only, admin), you’ll see a list and get to pick. If there’s only one, it’s automatic. Your Azure AD group membership is what drives this, which is the clean part.AssumeRoleWithSAML API. AWS checks the assertion signature against the Azure AD signing certificate it has on file (set up in the Enterprise Application configuration), confirms it’s valid and unexpired, and hands back temporary credentials: an Access Key ID, Secret Access Key, and Session Token.~/.aws/credentials file under the profile you specified. From that point your AWS CLI, any SDK — boto3, the AWS SDK for .NET, Terraform — picks them up automatically. They’re valid for up to an hour by default. When they expire, you just run the command again. One minute of terminal interaction instead of the browser dance.Getting It Set Up
Setup is straightforward. You’ll need Node.js 12 or higher and two pieces of information from whoever manages your Azure AD tenant — your Azure Tenant ID and the App ID URI of the AWS Enterprise Application. These are the two values that tell the package which Azure tenant to authenticate against and which AWS application to request access to.
Install the package
Install it globally so you can run it from any directory:
# Install globally so you can run it from any directory
npm install -g aws-azure-login
# Verify installation
aws-azure-login --version
Configure your profile
Run the configure command once per profile. It walks you through an interactive prompt — just have your tenant ID, App ID URI, and corporate email ready:
aws-azure-login --configure --profile mycompany-dev
# You will be prompted for:
# Azure Tenant ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Azure App ID URI: https://signin.aws.amazon.com/saml
# Username: [email protected]
# Default role ARN: (optional — skip if you want to choose at login)
# Default session duration (hours): 1
That writes a block to your ~/.aws/config file that looks like this — nothing sensitive, just configuration identifiers:
[profile mycompany-dev]
azure_tenant_id = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
azure_app_id_uri = https://signin.aws.amazon.com/saml
azure_default_username = [email protected]
azure_default_role_arn = arn:aws:iam::123456789012:role/Developer
azure_session_duration = 3600
region = us-east-1
Log in and get to work
# Login with your configured profile
aws-azure-login --profile mycompany-dev
# Use the credentials with the AWS CLI
aws s3 ls --profile mycompany-dev
# Or export the profile for the whole terminal session
export AWS_PROFILE=mycompany-dev
aws s3 ls
--mode gui which opens the browser visibly so you can watch exactly where it’s getting stuck. Nine times out of ten it’s a new UI element Puppeteer isn’t handling. Check the GitHub issues page — breakages get reported and patched quickly.
Why This Matters Beyond Just Convenience
I want to be clear that this isn’t just a quality-of-life improvement for developers. There are real operational and security properties here that matter in enterprise financial environments — things that come up in security reviews and compliance conversations.
The One Real Limitation Worth Knowing About
I want to be honest about this because I’ve hit it myself: aws-azure-login is inherently brittle. The package documentation says this openly, and the maintainers deserve credit for that transparency.
Because the tool is driving a real browser through a UI that Microsoft controls, any change on their side can break the automation without warning. This has happened. The Azure login page gets updated, Puppeteer can’t find the element it’s looking for, and the tool fails. It usually gets patched within a few days once someone files an issue, but if you’re on a deadline and it breaks on a Tuesday morning, that’s not great.
A few things I’d recommend if you’re rolling this out to a team:
- Pin to a specific version and test upgrades before pushing to everyone
- Make sure everyone knows about
--mode gui— visible browser mode lets you authenticate manually when the headless flow breaks - Keep the GitHub issues page bookmarked — if something breaks, the fix is usually already there
- Think of this as a bridge tool while your org evaluates AWS IAM Identity Center for a more robust long-term solution