STS and Role Assumption
AWS Security Token Service (STS) hands out temporary credentials in exchange for proof of identity. Every credential used in the platform comes from STS, whether the caller is a person who signed in or a pipeline running in GitHub.
What it does
STS has a few operations that matter here. AssumeRole swaps one set of credentials for a session in another role, possibly in another account. AssumeRoleWithWebIdentity does the same for a caller that presents a signed token from an external identity provider, such as GitHub. TagSession attaches tags to the session. Every session expires: a role session lasts one to twelve hours, and nothing can extend it. Because credentials are temporary, there is nothing to rotate and nothing to leak for long.
Role chaining is assuming a role from a session that was itself obtained by assuming a role. The platform uses one hop of chaining for pipelines.
How BuiltForProd uses it
Two entry points lead into the accounts. People sign in through IAM Identity Center, which creates a role session for them. Pipelines start from a GitHub Actions job:
- The job presents its OIDC token to STS. The
acme-<repo>-deployerrole incore-autotrusts the GitHub provider only for its own repository, for the audiencests.amazonaws.comand for the subjects the delivered workflows present: themainbranch, a pull request, or a declared GitHub Environment (see GitHub OIDC). - The deployer role assumes
acme-terraform-deployerin the target account. That role, created in every account bymodules/account-baseline, trusts onlycore-autoprincipals whose ARN matchesacme-*-deployer, and allowssts:AssumeRoleandsts:TagSession. - State reads and writes go through
acme-terraform-state-accessincore-root.
root.hcl generates the provider assume_role block for every unit from org_accounts.hcl, so no unit names an account id or a role. The landing-zone role may assume acme-terraform-* in any account; the blueprint roles only in plat-* accounts plus the state-access role.
Other STS callers follow the same pattern with their own roles: the secrets repository workflow assumes acme-secrets-syncer in core-security, then the target account's deployer role, to write parameters; the ACM certificate unit assumes acme-route53-cross-account in core-dns to write validation records; the private-zone units assume it in core-network. The guardrails SCP requires MFA for any principal that is not an SSO session or a tagged automation role, which is why each of these roles carries mfa-exempt = true.
Terms you will see
| Term | Meaning |
|---|---|
| Temporary credentials | An access key, secret key and session token that expire together. |
AssumeRole | The STS call that opens a session in a role. |
AssumeRoleWithWebIdentity | The STS call that opens a session from an external token, such as GitHub's. |
| Role chaining | Assuming a role from a session that came from another role. |
| Session tag | A key-value pair attached to a session with TagSession. |
Subject (sub) claim | The field in the OIDC token that names the repository and ref. |
Audience (aud) claim | The intended recipient of the token; sts.amazonaws.com here. |
Where to read more
- AWS Baseline overview for the accounts the chain reaches.
- IAM for every role named above.
- Terragrunt for how
root.hclgenerates the provider configuration.