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:
| Subject | The jobs that present it |
|---|---|
ref:refs/heads/main | The apply and drift detection workflows, which run on main |
pull_request | The 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:
| Role | May assume | ECR push |
|---|---|---|
acme-aws-platform-baseline-deployer | acme-terraform-* in any account | no |
acme-aws-blueprint-webapp-infra-deployer, acme-aws-blueprint-etl-infra-deployer | acme-terraform-* in the platform accounts and the state-access role in the management account | no |
acme-aws-blueprint-webapp-code-deployer, acme-aws-blueprint-etl-code-deployer | acme-terraform-* in the platform accounts | yes, 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
| Term | Meaning |
|---|---|
| Identity provider | The IAM object that trusts GitHub's token issuer. |
sub claim | The token field naming the repository and the ref, pull request or environment. |
| GitHub Environment | The named deployment target whose branch policy and reviewers gate a job. |
| Deployer role | The per-repository role in the automation account a workflow assumes first. |
| Role chaining | Using one role's credentials to assume a second role in another account. |
| Web identity | An external identity, such as a GitHub token, exchanged for AWS credentials. |
Where to read more
- GitHub Actions for the workflows that use it
- STS and role assumption for the chain
- IAM for the roles and policies
- Least privilege for the reasoning