Skip to main content

GitHub OIDC

GitHub OIDC is the arrangement where a GitHub Actions job proves who it is to AWS with a signed token instead of an access key. AWS trusts GitHub's OpenID Connect provider, checks which repository the token came from, and issues short-lived credentials for one role. The platform's pipelines hold no AWS keys at all.

What it does

GitHub mints a JSON Web Token for each job that asks for one, with claims such as the repository and branch in sub. An IAM OIDC identity provider in the AWS account trusts GitHub's issuer, and an IAM role's trust policy allows sts:AssumeRoleWithWebIdentity when the claims match. The aws-actions/configure-aws-credentials step does the exchange and exports temporary credentials to the job.

How BuiltForProd uses it

The github-oidc unit in the automation account creates the identity provider for token.actions.githubusercontent.com and one deployer role per repository, named acme-<repository>-deployer. A trust policy names both the repository and the part of the sub claim after it, in the classic and the id-suffixed claim formats GitHub issues.

The accepted subjects are exactly what the delivered workflows present, and nothing else:

SubjectThe jobs that present it
ref:refs/heads/mainThe apply and drift detection workflows, which run on main
pull_requestThe plan workflows, which run on pull requests
environment:*Any job declaring a GitHub Environment, such as a per-stage plan or apply

A workflow on any other branch is refused. For the jobs that declare an Environment, branch control lives in that Environment's deployment branch policy rather than in this list, which is also where reviewer approval is required. A repository that releases from a tag overrides the list per repository to add ref:refs/tags/*.

Each role's permissions express its blast radius:

RoleMay assumeECR push
acme-aws-platform-baseline-deployeracme-terraform-* in any accountno
acme-aws-blueprint-webapp-infra-deployer, acme-aws-blueprint-etl-infra-deployeracme-terraform-* in the platform accounts and the state-access role in the management accountno
acme-aws-blueprint-webapp-code-deployer, acme-aws-blueprint-etl-code-deployeracme-terraform-* in the platform accountsyes, to that repository's ECR repository only

All roles may read and write the state bucket. From there a workflow chains into acme-terraform-deployer in the target account, a role the account-baseline unit creates in every account and which trusts only acme-*-deployer roles from the automation account. The infrastructure workflows do this chain with an explicit sts assume-role step per stage; the code workflows use the credentials action's role-chaining option.

The secrets repository is deliberately separate. The iam-oidc-secrets-syncer unit in the security account creates its own provider and the acme-secrets-syncer role, which can decrypt the SOPS keys, write Parameter Store paths under /acme/ in the platform accounts, and assume each platform account's deployer role. It trusts one subject per stage environment, environment:sandbox, dev, staging and prod, because every job in both secrets workflows declares a GitHub Environment and therefore presents environment:<name> rather than a branch ref or pull_request. Deployer roles carry the mfa-exempt tag so the organization's MFA policy does not block them.

Terms you will see

TermMeaning
Identity providerThe IAM object that trusts GitHub's token issuer.
sub claimThe token field naming the repository and the ref, pull request or environment.
GitHub EnvironmentThe named deployment target whose branch policy and reviewers gate a job.
Deployer roleThe per-repository role in the automation account a workflow assumes first.
Role chainingUsing one role's credentials to assume a second role in another account.
Web identityAn external identity, such as a GitHub token, exchanged for AWS credentials.

Where to read more