GitHub Actions + OIDC: Kill the Long-Lived Keys
Stop pasting AWS access keys into GitHub secrets. Let Actions assume an IAM role via OIDC and hand out short-lived credentials per run.
The single most common AWS credential leak isn't a clever hack — it's a long-lived AKIA... access key sitting in a CI secret, a .env, or a Slack message. Modern pipelines don't store AWS keys at all. GitHub Actions is an OIDC identity provider, and AWS IAM can trust it directly.
How the handshake works
- You register GitHub's OIDC provider (
token.actions.githubusercontent.com) as an IAM identity provider — once per account. - You create an IAM role whose trust policy allows only your repo (and ideally only certain branches or environments) to assume it.
- At runtime,
aws-actions/configure-aws-credentialsexchanges the job's signed OIDC token for temporary STS credentials that expire in an hour.
Why this is the senior default
- Nothing to rotate — there's no static secret to leak or expire.
- Scoped by claim — the trust policy's
subcondition pins access to a repo and ref, so a fork or a feature branch can't assume prod. - Auditable — CloudTrail shows exactly which workflow run assumed the role.
Example
# .github/workflows/deploy.yml
name: deploy-prod
on:
push:
branches: [main]
permissions:
id-token: write # REQUIRED for the OIDC token
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (no static keys)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-myapp-deploy
aws-region: us-east-1
- name: Deploy
run: |
aws sts get-caller-identity
aws ecs update-service --cluster myapp-prod --service web --force-new-deploymentWhen to use it
- A security team rotates all AWS access keys out of GitHub Secrets in one afternoon by replacing them with OIDC role assumptions, eliminating secret sprawl.
- A monorepo uses OIDC with separate IAM roles per service so a compromise of the checkout service workflow cannot touch the payment service's resources.
- An audit shows zero long-lived AWS credentials in any CI system after OIDC is adopted, passing the company's SOC 2 access review.
More examples
Full OIDC GitHub Actions workflow
Complete workflow that exchanges a GitHub OIDC token for short-lived AWS credentials and deploys to ECS without any stored secret.
name: Deploy
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-deploy-prod
aws-region: us-east-1
- run: aws ecs update-service --cluster prod --service myapp --force-new-deploymentScope OIDC role to specific branch
Restricts the IAM role's trust policy to only the main branch, preventing feature branches from assuming production credentials.
{
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:myorg/myapp:ref:refs/heads/main"
}
}
}Register GitHub OIDC provider
One-time setup: registers GitHub Actions as a trusted OIDC provider so IAM can validate the JWT tokens it issues.
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
echo 'GitHub OIDC provider registered'
Discussion