GitHub Actions to AWS with OIDC: no more long-lived keys
Federate your workflows into AWS with short-lived tokens scoped to repo and branch. Delete the access keys after.
A long-lived AWS_ACCESS_KEY_ID in your repo secrets is a credential that never expires, works from anywhere on earth, and leaks the first time someone echos the wrong variable. OIDC federation deletes it: GitHub mints a short-lived token per run, AWS trades it for temporary credentials, and there's nothing left in the repo to steal.
A role that only your repo can assume
Register GitHub's OIDC provider once, then write a trust policy on the IAM role. The critical line is the sub condition — it pins which repo and which branch may assume the role. Get this wrong and any fork can assume it.
{"Effect": "Allow","Principal": {"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"},"Action": "sts:AssumeRoleWithWebIdentity","Condition": {"StringEquals": {"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"},"StringLike": {"token.actions.githubusercontent.com:sub": "repo:myorg/myapp:ref:refs/heads/main"}}}
The workflow side
Grant the job id-token: write — without it GitHub won't issue the OIDC token — then let the official action do the STS exchange. No aws-access-key-id, no aws-secret-access-key.
permissions:id-token: write # required to request the OIDC tokencontents: readjobs:deploy:runs-on: ubuntu-lateststeps:- uses: aws-actions/configure-aws-credentials@v4with:role-to-assume: arn:aws:iam::123456789012:role/gha-deployaws-region: us-east-1- run: aws s3 sync ./dist s3://my-artifacts
Inside the job the runner is now a first-class IAM principal — a temporary assumed-role session that expires in an hour:
aws sts get-caller-identityusing web identity token from GitHub OIDC ... { "Account": "123456789012", "Arn": "arn:aws:sts::...:assumed-role/gha-deploy/GitHubActions"} session expires in 1h — nothing stored in the repoWhere this goes next
Once it works, delete the access keys — a federated role next to a live long-lived key is just the old attack surface with extra steps. The same pattern federates GitHub into GCP (Workload Identity) and Azure (federated credentials), and the sub claim can gate production behind a protected environment.