GitHub Actions OIDC to AWS Is the Setup You Should Use When Long-Lived Cloud Keys in CI Are Still Making You Nervous
A practical guide to using GitHub Actions OIDC with AWS so CI can assume a role dynamically instead of storing long-lived cloud credentials as repository secrets.
Why this setup matters: long-lived cloud keys in CI are one of those practices teams keep around because they are familiar, not because they are good.
GitHub Actions supports OpenID Connect so workflows can request short-lived credentials from AWS instead of storing permanent access keys in repository secrets. That is one of the cleanest security upgrades many teams can make without rewriting their deployment pipeline from scratch.
The basic idea
Instead of storing AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, you:
- configure an IAM role in AWS
- trust GitHub’s OIDC provider
- let the workflow assume the role during execution
That means credentials are minted when needed and expire afterward.
This reduces the risk surface dramatically compared with static secrets that sit around waiting to be leaked, copied, or forgotten.
The GitHub workflow side
You need id-token: write permission:
permissions:
id-token: write
contents: readThen configure AWS credentials with the official action:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: us-east-1After that, AWS CLI commands can run with the assumed role.
Why this is better than secret-based keys
Static CI credentials create several problems:
- they live too long
- rotation gets neglected
- blast radius is larger
- multiple repos often share them carelessly
OIDC-based role assumption is better because it gives you:
- short-lived credentials
- tighter trust conditions
- better auditability
- less secret sprawl
This is one of those upgrades that improves both security and operational hygiene.
Be strict with trust policy conditions
The AWS trust policy should not be overly broad. Scope it by repository, branch, or environment where appropriate. That way, not every workflow in the universe can ask for your deploy role.
The exact policy details depend on your setup, but the principle is simple:
least privilege still matters, even when the credentials are temporary.
Keep your workflow trigger discipline tight too
Even with OIDC, do not let every random workflow path assume the deploy role. Be intentional about when the job runs:
on:
push:
branches:
- mainThen combine that with environment protection or branch rules where it makes sense. OIDC removes long-lived secrets, but it does not remove the need for discipline around who is allowed to trigger cloud-changing jobs.
A practical deploy step
Once credentials are configured, your deployment commands can stay pretty normal:
aws s3 sync dist/ s3://my-bucket
aws cloudfront create-invalidation --distribution-id ABC123 --paths "/*"The point is not to make deployment exotic. The point is to make authentication safer.
Verify identity during rollout changes
When migrating a pipeline from static keys to OIDC, add one harmless verification command first:
aws sts get-caller-identityThat immediately tells you whether the role assumption worked before you let the workflow touch S3, ECS, Lambda, or anything more expensive.
Final recommendation
If your GitHub Actions workflows still rely on long-lived AWS keys stored as secrets, OIDC is one of the cleanest upgrades available. Use temporary role assumption, lock trust policies down carefully, and let the workflow request only what it needs when it actually runs.