A deployment workflow needs to push a build artifact to AWS. The risky version is familiar: create a long-lived AWS credential, store it beside the repository workflow, and hope it never leaks, never gets over-scoped, and never outlives the pipeline that needed it.
GitHub Actions OpenID Connect is the cleaner pattern. GitHub says a workflow job can request a short-lived OIDC token from GitHub’s provider, then present that token to a cloud provider instead of keeping long-lived cloud credentials in GitHub. AWS then decides whether that job may assume a role by checking token claims such as audience and subject. The hard part is not enabling OIDC. The hard part is making the AWS trust policy narrow enough that the wrong workflow cannot borrow the deploy role.
On this page
- The identity is the workflow job, not the whole repository
- Create the AWS OIDC provider first
- Bind the role to audience and subject
- Treat wildcards as a production risk
- Check the July 2026 subject-claim change
- Do not rely on AWS custom OIDC claims
- Give the workflow OIDC permission only where needed
- Separate role trust from role permissions
- Run this review before merging the change
The identity is the workflow job, not the whole repository#
GitHub describes each OIDC token as a job-specific JSON Web Token. The token contains claims that identify the workflow context: repository, owner, ref, environment, triggering event, subject, and related fields. A cloud provider compares those claims with the role’s trust rules before it issues temporary credentials for the job.
That means the AWS role should not trust “GitHub” in a broad sense. It should trust one narrow GitHub Actions identity: a specific repository path, branch or protected environment, and AWS audience. This is the same identity-first idea behind build provenance. If you already verify release evidence, the related guide on GitHub artifact attestations applies the same question to artifacts: not only “is this signed?” but “which workflow signed it?”
Create the AWS OIDC provider first#
GitHub’s AWS OIDC guide says the IAM identity provider should use https://token.actions.githubusercontent.com as the provider URL. It also says to use sts.amazonaws.com as the audience when using the official AWS credentials action. Those two values are the foundation, not the full authorization model.
AWS’s IAM OIDC provider documentation adds the lower-level requirements: the provider URL must begin with HTTPS, should not contain a port number, and must expose OIDC discovery metadata plus a JSON Web Key Set endpoint. AWS also explains that after the provider exists, you create one or more IAM roles that external identities can assume.
Keep the two policy surfaces separate:
- the identity provider configuration tells AWS where GitHub’s tokens come from and which audience is accepted;
- the role trust policy decides which GitHub workflow identities may assume a particular AWS role.
The second surface is where most deployment mistakes happen.
Bind the role to audience and subject#
GitHub’s AWS guide says you must define at least one condition so untrusted repositories cannot request cloud access tokens for your resources. The same page notes that AWS IAM recommends evaluating GitHub’s OIDC subject condition in the role trust policy. In plain English, the role should check both “was this token meant for AWS STS?” and “does this token come from the exact workflow context I intended?”
A safe review sketch looks like this:
Trust decision for a deploy role
issuer: GitHub Actions OIDC provider
accepted audience: AWS STS
accepted subject: one repository branch or one protected environment
action allowed by trust: web-identity role assumption
The audience check prevents a token minted for a different relying party from being treated as an AWS STS token. The subject check narrows the workflow identity to one repository context. For a branch-bound deployment, GitHub documents a subject shape tied to a repository and branch ref. For an environment-bound deployment, GitHub documents a subject shape tied to a repository and environment name.
Environments deserve special care. GitHub’s AWS guide says that when environments are used in workflows or OIDC policies, GitHub recommends adding protection rules to the environment, such as branch or tag restrictions. Without those rules, an environment name in a trust policy may look stricter than the release process really is.
Treat wildcards as a production risk#
GitHub’s AWS documentation includes a wildcard example that allows any branch, pull request merge branch, or environment from one repository to assume a role. That can be valid for a low-risk read-only role, but it is a poor default for a production deploy role.
The problem is not that wildcards never work. The problem is that a broad repository wildcard says any matching workflow context in that repository is now close to the same cloud role. Test workflows, pull request workflows, release workflows, and emergency workflows often have different review standards. If they all match one deploy role, the weakest path becomes the cloud boundary.
Prefer separate roles for separate trust levels. A preview environment might allow a wider branch pattern and limited permissions. A production deployment should usually bind to a protected branch or protected environment, then pair that trust rule with a permissions policy that only allows the deployment task.
Check the July 2026 subject-claim change#
GitHub documents an important date-sensitive detail: repositories created after July 15, 2026 use an immutable default subject format that includes owner and repository IDs. GitHub also says repositories can opt in to immutable subject claims, and that this format is not available on GitHub Enterprise Server.
That matters during migrations. If you copy an older trust policy into a newer repository whose subject uses immutable IDs, AWS may reject the token because the subject no longer matches the older owner-and-name form. The reverse mistake is also possible when moving examples between GitHub.com and GitHub Enterprise Server.
The safe migration step is boring but important: confirm the subject format for the repository before you bind the AWS role. Do not infer it from an old blog post, a different repository, or a stale workflow comment.
Do not rely on AWS custom OIDC claims#
GitHub’s general OIDC documentation describes custom property claims that can support attribute-based access decisions in some cloud policy designs. The AWS-specific GitHub guide adds a limit that changes the design: support for custom claims for OIDC is unavailable in AWS.
For AWS trust policies, stay with the supported GitHub and AWS claim model documented for this integration. Build the rule around the issuer, AWS audience, subject, branch or environment context, and role permissions. If you need business-unit or data-classification policy, do not assume GitHub custom property claims will be usable in AWS just because they appear in the broader OIDC documentation.
Give the workflow OIDC permission only where needed#
GitHub’s AWS guide says the workflow or job needs the id-token permission at write scope so GitHub’s OIDC provider can create a JWT for the run. The same note says that permission does not grant write access to resources by itself. It lets the workflow request and use an OIDC token for an external exchange.
Still, keep that permission on the job that needs AWS access, not automatically on every job in a workflow. A build job that only compiles and runs tests should not need the same cloud identity path as a deploy job. Smaller permission boundaries make review easier because the trust policy, workflow permission, and AWS role permission all point to the same task.
Separate role trust from role permissions#
The trust policy answers “who may assume this role?” The attached permissions policies answer “what may this role do?” AWS’s OIDC role documentation describes this split: a role for an external identity has a trust policy plus permissions policies. Do not try to solve both problems with the subject string.
A narrow trust policy still needs narrow role permissions. For a static site upload, that might mean write access to one bucket prefix and no IAM mutation. For a container deployment, it might mean updating one service, not full account administration. If the old long-lived credential had excessive permissions, moving to OIDC shortens credential lifetime, but it does not automatically reduce the blast radius.
This also belongs next to repository hygiene. OIDC reduces the need to store cloud credentials in GitHub, but repositories can still expose tokens, URLs, and other sensitive values. The older guide on pre-commit scanning for leaked credentials is still useful for everything OIDC does not replace.
Run this review before merging the change#
Before replacing a stored AWS credential with OIDC, review the role and workflow as one unit:
- the IAM OIDC provider URL is GitHub’s Actions token issuer;
- the AWS audience is the STS audience documented by GitHub;
- the role trust policy checks a narrow subject value;
- the subject format matches the repository’s current GitHub OIDC behavior, including immutable subject claims where they apply;
- the OIDC token permission is present only on jobs that need to assume the AWS role;
- the role permissions are scoped to the deployment task, not copied from the old credential.
Pick one deployment role this week and inspect only those six points. If the trust policy uses a repository-wide wildcard, split the deploy path into a narrower branch or protected environment before deleting the old credential.
Leave a Reply