Practical Linux, Windows Server and cloud guides for IT pros.

GitHub Actions AWS OIDC AccessDenied: Diagnose the Trust Policy

Find AWS OIDC failures in GitHub Actions: token permissions, audience, branch and environment subjects, immutable IDs and role policies.

Filed under

,

Published

Written by

Last updated

AWS OIDC diagnostic map: GitHub token issuance, AWS role assumption through audience and subject, then service authorisation.

An AWS AccessDenied in GitHub Actions can happen before the job obtains a role or after it has valid role credentials. Those failures need different fixes. Adding S3 permissions cannot repair a role trust-policy mismatch; changing trust cannot authorise an operation the assumed role is forbidden to perform.

TL;DR

  • Check token issuance, role assumption and service authorisation separately.
  • Grant id-token: write only to the job that needs federation.
  • Match the actual audience and subject, including immutable repository IDs where used.
  • Verify the assumed identity before attempting deployment.
SymptomCheck firstNext step
Cannot request an OIDC tokenJob permissionsCheck id-token: write
AssumeRoleWithWebIdentity deniedRole trust and issued claimsCompare exact audience and subject
Assumption works; service call deniedRole/service policyInspect the denied action and resource
Branch works; environment failsEnvironment subjectMatch the protected environment context

Start here: identify which of the three boundaries failed. The examples below are diagnostic templates, not permission to change a production role.

Documentation reviewed 8 September 2026. The dedicated repository/account lab, positive identity run and expected-denial runs remain outstanding. These examples have not been presented as a successful live deployment.

AWS OIDC diagnostic map: GitHub token issuance, AWS role assumption through audience and subject, then service authorisation.

Three boundaries: issue, assume, act

GitHub issues the job an OIDC token. AWS STS evaluates that token against an IAM role’s trust policy and, if accepted, supplies temporary role credentials. The target AWS service then evaluates the requested action. Keep the first failing step visible in the job log; a later cleanup failure can otherwise distract from the original authentication error.

The OIDC permission controls whether a job can obtain an identity token. It does not grant GitHub repository writes or AWS service writes. Give that permission to a narrow deployment or identity-check job, not every scanner and pull-request job in the repository.

Use an identity-only workflow before a deployment

In an authorised test repository, substitute your reviewed role ARN and region. This example deliberately has no checkout and no resource-changing command. The action is pinned to the verified upstream v6.2.4 release commit; inspect its source and release notes before adopting it.

name: aws-identity-check
on:
  workflow_dispatch:
permissions: {}
jobs:
  identity:
    runs-on: ubuntu-24.04
    permissions:
      id-token: write
    steps:
      - name: Obtain temporary AWS credentials
        uses: aws-actions/configure-aws-credentials@cbe3b392738ccf3f987d68400dafcf4b0624a56c # v6.2.4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-readonly-test
          aws-region: eu-west-1
          role-session-name: github-${{ github.run_id }}
          mask-aws-account-id: true
      - name: Verify the expected identity privately
        run: aws sts get-caller-identity --no-cli-pager

The account number above is a placeholder. Review identity output in access-controlled logs and redact it before sharing. The action’s account masking setting helps with the account ID; it is not a blanket guarantee that all organisational identifiers are hidden. Restrict the test role to the intended subject and do not grant deployment rights just to make the identity test useful.

Compare the trust policy with the token actually issued

Start with the role ARN used by the action. A correct policy on a similarly named role in another account will not help. Check the configured federated provider, sts:AssumeRoleWithWebIdentity action and condition values. For the ordinary AWS partition, the official action normally uses audience sts.amazonaws.com; other partitions or explicit overrides require their own matching configuration.

{
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "repo:example-org/example-repo:ref:refs/heads/main"
    }
  }
}

This is only a condition fragment, not a complete trust policy. It illustrates the older name-based branch subject. Do not copy it into a newly created repository without checking the current claim format. GitHub’s AWS OIDC guide documents the provider and audience setup.

New repositories can include immutable IDs in the subject

GitHub documents immutable owner and repository IDs in the subject for repositories created after 15 July 2026, and repositories that opt in. The feature is not available on GitHub Enterprise Server. A trust policy written for the older name-only format can therefore reject an otherwise correctly configured new repository.

Name-based branch example:
repo:example-org/example-repo:ref:refs/heads/main

Immutable-ID branch example:
repo:example-org@123456/example-repo@456789:ref:refs/heads/main

These IDs are illustrative, not values to copy. Compare your repository’s actual subject format with the trust condition. Custom subject templates are another reason not to infer the whole value from a repository URL. Inspect only the necessary claims through an approved diagnostic procedure; never print the complete bearer token. See the OIDC reference for claim construction and encoding.

An environment changes the subject context

A job that declares environment: production uses an environment context in its subject rather than the ordinary branch suffix. A name-based example is repo:example-org/example-repo:environment:production. Immutable-ID or customised prefixes still need to match the repository’s actual format. Do not keep a branch-only trust condition and assume the environment declaration adds a second independent match.

Protect the environment with the deployment-branch restrictions and review rules your workflow needs. Otherwise, trusting an environment name alone may authorise a broader set of runs than intended. Environment secrets and variables are related configuration, not the IAM trust policy; the environment configuration guide covers that separate layer.

After assumption, diagnose service permissions

If the identity step returns the expected role but the next command is denied, preserve that evidence and move to the service layer. Check the requested action, resource, region, identity policy and applicable organisation or resource policy. A permissions boundary or session restriction can limit access even when a policy contains an allow.

Do not replace a precise subject condition with a repository wildcard to fix a service denial. It changes who may obtain the role, not what the role can do. Likewise, a successful Terraform plan does not authorise every possible apply; inspect the plan output and approve changes separately.

Validate both success and refusal

A useful authorised lab has at least three results: the intended branch or environment can obtain the expected identity; a different subject is refused; and a service operation outside the role’s policy remains refused. Capture the run IDs and redacted errors. Then remove the test configuration through the agreed cleanup process.

The negative tests matter because a broad trust policy can make a happy-path run look healthy while admitting unintended jobs. Keep scanning jobs separate from deployment authentication; my practical DevSecOps rollout explains the scanning and release-artifact side. Completing those scans does not prove the deployment role is correctly scoped.

Leave a Reply

Your email address will not be published. Required fields are marked *

Find more on the site

Keep reading by topic.

If this post was useful, the fastest way to keep going is to pick the topic you work in most often.

Want another useful post?

Browse the latest posts, or support TurboGeek if the site saves you time regularly.