Set up workforce SSO with IAM Identity Center and an external IdP

Give your people single sign-on to AWS accounts — connect Okta, Microsoft Entra ID or another SAML 2.0 IdP to IAM Identity Center, turn on SCIM provisioning, create permission sets, assign groups to accounts (console or TypeScript), and verify from the portal and the CLI, avoiding the attribute and NameID mistakes that block sign-in.

markdownguidev47P0E-SFRGstage: buildtrust: officialservice: identity-centerinterface: consoleinterface: sdkinterface: cliSSOsingle sign-onIAM Identity Center setupexternal identity providerSAML 2.0SCIM provisioningOktaMicrosoft Entra IDAzure ADGoogle WorkspaceACS URLissuer URLSCIM access tokenpermission setaccount assignmentCreateAccountAssignmentCommandProvisionPermissionSetaws configure sso

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

Keep users in your IdP and access in AWS. The IdP (Okta, Entra ID, Google Workspace, …) authenticates people over SAML 2.0 and pushes users and groups into Identity Center over SCIM. Identity Center decides what they can do through permission sets assigned to groups on accounts. Users must exist in Identity Center before they can sign in. SAML can't create them on the fly.

0. Prerequisites

  • An organization instance of Identity Center, enabled from the Organizations management account (or managed by a delegated administrator). Account assignments need an organization instance. On instances created since August 2026, "AWS account access" may be off: turn it on in Settings, or with UpdateInstance, before you create permission sets.
  • One identity source per organization. Switching to an external IdP changes how every user signs in.

1. Exchange SAML metadata

  1. In the IdP, add the gallery app named AWS IAM Identity Center (Okta and Entra ID both have one) and download its IdP metadata XML.
  2. In Identity Center, go to Settings → Identity source → Actions → Change identity source → External identity provider. Copy the ACS URL and issuer URL (or the service-provider metadata file), upload the IdP's metadata, type ACCEPT, and confirm.
  3. Back in the IdP, set the ACS URL and issuer URL on the app. The username or NameID it sends must be unique per user and is what users sign in as.

The rules that decide whether sign-in works:

  • The NameID format is urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress, an RFC 2822 address that equals the Identity Center user name.
  • One assertion per response, and the assertion is not encrypted.
  • For SCIM users, the attribute sent as the SAML Subject NameID must be the same one mapped to Username in SCIM.

2. Turn on SCIM provisioning

  1. In Identity Center Settings, enable Automatic provisioning. Copy the SCIM endpoint (https://scim.<region>.amazonaws.com/<id>/scim/v2, or dual-stack scim.<region>.api.aws) and the access token. This is the only time both are shown.
  2. In the IdP's provisioning settings, enter them (Okta: Base URL / API Token; Entra ID: Tenant URL / Secret Token, mode Automatic), test the connection, and save.
  3. Assign users or groups to the app in the IdP. Only assigned users sync. In Okta, also push the groups, using a different group from the one used for assignment. In Entra ID, start provisioning, and remember that nested groups are not provisioned: only direct members of assigned groups sync.

Each synced user needs a first name, last name, username and display name with single values (one email, one phone). Map a stable, unique externalId (an immutable object ID), so renames don't orphan a user's access.

  • The SCIM token lasts one year. Reminders begin 90 days before expiry. When it expires, sync stops silently. Put the rotation date in a calendar.
  • Once SCIM is on, edit users in the IdP, not in the Identity Center console. Delete users there too: deleting in Identity Center breaks the IdP's push.

3. Create permission sets and assign groups

Console: Multi-account permissions → Permission sets → Create (start with a predefined one such as PowerUserAccess), then AWS accounts → select accounts → Assign users or groups.

TypeScript, AWS SDK for JavaScript v3:

import {
  SSOAdminClient,
  ListInstancesCommand,
  CreatePermissionSetCommand,
  AttachManagedPolicyToPermissionSetCommand,
  CreateAccountAssignmentCommand,
  DescribeAccountAssignmentCreationStatusCommand,
  ProvisionPermissionSetCommand,
} from "@aws-sdk/client-sso-admin";
import { IdentitystoreClient, GetGroupIdCommand } from "@aws-sdk/client-identitystore";

const region = "<Identity Center Region>";
const sso = new SSOAdminClient({ region });
const ids = new IdentitystoreClient({ region });

const inst = (await sso.send(new ListInstancesCommand({}))).Instances?.[0];
const InstanceArn = inst?.InstanceArn!;
const IdentityStoreId = inst?.IdentityStoreId!;

const ps = await sso.send(new CreatePermissionSetCommand({
  InstanceArn,
  Name: "Developer",
  SessionDuration: "PT4H", // ISO 8601; 1 h minimum, 12 h maximum
}));
const PermissionSetArn = ps.PermissionSet?.PermissionSetArn!;

await sso.send(new AttachManagedPolicyToPermissionSetCommand({
  InstanceArn,
  PermissionSetArn,
  ManagedPolicyArn: "arn:aws:iam::aws:policy/PowerUserAccess",
}));

// Groups come from your IdP via SCIM; look one up by its display name.
const { GroupId } = await ids.send(new GetGroupIdCommand({
  IdentityStoreId,
  AlternateIdentifier: { UniqueAttribute: { AttributePath: "displayName", AttributeValue: "aws-developers" } },
}));

const req = await sso.send(new CreateAccountAssignmentCommand({
  InstanceArn,
  PermissionSetArn,
  PrincipalType: "GROUP",
  PrincipalId: GroupId!,
  TargetType: "AWS_ACCOUNT",
  TargetId: "<12-digit account id>",
}));

let status = req.AccountAssignmentCreationStatus;
while (status?.Status === "IN_PROGRESS") {
  await new Promise((r) => setTimeout(r, 3_000));
  status = (await sso.send(new DescribeAccountAssignmentCreationStatusCommand({
    InstanceArn,
    AccountAssignmentCreationRequestId: status.RequestId!,
  }))).AccountAssignmentCreationStatus;
}
console.log(status?.Status, status?.FailureReason);

// Later, after editing the permission set through the API, push the change to every account:
await sso.send(new ProvisionPermissionSetCommand({ InstanceArn, PermissionSetArn, TargetType: "ALL_PROVISIONED_ACCOUNTS" }));
  • Assignments are asynchronous. Poll the status as shown. ConflictException means an earlier write hasn't propagated yet: retry with backoff.
  • API and IaC edits to a permission set are not pushed automatically. Call ProvisionPermissionSet. The console reprovisions for you.
  • Assign groups, not users, so membership changes in the IdP flow through SCIM without AWS changes. The SessionDuration format is ISO 8601 (for example PT8H; format per the SSO Admin API model, the example is the hub's); the documented range is 1–12 hours.

Each assignment creates a role named AWSReservedSSO_<PermissionSet>_<suffix> in the account (reference/identity-center/provisioned-roles-and-sessions.md).

4. Verify

  1. As a test user, open the AWS app from the IdP's dashboard (IdP-initiated sign-in) and go to the access portal URL (SP-initiated sign-in). Both should land in the portal, showing the assigned account and permission set.
  2. CLI: aws configure sso (session name, start URL, Region, scope sso:account:access), then aws sso login --profile <p>, then aws sts get-caller-identity --profile <p>. The ARN should show assumed-role/AWSReservedSSO_…. Details: guides/iam/local-credentials.md.
  3. Multiple Regions: add each additional Region's ACS URL to the IdP app (Okta: Other Requestable SSO URLs; Entra ID: Reply URLs).

If it fails

Look the exact message up in reference/identity-center/errors.md. To see the assertion Identity Center sends to an application, hold Shift while choosing the app tile in the portal. For failed external-IdP sign-ins, filter CloudTrail on ExternalIdPDirectoryLogin.

Read this document as markdown — the same bytes an agent receives.