"category": "CI/CD federation" · job/github-actions-aws-oidc

Scoping a GitHub Actions trust policy in AWS

Which condition keys to use in an IAM role trust policy for GitHub Actions, which ones are immutable, and worked examples for a branch, an environment and a reusable workflow.

published updated 8 min readby Cloud Identity Security

payload.takeaways# checked against sources November 10, 2026
  • Always check aud and pin sub to a specific repository and context.
  • Add repository_id and repository_owner_id: they are immutable, names are not.
  • Prefer one role per environment, and protect that environment in GitHub.
  • IAM rejects new GitHub trust policies without a sub or job_workflow_ref condition, but older roles are not re-checked.

A role that GitHub Actions can assume has two policies. The permissions policy says what the role can do. The trust policy says which workflows may become the role. This guide is about the second one, where a loose condition lets other people’s workflows in.

What does a minimal safe trust policy look like?

For a deploy job that runs in a GitHub environment called production:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:octo-org/octo-repo:environment:production",
          "token.actions.githubusercontent.com:repository_owner_id": "123456",
          "token.actions.githubusercontent.com:repository_id": "456789"
        }
      }
    }
  ]
}

Each line does one job:

  • Principal.Federated names the GitHub OIDC provider in this account.
  • aud refuses tokens minted for some other service.
  • sub pins the repository and the job context.
  • repository_owner_id and repository_id pin the numeric IDs, which do not change on rename and are not reused.[AWS IAM User Guide]

If the repository emits the immutable sub format, write sub as repo:octo-org@123456/octo-repo@456789:environment:production. The ID keys can stay; they cost nothing.

Which condition keys can I use?

AWS exposes these GitHub claims as condition keys, all prefixed with token.actions.githubusercontent.com:.[AWS IAM User Guide]

Key Example value Stable across renames?
aud sts.amazonaws.com n/a
sub repo:octo-org/octo-repo:ref:refs/heads/main Only in the immutable format
repository_id 456789 Yes
repository_owner_id 123456 Yes
actor_id 1 Yes
repository octo-org/octo-repo No
ref refs/heads/main n/a
environment production n/a
job_workflow_ref octo-org/deploy/.github/workflows/deploy.yml@refs/heads/main No (contains names)
enterprise_id 345 Yes

AWS’s guidance is to use the immutable identifiers (repository_id, repository_owner_id, actor_id) because repository, organization and user names can change.[AWS IAM User Guide]

What does IAM enforce for me?

GitHub’s issuer is shared by every GitHub customer, so the issuer alone says nothing about whose job it is. AWS calls these shared providers and requires certain claims in their trust policies.[AWS IAM User Guide] For GitHub, IAM checks on create and update that the policy has a sub condition (or job_workflow_ref) whose value is not only a wildcard. Otherwise the call fails with MalformedPolicyDocument.[AWS IAM User Guide][AWS IAM User Guide]

That check has two limits:

  1. It runs only when the policy is created or changed. Existing trust policies are not re-evaluated until someone edits them.[AWS IAM User Guide]
  2. It accepts broad patterns such as repo:octo-org/*, which AWS documents as an example of organization-wide trust.[AWS IAM User Guide]

So an account can still hold roles with no sub condition, and new roles can still be organization-wide. Both are worth auditing.

How should I scope by branch, environment or workflow?

Goal Condition Notes
One branch sub = repo:ORG/REPO:ref:refs/heads/main Breaks if the job adds environment:
One environment sub = repo:ORG/REPO:environment:production Add protection rules in GitHub
Any tag StringLike sub = repo:ORG/REPO:ref:refs/tags/* Anyone who can push a tag can deploy
A central reusable workflow job_workflow_ref = ORG/deploy/.github/workflows/deploy.yml@refs/heads/main Plus repository_owner_id
Whole repository StringLike sub = repo:ORG/REPO:* Includes pull requests

AWS recommends environment protection rules with deployment branch restrictions when you trust an environment.[AWS IAM User Guide] That way GitHub enforces which branches may run the job, and the trust policy only has to name the environment.

Which mistakes come up most often?

  • StringEquals with a * in the value. It is compared literally and never matches.
  • ForAllValues:StringEquals in an Allow statement. It evaluates to true when the claim is missing, so a misspelled key can grant access. AWS’s action README warns against it.[AWS (GitHub repository)]
  • No aud check.
  • Name-only trust. A deleted organization name can be registered again by someone else, who then gets tokens with the same sub. See subject recycling.
  • One role shared by many repositories with repo:ORG/*, so every repository inherits the broadest permissions any of them needed.

Paste your policy into the OIDC trust checker to see which of these apply, then use its generated policy as a starting point.

Questions people ask

Is StringLike with repo:my-org/* acceptable?
IAM accepts it, and AWS shows it as an example. It lets every repository, branch and pull request in the organization assume the role, including repositories created later. AWS itself recommends narrowing to specific repositories or branches.
Can I use job_workflow_ref instead of sub?
IAM accepts either one as the required GitHub control. job_workflow_ref is set for jobs that call a reusable workflow, so it lets you trust a central deploy workflow instead of each calling repository. Keep the repository_owner_id check alongside it.
Which condition keys survive a repository rename?
repository_id, repository_owner_id and actor_id are numeric and stable. repository, repository_owner and the name-only sub format change when the repository or owner is renamed.

Sources

  1. Create a role for OpenID Connect federation (console) — AWS IAM User Guide.# official documentation · checked November 10, 2026 (full page text)
  2. IAM and AWS STS condition context keys — available keys for AWS OIDC federation — AWS IAM User Guide.# official documentation · checked November 10, 2026 (first 100,000 characters of the page (the GitHub and default OIDC sections were inside that range))
  3. Identity-provider controls for shared OIDC providers — AWS IAM User Guide.# official documentation · checked November 10, 2026 (full page text)
  4. OpenID Connect reference — GitHub Docs.# official documentation (issuer) · checked November 10, 2026 (full page text)
  5. Configuring OpenID Connect in Amazon Web Services — GitHub Docs.# official documentation (issuer) · checked November 10, 2026 (full page text)
  6. aws-actions/configure-aws-credentials (README) — AWS (GitHub repository).# official documentation (action maintainer) · checked November 10, 2026 (full README)

Update history

  • — First published.
3 jobs
  1. job/fix-not-authorized-assumerolewithwebidentity category Troubleshooting

    Fixing “Not authorized to perform sts:AssumeRoleWithWebIdentity” in GitHub Actions

    A checklist for the AWS OIDC error, ordered by how often each cause applies: sub format, job context, wildcards under StringEquals, audience, provider ARN and customized claims.

    · 7 min
  2. job/github-oidc-immutable-subject category CI/CD federation

    GitHub's immutable OIDC subject claims, fixed in AWS, Azure and Google Cloud

    Repositories created after July 15, 2026 send a new sub format with owner and repository IDs. What changed, how to tell which format you get, and the trust changes for each cloud.

    · 8 min
  3. job/oidc-subject-recycling category Risks

    Subject recycling: when a deleted GitHub name still unlocks your cloud role

    Trust rules that match names can be satisfied by whoever registers that name next. What the 2026 research found, what GitHub, GitLab and the clouds changed, and how to audit your own roles.

    · 7 min