How to set up AWS IAM Identity Center (SSO) for a startup

· 8 min read · aws · security

Most startups we meet still log in to AWS with IAM users and keep access keys on laptops. IAM Identity Center replaces all of that with one login per person, short-lived credentials and access you can revoke in one place. This is the setup we use, from enabling it to deleting the last IAM user.

The short version: enable IAM Identity Center in the management account of your AWS Organization, connect your identity provider (or use the built-in directory), create a few permission sets (Admin, PowerUser, ReadOnly, Billing), assign groups to accounts, and switch everyone's CLI to aws configure sso. Then delete the IAM users. Keep one documented break-glass path for when SSO itself is broken.

AWS doesn't charge extra for IAM Identity Center, and once it is in place, onboarding and offboarding an engineer becomes a change in one directory rather than a hunt across accounts.

Before you start: you need AWS Organizations

Enable Identity Center from the management account of an AWS Organization. That creates an organization instance, which is the only kind that can grant access across multiple AWS accounts.

AWS also offers account instances for a single standalone or member account, but according to the AWS comparison of instance types they don't support multi-account permissions or access-portal sign-in to AWS accounts. For workforce access to AWS, you want the organization instance.

If you don't have an organization yet, set one up first, with at least separate accounts for production and everything else. We explain the account structure we recommend in AWS Organizations from day one.

Step 1: enable Identity Center and pick a region

Sign in to the management account, open the IAM Identity Center console in the region where you want it to live, and choose Enable. For a company operating in the EU, choose the EU region you already run in, such as eu-central-1 or eu-west-1, so the identity store sits next to the rest of your estate.

Two settings to change straight away:

Step 2: choose the identity source

If your company already runs Google Workspace or Microsoft Entra ID, use it as an external identity provider so that leaving the company removes AWS access automatically. Use the built-in Identity Center directory only if you have no central directory at all.

Identity sourceSign-in and MFAUser provisioningGroups
Identity Center directory (built-in)Managed in Identity Center; turn on MFA in settingsManual (console, API, Terraform)Managed in Identity Center
Google WorkspaceSAML; MFA enforced by GoogleSCIM, users onlyNot synced; create manually or use ssosync
Microsoft Entra IDSAML; MFA and Conditional Access by EntraSCIMSynced via SCIM

The Google Workspace limitation catches people out. AWS's Google Workspace setup guide states that SCIM sync from Google is limited to users and that automatic group provisioning isn't supported. You can create groups in Identity Center with the Identity Store API (or Terraform, below) and manage membership there, or run AWS's open-source ssosync tool to mirror Google groups. For a team of ten, managing three or four groups in Terraform is usually simpler than running another sync job.

Microsoft Entra ID provisions both users and groups over SCIM, so group membership in Entra drives AWS access directly. See the Entra ID tutorial.

When you switch to an external IdP, the console shows you a SCIM endpoint and access token exactly once. Copy them into the IdP before closing the dialog, and put a calendar reminder on the token's rotation.

Step 3: create groups and permission sets

A permission set is a template of IAM policies. When you assign it to an account, Identity Center creates a matching IAM role in that account. Start with four, mapped to how people actually work, and assign them to groups, never to individuals (with one exception, below).

Permission setAWS managed policyWho gets itSession
AdministratorAccessAdministratorAccessPlatform team, in non-prod; one or two people in prod1 hour
PowerUserAccessPowerUserAccessEngineers, in dev and staging8 hours
ReadOnlyAccessReadOnlyAccessEngineers in production, on-call8 hours
Billingjob-function/BillingFounders, finance4 hours

Session duration defaults to one hour and can go up to twelve, per the session duration documentation. Keep admin sessions short, so a forgotten terminal doesn't stay privileged all day.

PowerUserAccess allows everything except IAM and Organizations management, which is a good default for building things in a dev account. In production, read-only by default and changes through CI is the healthier pattern; your deploy pipeline should use its own role via OIDC rather than a person's session (see deploying from GitHub Actions without access keys).

As the team grows, replace the broad managed policies with custom permission sets, for example a "Developer" set that can read logs, connect to ECS tasks and nothing more in production.

Step 4: assign access to accounts

An assignment is a triple: group, permission set, account. A typical startup matrix looks like this:

Groupdev / stagingproductionmanagement
platform-adminsAdministratorAccessAdministratorAccessUsers assigned directly
engineersPowerUserAccessReadOnlyAccessnone
financenonenoneBilling

The management account is the exception to "groups only". AWS recommends assigning users, not groups, to permission sets there, because anyone who can edit group membership in your IdP would otherwise control who reaches the most privileged account in the organization.

Step 5: manage it in Terraform

Click-ops is fine for the first test, but permission sets and assignments belong in code, reviewed like any other change. Run this from the management account or the delegated administrator account.

data "aws_ssoadmin_instances" "this" {}

locals {
  instance_arn      = tolist(data.aws_ssoadmin_instances.this.arns)[0]
  identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0]

  permission_sets = {
    AdministratorAccess = { policy = "arn:aws:iam::aws:policy/AdministratorAccess", duration = "PT1H" }
    PowerUserAccess     = { policy = "arn:aws:iam::aws:policy/PowerUserAccess", duration = "PT8H" }
    ReadOnlyAccess      = { policy = "arn:aws:iam::aws:policy/ReadOnlyAccess", duration = "PT8H" }
    Billing             = { policy = "arn:aws:iam::aws:policy/job-function/Billing", duration = "PT4H" }
  }

  # group, permission set, account ID
  assignments = [
    { group = "engineers", set = "PowerUserAccess", account = "111111111111" }, # dev
    { group = "engineers", set = "ReadOnlyAccess", account = "222222222222" },  # prod
    { group = "platform-admins", set = "AdministratorAccess", account = "111111111111" },
    { group = "platform-admins", set = "AdministratorAccess", account = "222222222222" },
  ]
}

resource "aws_ssoadmin_permission_set" "this" {
  for_each         = local.permission_sets
  name             = each.key
  instance_arn     = local.instance_arn
  session_duration = each.value.duration
}

resource "aws_ssoadmin_managed_policy_attachment" "this" {
  for_each           = local.permission_sets
  instance_arn       = local.instance_arn
  permission_set_arn = aws_ssoadmin_permission_set.this[each.key].arn
  managed_policy_arn = each.value.policy
}

resource "aws_identitystore_group" "this" {
  for_each          = toset(["engineers", "platform-admins", "finance"])
  identity_store_id = local.identity_store_id
  display_name      = each.key
}

resource "aws_ssoadmin_account_assignment" "this" {
  for_each = { for a in local.assignments : "${a.group}-${a.set}-${a.account}" => a }

  instance_arn       = local.instance_arn
  permission_set_arn = aws_ssoadmin_permission_set.this[each.value.set].arn
  principal_type     = "GROUP"
  principal_id       = aws_identitystore_group.this[each.value.group].group_id
  target_type        = "AWS_ACCOUNT"
  target_id          = each.value.account
}

If your groups come from Entra ID over SCIM, don't create them in Terraform; look them up with the aws_identitystore_group data source instead, so Terraform and SCIM don't fight over the same objects. With Google Workspace or the built-in directory, the resource above is the right approach. We keep this in its own Terraform stack with its own state, as described in how we organise Terraform state.

Step 6: switch the CLI to SSO

Every engineer runs aws configure sso once, then aws sso login when their session expires. No access keys on disk.

$ aws configure sso
SSO session name (Recommended): acme
SSO start URL [None]: https://acme.awsapps.com/start
SSO region [None]: eu-central-1
SSO registration scopes [None]: sso:account:access

The CLI opens a browser, you pick an account and role, and it writes a profile plus a shared sso-session block to ~/.aws/config. Add more profiles by hand so they reuse the same session:

[sso-session acme]
sso_start_url = https://acme.awsapps.com/start
sso_region = eu-central-1
sso_registration_scopes = sso:account:access

[profile acme-dev]
sso_session = acme
sso_account_id = 111111111111
sso_role_name = PowerUserAccess
region = eu-central-1

[profile acme-prod]
sso_session = acme
sso_account_id = 222222222222
sso_role_name = ReadOnlyAccess
region = eu-central-1
aws sso login --sso-session acme
aws sts get-caller-identity --profile acme-prod

Since AWS CLI 2.22.0, sign-in uses PKCE authorization by default; on a machine without a browser, add --use-device-code. Details are in the AWS CLI SSO guide. Terraform, the AWS SDKs and most tools read these profiles directly, so AWS_PROFILE=acme-dev terraform plan just works.

Step 7: remove IAM users and long-lived keys

SSO only improves security once the old way in is gone. Do it deliberately, account by account.

  1. Generate a credential report in each account (aws iam generate-credential-report, then aws iam get-credential-report) and list every IAM user, password and access key with its last-used date.
  2. Sort them into people (move to SSO), CI systems (move to OIDC roles) and applications (move to instance, task or pod roles).
  3. Deactivate keys before deleting them. Wait a week, watch for breakage, then delete the keys and the users.
  4. Add a guardrail: an alert on CreateAccessKey and CreateUser events in CloudTrail, or an SCP that denies them outside a break-glass role.

In our experience the stragglers are always the same: a third-party tool configured years ago with an admin key, and a cron job on someone's server. Find them before you delete, not after.

Step 8: plan break-glass access

If your IdP is down, or someone misconfigures Identity Center, you still need a way in. Decide it now, write it down and test it twice a year.

Write the procedure as a short runbook: who can open the safe, how to sign in, what to fix, and how to rotate the credentials afterwards. Our incident response runbook template has a section for exactly this.

A rollout order that works

  1. Enable Identity Center, register the delegated admin, connect the IdP.
  2. Create the four permission sets and groups in Terraform.
  3. Give the platform team access first and have them use only SSO for a week.
  4. Onboard engineers, then finance.
  5. Remove IAM users and keys, add guardrails, test break-glass.

For a small team this takes days, not weeks, and it is one of the controls auditors and enterprise customers check first, whether you are working towards SOC 2 or NIS2. Identity is the first layer of the cloud foundations we put in place for clients; if you would rather have it designed and rolled out for you, alongside account structure and CI access, that is part of our cloud migration and foundations service.

Frequently asked questions

What is the difference between AWS SSO and IAM Identity Center?

They are the same service. AWS renamed AWS Single Sign-On to IAM Identity Center in 2022. The CLI still uses the sso name, so commands like aws configure sso and aws sso login, and the sso_ settings in ~/.aws/config, refer to IAM Identity Center.

Can I use Google Workspace as the identity source for IAM Identity Center?

Yes, via SAML for sign-in and SCIM for user provisioning. AWS documents that SCIM sync from Google Workspace covers users only and does not provision groups, so you create groups in Identity Center yourself (console, API or Terraform) or use the open-source ssosync tool.

How long can an IAM Identity Center session last?

Each permission set has a session duration that defaults to one hour and can be set up to twelve hours. Keep administrator permission sets short and use longer sessions only for lower-risk roles such as read-only access.

Should we delete all IAM users after enabling IAM Identity Center?

Delete IAM users for people and CI systems, moving people to SSO and pipelines to OIDC roles. Keep a documented break-glass path: the management account root user with hardware MFA, and optionally one tightly controlled emergency IAM user with alerts on every use.