Skip to main content

Entra ID Federation — SAML/SCIM Seam (L1)

Scope: TI-2e — L1 federation only (Entra ID ↔ IAM Identity Center). Keycloak (L2) is a separate federation layer documented elsewhere. Pre-requisite: complete identity-center-enablement.mdx (TI-2a) first. ADR-020 rationale for L1/L2 non-conflation is cross-referenced below.


Section 1 — Understand the Entra Seam in modules/sso

As the HITL operator, I want to understand how modules/sso v1.3.0 exposes the Entra federation seam so that I know exactly which Terraform variables to set when wiring SCIM- provisioned users and groups from Entra ID.

The two seam variables

modules/sso exposes two variables specifically for external IdP scenarios:

existing_sso_users (variables.tf lines 71–78):

variable "existing_sso_users" {
description = "Names of the existing users that you wish to reference from IAM Identity Center."
type = map(object({
user_name = string
group_membership = optional(list(string), null)
# group_membership: only used if your IdP syncs users only (not groups);
# set this to add SCIM-provisioned users to Terraform-managed groups.
}))
default = {}
}

Use this variable to reference Entra-provisioned users in account assignments without managing their lifecycle inside Terraform. Entra SCIM creates and updates the users; Terraform assigns them to groups and permission sets.

principal_idp in account_assignments (variables.tf line 117):

variable "account_assignments" {
type = map(object({
principal_idp = string # "INTERNAL" or "EXTERNAL"
...
}))
}

Set principal_idp = "EXTERNAL" for any assignment whose principal (user or group) originates from Entra ID (or any external IdP). Set principal_idp = "INTERNAL" for principals managed natively inside Identity Center.

Step 1.1 — READONLY verify: confirm the seam exists in the live module

# READONLY verify
grep -n 'existing_sso_users\|principal_idp' \
terraform-aws/modules/sso/variables.tf
# Expected output includes:
# 71: variable "existing_sso_users" {
# 117: principal_idp = string # "INTERNAL" or "EXTERNAL"

Branch on result:

  • If both lines are found → seam is live in the working tree; continue to Section 2.
  • If either line is missing → check modules/sso version; the seam was introduced in v1.2.0 and is present in v1.3.0 (current).

5W1H — Entra Seam in modules/sso

QuestionAnswer
WhyEntra SCIM creates users and groups in Identity Center's identity store; Terraform must reference those objects without owning their lifecycle — existing_sso_users is the pointer, not the creator.
What if missingTerraform attempts to manage user lifecycle and conflicts with SCIM provisioning; the result is a state-drift loop where Terraform destroys SCIM-created users on every apply.
Business valueEntra-managed users get automatic provisioning and deprovisioning; offboarding removes access across all AWS accounts in seconds without Terraform changes.
PurposeSeparates the two concerns: Entra owns identity lifecycle (create/update/delete); Terraform owns access policy (which groups get which permission sets on which accounts).
Critical thinkingADR-020 mandates L1/L2 non-conflation: Entra federation (L1) and Keycloak federation (L2) must not share the same Identity Center identity source. If both are needed, L1 (Entra) is the direct IdP to Identity Center; Keycloak sits behind a separate boundary. Do not configure Keycloak as the Identity Center IdP when Entra is the org-wide SSO provider.

Section 2 — Configure SAML in Entra and Identity Center

As the HITL operator, I want to set up the SAML trust between Entra ID and IAM Identity Center so that OceanSoft engineers can authenticate to AWS using their Entra credentials.

Step 2.1 — READONLY verify: no existing external IdP is configured

# READONLY verify
aws sso-admin describe-managed-policy-in-permission-set \
--instance-arn arn:aws:sso:::instance/<YOUR_INSTANCE_ID> \
--permission-set-arn <ANY_PERMISSION_SET_ARN> \
--managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
--region <HOME_REGION> \
--profile sso 2>&1 | head -5
# This is a connectivity check only — not the IdP query.
# Navigate to Identity Center console → Settings → Identity source to confirm source type.

Branch on result:

  • If Identity source shows Identity Center directory → no IdP configured; continue.
  • If Identity source shows External identity provider and it is already Entra → verify the SAML metadata is current; skip to Step 2.4.
  • If Identity source shows Active Directory → a different federation path is in use; consult your AD team before changing the source.

Step 2.2 — Create the Entra Enterprise Application [HITL]

  1. Sign in to the Microsoft Entra admin centre as a Global Administrator or Cloud Application Administrator.
  2. Navigate to Enterprise applicationsNew application.
  3. Search for AWS IAM Identity Center and select the gallery app.
  4. Click Create.
  5. In the app overview, go to Single sign-onSAML.
  6. In the SAML Signing Certificates section, download Federation Metadata XML and save as entra-metadata.xml locally.

Step 2.3 — Change the Identity Center identity source [HITL]

  1. In IAM Identity Center console → SettingsIdentity source tab.
  2. Click ActionsChange identity source.
  3. Select External identity provider.
  4. In Service provider metadata, copy:
    • IAM Identity Center Assertion Consumer Service (ACS) URL
    • IAM Identity Center issuer URL
  5. In Identity provider metadata, upload entra-metadata.xml from Step 2.2.
  6. Type ACCEPT to confirm the change and click Change identity source.
Changing identity source is irreversible without downtime

Changing from Identity Center directory to an external IdP deprovisions all local users immediately. Ensure the Entra SCIM provisioning is configured (Section 3) before this step, or you will lose access to the console. Always have a break-glass IAM user in the management account as a fallback.

Step 2.4 — Complete the Entra SAML configuration [HITL]

  1. Return to the Entra Enterprise application → Single sign-onSAML.
  2. In Basic SAML Configuration:
    • Identifier (Entity ID): paste the IAM Identity Center issuer URL from Step 2.3.
    • Reply URL (ACS URL): paste the ACS URL from Step 2.3.
    • Leave Sign on URL and Relay State empty.
  3. Click Save.

Post-config verify:

  1. In Entra, assign a test user to the Enterprise application (Entra admin portal → Users and groupsAdd user/group).
  2. Open the AWS access portal URL (https://d-<YOUR_OWN_ID>.awsapps.com/start) in an incognito window.
  3. You should be redirected to the Entra login page. After login, the AWS accounts portal should load.

5W1H — SAML Configuration

QuestionAnswer
WhySAML is the authentication layer; it proves the user's identity to Identity Center using a signed assertion from Entra; without it every sign-in attempt redirects to an error page.
What if missingEngineers cannot authenticate via Entra; the AWS access portal shows an authentication error; SCIM provisioning works but nobody can log in.
Business valueSingle sign-on across Office 365 and AWS: engineers use one password and one MFA token for both; onboarding/offboarding is a single Entra action with immediate AWS effect.
PurposeEstablishes the cryptographic trust between Entra (the IdP) and Identity Center (the service provider) so that signed SAML assertions are accepted.
Critical thinkingThe ACS URL and issuer URL are unique per Identity Center instance; if you re-enable Identity Center or change regions, they change and the Entra application must be updated. Always re-download service provider metadata after any Identity Center instance recreation.

Section 3 — Configure SCIM Provisioning

As the HITL operator, I want to enable SCIM so that Entra automatically provisions and deprovisions users and groups in Identity Center — enabling the existing_sso_users seam in Terraform to reference Entra-managed users without managing their lifecycle.

Step 3.1 — Enable automatic provisioning in Identity Center [HITL]

  1. In IAM Identity Center console → SettingsProvisioning tab.
  2. Click Enable in the Inbound automatic provisioning section.
  3. Copy:
    • SCIM endpoint (form: https://scim.<HOME_REGION>.amazonaws.com/<INSTANCE_ID>/scim/v2)
    • Access token (shown once; save it securely)

Step 3.2 — Configure provisioning in the Entra app [HITL]

  1. In the Entra Enterprise application → ProvisioningGet started.
  2. Set Provisioning Mode to Automatic.
  3. In Admin Credentials:
    • Tenant URL: paste the SCIM endpoint from Step 3.1.
    • Secret Token: paste the Access token from Step 3.1.
  4. Click Test Connection. Expect: "The supplied credentials are authorized to enable provisioning."
  5. Click Save.
  6. Under ProvisioningMappingsProvision Entra ID Groups → confirm Enabled.
  7. Under MappingsProvision Entra ID Users → confirm Enabled.
  8. Under Settings, set Provisioning Status to On.
  9. Click Save.

Step 3.3 — Verify SCIM is delivering users to Identity Center

# READONLY verify — check that at least one SCIM-provisioned user exists
aws identitystore list-users \
--identity-store-id <YOUR_IDENTITY_STORE_ID> \
--region <HOME_REGION> \
--profile sso \
--query 'Users[*].{UserName:UserName,UserId:UserId}'
# Expected: one or more users provisioned by Entra (after first sync completes ~5-10 min)

Branch on result:

  • If users appear → SCIM is working; continue to Section 4.
  • If no users after 10 minutes → check Entra provisioning logs (Entra admin → Provisioning logs) for errors; common causes: group assignment missing in Entra, attribute mapping mismatch, access token expired.

5W1H — SCIM Provisioning

QuestionAnswer
WhySAML handles authentication (login) but not provisioning (creating accounts); without SCIM, every new engineer must be manually added to Identity Center before they can log in — that is the manual-provisioning anti-pattern that Entra solves.
What if missingUsers authenticate via Entra SAML but Identity Center has no user record for them; the portal shows "user not found"; Terraform existing_sso_users has no targets to reference.
Business valueZero-touch provisioning: a new hire added to the Entra group for PowerUsers gets AWS access within minutes; a leavers' Entra account deactivation cascades to all AWS accounts automatically.
PurposeCreates the live user records in Identity Center's identity store that existing_sso_users in config/account_assignments.yaml will reference for account assignments.
Critical thinkingSCIM syncs users and groups but does NOT assign permission sets; that is Terraform's job via account_assignments. The SCIM-created group names in Identity Center must match the principal_name values in config/account_assignments.yaml exactly — case-sensitive.

Section 4 — Wire Terraform to Reference Entra Users

As the HITL operator, I want to know the exact YAML and Terraform patterns for referencing Entra-provisioned users in account assignments so that I can extend config/account_assignments.yaml without managing user lifecycle in Terraform.

Step 4.1 — READONLY verify: list the SCIM-provisioned group names

# READONLY verify — get exact group names as they appear in Identity Center
aws identitystore list-groups \
--identity-store-id <YOUR_IDENTITY_STORE_ID> \
--region <HOME_REGION> \
--profile sso \
--query 'Groups[*].DisplayName'
# Expected: group names provisioned by Entra SCIM
# These names must match principal_name in account_assignments.yaml EXACTLY

Step 4.2 — Extend config/account_assignments.yaml for Entra groups

For SCIM-provisioned groups (the recommended approach — manage group membership in Entra):

# In config/account_assignments.yaml
# Entra-provisioned group assignment — principal_idp: EXTERNAL
EntaDevs_B2B_PowerUser:
principal_name: "Entra-Developers" # exact SCIM group name from Step 4.1
principal_type: GROUP
principal_idp: EXTERNAL # <— this is the key seam variable
permission_sets:
- PowerUserAccess
account_ids:
- oceansoft-b2b # account name resolved via enable_organizations_lookup

For SCIM-provisioned individual users (only when group management is unavailable):

# In infra/terraform/aws/identity/main.tf (or terraform.tfvars) — the pilot consumer root
existing_sso_users = {
"engineer-alice" = {
user_name = "alice@oceansoft.io" # exact SCIM username
group_membership = ["PlatformTeam"] # optional: add to Terraform-managed group
}
}
Prefer group-based assignments

Individual user assignments (existing_sso_users) work but do not scale. Entra group → Identity Center group → permission set → account assignment is the pattern that requires zero Terraform changes when team composition changes.


5W1H — Terraform Entra Wiring

QuestionAnswer
WhyThe principal_idp = "EXTERNAL" flag tells the modules/sso account assignment resource to look up the principal in the Identity Center identity store (where SCIM put it) rather than in the local Terraform-managed user set; without it the assignment fails with a "principal not found" error.
What if missingAccount assignments for Entra-provisioned principals fail at apply time; engineers authenticated via Entra SAML have no permission set and see an empty AWS portal.
Business valueTerraform governs access policy (what can be done) while Entra governs identity (who is allowed in); the two responsibilities are separated by the principal_idp flag — this is the principle of least coupling between IdP and cloud access governance.
PurposeCloses the loop: SAML proves identity → SCIM provisions the user record → principal_idp = "EXTERNAL" wires the Terraform assignment to the provisioned record → the engineer sees their assigned accounts in the AWS portal.
Critical thinkingSCIM group names and Terraform principal_name values must be identical strings. If Entra SCIM uses AWS-Developers but account_assignments.yaml says Developers, the assignment silently fails. Run the Step 4.1 verify command and copy-paste the exact names rather than typing them.

References

  • Pre-requisite runbook: identity-center-enablement.mdx (TI-2a) — must complete first
  • ADR-020 (L1/L2 non-conflation, Entra-not-Keycloak): docs/content/architecture/adrs/
  • ADR-021 (parallel 2-account topology): docs/content/architecture/adrs/ADR-021-parallel-2-account-topology.md
  • Module seam: terraform-aws/modules/sso/variables.tf lines 71-78 (existing_sso_users), line 117 (principal_idp)
  • AWS documentation: Set up SAML with Entra ID