Skip to content

The AWS SSO config generator

tools/aws-sso/generate-aws-config.py writes one AWS SSO profile per account and role into ~/.aws/config. To set your machine up for the first time, follow Configure AWS SSO for the CLI — this page is the machinery behind it.

The script has no third-party dependencies and needs Python 3.8 or newer.

Flag Effect
--stdout Print the profiles instead of writing them. Changes nothing on disk.
--check Compare the target file’s generated block with what would be written. Prints a verdict; exits 1 if it is stale or absent.
--roles ROLE [ROLE ...] Emit only these roles, matched case-insensitively — e.g. --roles ReadOnly.
--environment ENV Emit only accounts with this environment value: nonprod or prod.
--default-profile PROFILE Also write a [default] profile carrying the settings of PROFILE, e.g. --default-profile readonly@ajo-mark-dev.
--config PATH The file to update or check. Defaults to $AWS_CONFIG_FILE, otherwise ~/.aws/config.
--accounts PATH The account list to read. Defaults to accounts.json beside the script.
--no-backup Skip the .bak copy that is otherwise written before a non-empty file is changed.

--stdout and --check are mutually exclusive: printing and verifying are different questions, and the script rejects the pair rather than silently honouring one. Every other combination is valid, so a prod-free preview is:

Terminal window
./tools/aws-sso/generate-aws-config.py --stdout --environment nonprod

--help is the authoritative list.

Profiles are named <role>@<account>, with the role lowercased — readonly@ajo-mark-dev from role ReadOnly and account ajo-mark-dev. Each profile’s region comes from the account’s own region, falling back to default_region in accounts.json.

Output is wrapped in marker comments:

# >>> alma-mobility AWS SSO profiles (generated) >>>
...
# <<< alma-mobility AWS SSO profiles (generated) <<<

The script replaces only what is between those markers, so profiles you wrote yourself survive regeneration — as long as they live outside the block. Editing inside it is pointless; the next run overwrites your changes.

If the file contains the start marker but not the end marker, the script refuses to touch it rather than guess where the block ends. Delete the partial block by hand and re-run.

--default-profile appends a [default] section carrying the same four settings as the named profile:

# Commands with no --profile and no AWS_PROFILE use this - a copy of readonly@ajo-mark-dev.
[default]
sso_session = alma-sso
sso_account_id = 226023431967
sso_role_name = ReadOnly
region = eu-west-1

It is a copy rather than a reference, because the AWS config format has no way to point one profile at another without assuming a role.

The name is matched case-insensitively against the profiles this run would write, so --default-profile readonly@nettix --environment nonprod is an error rather than a silent no-op: the error lists the names that were available.

The [default] lives inside the managed block, which has one consequence worth knowing — it is part of what the block is, so a run without the flag removes it again, and --check reports a config generated with the flag as stale unless you pass the flag there too:

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

If you would rather not repeat the flag, put a hand-written [default] below the end marker instead. Anything outside the block is never touched.

Writes go to a temporary file that then replaces the target, so an interrupted run cannot leave a half-written config. The mode of the file being replaced is carried across; a file the script creates from scratch is 0600.

Unless --no-backup is given, the previous version is copied to <config>.bak before anything changes. A file that does not exist yet, or exists but is empty, is written without a backup — there is nothing in it to lose.

By default the script touches nothing but ~/.aws/config. In particular it leaves ~/.aws/credentials and ~/.aws/sso/cache/ alone. The cache directory is where aws sso login stores your access and refresh tokens — those are the real secrets, and the CLI creates them 0600.

Two different targets, and picking the wrong one is the easy mistake:

Terminal window
# Is my own machine's config stale? Reads ~/.aws/config.
./tools/aws-sso/generate-aws-config.py --check
# Is the committed copy-paste file stale? For pre-commit hooks and repo scripts.
./tools/aws-sso/generate-aws-config.py --check --config tools/aws-sso/aws-config

Bare --check inspects whoever runs it, which is not what a repository hook wants.

CI already guards the committed file on every pull request, by diffing --stdout output against tools/aws-sso/aws-config, so a stale artifact fails the build whether or not anyone runs the hook locally.

tools/aws-sso/accounts.json is the source of truth for account IDs, and the generator reads nothing else. Add or remove an account there — not in your local config, and not in a copy of the table on a wiki page.

Account IDs must be quoted strings. 084786648538 loses its leading zero if written as a number.

Each entry carries a name, an id, an environment (nonprod or prod), and the roles available in that account. An entry may also carry a region, which overrides default_region for that account alone; none currently does.

Four top-level keys sit alongside accounts, and the generator reads all four. This is where they live, so change them here rather than in a local config:

Key Is
sso_session The session name, alma-sso — what aws sso login --sso-session takes
sso_start_url The Identity Center portal URL
sso_region The region Identity Center itself runs in
default_region The region written into every profile

After changing the file, regenerate the committed copy-paste output or CI will fail:

Terminal window
./tools/aws-sso/generate-aws-config.py --stdout > tools/aws-sso/aws-config