In my previous post I set up AWS DevOps Agent across a couple of AWS accounts — Agent Space, IAM roles, the lot. The bit I left out was login.
We already use Microsoft Entra ID for everything else, so putting the DevOps Agent web app behind the same SSO was the obvious next step. It took about twenty minutes. Here's the whole thing, start to finish.
What you're actually setting up
It's OIDC, not SAML. AWS DevOps Agent runs an authorization code flow with PKCE against Entra, and you give it four values: issuer, client ID, client secret, and a redirect URI that AWS generates for you.
Two things stay separate, and it's worth being clear about which is which:
Microsoft Entra ID → decides WHO can open the web app
AWS IAM roles → decide WHAT the agent can read in your accounts
Setting up SSO changes the front door. It doesn't change anything about what the agent can reach.
Before you start
- An Agent Space that already exists — the IdP config lives on it
- Permission to create an App Registration and a client secret in Entra
- Admin access to the AWS DevOps Agent console
- The Region your Agent Space is in. I lost five minutes staring at an empty list because I was in the wrong one.
You don't need IAM Identity Center. This replaces it.
Step 1 — Register an app in Entra
Entra admin center → Entra ID → App registrations → New registration
Three things on the form:
-
Name — I used
AWS DevOps Agent. It shows up on the consent screen, so pick something people will recognise. - Supported account types — Accounts in this organizational directory only. Single tenant.
- Redirect URI — leave it blank.
That last one feels wrong, but AWS won't give you the redirect URI until the IdP is connected, so you have to come back for it in Step 4.
Hit Register, then grab two values off the Overview page: Application (client) ID and Directory (tenant) ID.
Step 2 — Create a client secret
Certificates & secrets → Client secrets → New client secret
Add a description, pick an expiry, hit Add.
Now the part that trips people up: the table shows a Value and a Secret ID. AWS wants the Value. Copy it now — it's masked the moment you leave the page and there's no way to get it back.
On expiry, Entra caps you at 24 months and recommends under 12. I went with the maximum, which I'd now do differently — AWS has a Rotate client secret button that doesn't disconnect anything, so rotation is cheap. Whatever you pick, put the date in a calendar. When the secret expires, nobody can log in, and there's no warning anywhere in the AWS console.
Step 3 — Connect it in AWS
DevOps Agent console → Agent Spaces → your space → Access tab → User access → External identity provider
Four fields:
| Field | What goes in it |
|---|---|
| Identity Provider | Microsoft Entra |
| Issuer URL | https://login.microsoftonline.com/<TENANT_ID>/v2.0 |
| Client ID | Application (client) ID from Step 1 |
| Client Secret | the secret Value from Step 2 |
The issuer has to be the tenant-specific v2.0 URL. Not common, not without the /v2.0, no trailing slash.
There's also a role option below — auto-create, use existing, or from a template. Auto-create is fine here; this role is just for the web app and isn't the one reaching into your other accounts.
Hit Connect. AWS immediately shows you two URLs:
Callback URL: https://<AGENT_SPACE_ID>.aidevops.global.app.aws/authorizer/idp/callback
Login URL: https://<AGENT_SPACE_ID>.aidevops.global.app.aws/authorizer/idp/login
Copy the callback URL.
Step 4 — Paste the redirect URI back into Entra
App Registration → Authentication → Add a platform → Web
Paste the callback URL. Platform type Web, not SPA — the token exchange happens server-side with the client secret.
Paste it, don't retype it. Entra matches the redirect URI exactly, the path is case-sensitive, and there's no trailing slash. Get it wrong and you'll get AADSTS50011 at login, which at least tells you what's broken.
Step 5 — Restrict who can log in
Easy to skip, and worth thirty seconds.
Enterprise applications → your app → Properties → Assignment required? → Yes
Then Users and groups → Add user/group and assign a group.
Assigning people without flipping that toggle does nothing — it defaults to No, and any account in your tenant can sign in. Since opening the web app is the access control for a shared Agent Space, this is the setting that matters.
Step 6 — Test it
Open the Login URL in a private window. Use private browsing — with an existing Microsoft session it all happens too fast to tell whether it actually worked.
This is the AWS-hosted hand-off page, and the one your team will actually meet day to day once a session has lapsed. Log in with SSO starts the flow:
From a clean session the Login URL skips straight past it:
Login URL
↓ straight to Microsoft, no AWS login page
Microsoft sign-in + your usual MFA
↓ consent prompt (first time only)
AWS DevOps Agent, logged in
AWS never asks for a username or password of its own — credentials only ever get typed on Microsoft’s side. If anything other than Microsoft prompts you for one, something is wrong.
When it doesn't work
-
AADSTS50011— redirect URI mismatch. Compare the two character by character, including case. - Login works, app then errors — same thing. Recheck the redirect URI.
-
"IdP configuration error" — no refresh token came back. Add
offline_accessas a delegated Microsoft Graph permission on the app registration. - Fails before Microsoft even loads — issuer URL is wrong. And note you can't edit it: AWS only lets you change the client secret after connecting, everything else means disconnect and reconnect. Disconnecting also wipes chat and artifact history, so get it right the first time.
- Worked yesterday, dead today — check the client secret expiry.
- Nothing works and the secret looks right — make sure you pasted the Value and not the Secret ID. They sit next to each other and look identical.
- Blocked by policy — check the Enterprise Application's Sign-in logs before touching anything in AWS. Conditional Access failures show up there with a real error code.
One more: don't add a groups claim under Token configuration. AWS doesn't use group membership and it causes authentication failures.
Bonus — a nicer URL
The web app lives at https://<AGENT_SPACE_ID>.aidevops.global.app.aws. Nobody is remembering that, and it looks like phishing when you paste it in Slack. So we put a small Nginx pod in front of it and gave the team devops-agent.<YOUR_DOMAIN> instead. Convenience only — the real boundary is still Entra.
server {
listen 80;
server_name devops-agent.<YOUR_DOMAIN>;
location / {
# variable + resolver, so Nginx re-resolves DNS at runtime instead of
# caching the CloudFront IPs forever at startup
resolver 8.8.8.8 valid=30s ipv6=off;
set $upstream "<AGENT_SPACE_ID>.aidevops.global.app.aws";
# upstream needs both SNI and Host set to its own name
proxy_pass https://$upstream/authorizer/idp/login;
proxy_ssl_server_name on;
proxy_set_header Host $upstream;
}
}
Note it points at the login path, not /. This is a front door, not a proxy for the whole app.
The one thing to know: don’t add your custom domain as a redirect URI in Entra. AWS builds the redirect_uri from the Agent Space ID, so the login always comes back to the AWS hostname whichever door you walked in through. Your domain never appears in the OAuth flow at all.
A plain return 302 to the login URL does the same job in one line, if you would rather skip the proxy. You land on the AWS URL either way.
The whole thing
devops-agent.<YOUR_DOMAIN>
│
▼
Nginx (EKS)
│
▼
AWS DevOps Agent Agent Space
│ │
▼ ▼
Microsoft Entra ID IAM roles
(who logs in) (what it can read)
│
┌─────┴─────┐
▼ ▼
Dev Prod
That's it. Entra handles the login, IAM handles the access, and the team just bookmarks one short URL.
If you want the IAM and multi-account half — trust policies, Terraform, connecting secondary accounts — that's all in the previous post.



Top comments (0)