Skip to content

Configure AWS SSO for the CLI

How to get aws and Terraform working against our AWS accounts through AWS SSO (IAM Identity Center). This replaces the deprecated alma-iam users and their long-lived access keys.

Prerequisites: AWS CLI v2 (aws --version), and membership in an IAM AWS-SSO Marketplaces-Mobility-* group. Terraform is not required — step 4 only uses it as an example of a tool that reads AWS_PROFILE. Don’t have the group yet? Start from the access guide in Confluence (MONE). It covers who to ask and how long it takes. This page is only the mechanics.

The generator lives in this site’s own repo, so clone it first. Nothing is installed — the script is Python 3.8+ standard library only.

Terminal window
git clone https://github.com/alma-mobility-marketplaces/docs.ajomark.dev.git
cd docs.ajomark.dev
./tools/aws-sso/generate-aws-config.py --stdout # see what you would get
./tools/aws-sso/generate-aws-config.py # write it into ~/.aws/config

The HTTPS URL asks for GitHub credentials. If your GitHub SSH key is set up, clone git@github.com:alma-mobility-marketplaces/docs.ajomark.dev.git instead.

--stdout prints and changes nothing, so run it first if you want to look before you let anything near your config. Don’t want a clone at all? Skip to the copy-paste alternative below.

The write step produces one profile per account and role, named <role>@<account>:

# >>> alma-mobility AWS SSO profiles (generated) >>>
# Do not edit inside this block - regenerate with tools/aws-sso/generate-aws-config.py
[sso-session alma-sso]
sso_start_url = https://alma-sso.awsapps.com/start
sso_region = eu-west-1
sso_registration_scopes = sso:account:access
[profile readonly@ajo-mark-dev]
sso_session = alma-sso
sso_account_id = 226023431967
sso_role_name = ReadOnly
region = eu-west-1
# ... one more profile per account and role ...
# <<< alma-mobility AWS SSO profiles (generated) <<<

Those markers matter: keep your own profiles outside the block and they survive regeneration. What it writes has the detail. If you already had a ~/.aws/config, the previous version is kept as ~/.aws/config.bak.

Two flags are worth knowing while you are here: --roles ReadOnly gives you read-only profiles only, and --environment nonprod leaves the prod accounts out. Everything the script can do is in The AWS SSO config generator.

Prefer to copy-paste? tools/aws-sso/aws-config is the same output, committed. Paste the whole file into ~/.aws/config, marker comments included — keeping them lets the generator take over later without duplicating everything.

Account IDs come from tools/aws-sso/accounts.json, which is where you add or remove an account — never in your local config.

Terminal window
aws sso login --sso-session alma-sso

A browser tab opens and asks you to Allow botocore-client-alma-sso to access your data. That is the AWS CLI itself: botocore is its SDK layer, and alma-sso is the session name from step 1, so the prompt is naming your own CLI rather than a third party. “Applications and AWS accounts” is the sso:account:access scope your generated config asks for.

Choose Allow access. When the tab reports that your credentials were shared successfully, the CLI has its token and you can close it.

One login covers every profile in the session — you do not log in per account.

The role credentials the CLI hands to each command last about an hour, and it renews them from the cached access token for you, so expiry is not something you track. The cached token itself lives as long as the Identity Center session. When that session ends, commands start failing with ExpiredToken and you run the same login command again. How long that session lasts is an Identity Center setting, not something you configure locally.

Terminal window
aws sts get-caller-identity --profile readonly@ajo-mark-dev

Your own name should appear in the ARN:

{
"UserId": "AROAEXAMPLEPRINCIPAL:your.name@almamedia.fi",
"Account": "226023431967",
"Arn": "arn:aws:sts::226023431967:assumed-role/AWSReservedSSO_ReadOnly_<id>/your.name@almamedia.fi"
}

Two things to look for: assumed-role/AWSReservedSSO_…, which means the credentials came from SSO rather than from a static key, and your own address at the end. Note that the SSO identity uses @almamedia.fi even if your mail is @almamedia.net.

Terminal window
# per command
aws s3 ls --profile readonly@ajo-mark-dev
# or for a shell, Terraform, CDK, or any AWS SDK
export AWS_PROFILE=devadmin@ajo-mark-dev
terraform plan

Two habits worth having:

  • Default to readonly@. Switch to devadmin@ only when you are changing something. Having both profiles is what makes that easy. One read-only task needs devadmin@ anyway: ReadOnly has no ssm:StartSession in any account, so port-forwarding to a database cannot start a session.
  • In prod, most of us only have readonly@ — and it covers logs, metrics and nearly all debugging. Prod write access lives in a separate, deliberately small group.

A command with no --profile and no AWS_PROFILE uses the [default] profile. Give it one, so that case is harmless rather than a surprise:

Terminal window
./tools/aws-sso/generate-aws-config.py --default-profile readonly@ajo-mark-dev

That writes a [default] profile with those settings inside the managed block. Keep the flag on later runs — regenerating without it takes the [default] away again.

You lose nothing by setting one, because the order of precedence is --profile, then AWS_PROFILE, then [default]. Overriding for a single command still works:

Terminal window
AWS_PROFILE=devadmin@ajo-mark-dev terraform plan

readonly@ajo-mark-dev is the right choice for a default: the worst a forgotten --profile can then do is read from dev.

Exporting AWS_PROFILE from your shell startup file does the same job in one line, and without repeating the account settings. The tradeoff is reach — it only applies to processes started from that shell, while ~/.aws/config is also what IDEs and other tools read.

SSO replaces the old two-hop setup entirely, so nothing in it needs to survive — see what SSO replaced if the names below are unfamiliar.

Delete the access key in IAM first. Sign in at https://alma-iam.signin.aws.amazon.com/console/, then IAM → Users → your firstname.lastname user → Security credentials, and delete the access key. Deleting your local copy only hides the key; until you remove it here it still works from anywhere, for anyone who has it.

Then clear the local entries in ~/.aws/credentials:

  • The alma-iam key is usually the [default] block — aws_access_key_id, aws_secret_access_key, and an MFA device ARN pointing at 142020163350.
  • Any block with an aws_session_token holds temporary credentials, typically an aws-mfa leftover. They expire within hours, so an old one grants nothing — the get-caller-identity check below returns ExpiredToken and you can delete it.
  • If the file ends up empty, delete it.

Check what a block is before you delete it, rather than assuming:

Terminal window
aws sts get-caller-identity --profile <block-name>

An account that is not one of the twelve is not part of this migration, and SSO will not give you access to it. Find out what it is for before removing the only credential you have for it.

A leftover [default] in ~/.aws/credentials is a classic source of “why am I in the wrong account”, because it answers for every command that names no profile. With AWS CLI v2, a [default] profile in ~/.aws/config that uses sso_session does take precedence over static keys in ~/.aws/credentials — but if you have no [default] in your config, those static keys are simply what gets used.

Some repos still carry pre-SSO setup instructions, and so does Confluence. Ignore them: what SSO replaced describes the old way and why none of it applies.

The clearest example in a repo is infra. Its README.md asks you to fill aws_access_key and aws_secret_key into deployment/terraform.tfvars, and pins AWS CLI 1.x. Ignore those instructions. We no longer issue static access keys. The [sso-session] block this page uses, and aws sso login --sso-session, both require AWS CLI v2.

What to do instead when you work in one of those repos:

Terminal window
aws sso login --sso-session alma-sso
export AWS_PROFILE=devadmin@<target-account>

Terraform, the AWS SDKs and the bin/ helper scripts (deploy-terraform, param, ecs-run-task) all read AWS_PROFILE, so nothing else needs changing. Leave the two key lines out of your local terraform.tfvars. If the file holds other variables, keep it and omit just those two. Don’t commit a modified terraform.tfvars — it is gitignored for a reason.

For unattended builds, use the per-account GitHub OIDC role rather than any personal profile. CI must not depend on a human’s SSO session.

Symptom Cause / fix
Error loading SSO Token, ExpiredToken Session expired: aws sso login --sso-session alma-sso
The config profile (...) could not be found Typo, or ~/.aws/config is missing the [sso-session alma-sso] block. Re-run the generator.
An account is missing from the portal Refresh https://alma-sso.awsapps.com/start/#/?tab=accounts. Still missing → you are not a member of the group yet. Being an owner of a group grants no access on its own.
Just got added, still nothing Group membership sync takes 1–3 hours.
AccessDenied with a devadmin@ prod profile Expected unless you are in the prod write group.
Resources “not found” that clearly exist Wrong region — every profile should be eu-west-1.
sso-session not recognised, or aws sso login --sso-session fails AWS CLI v1. The [sso-session] block needs v2 — install it.
found the start marker but not the end marker A previous edit truncated the generated block in ~/.aws/config. Delete the partial block and re-run.
A command with no --profile lands in the wrong account Check echo $AWS_PROFILE first — an export left over from an earlier task overrides [default]. Otherwise ~/.aws/credentials still has a [default] with static keys and your config has no [default] at all. See step 5 and Make one profile the default.
--default-profile '...' is not one of the profiles being written Typo in the profile name, or --roles / --environment filtered that account out of this run. The error lists the valid names.