GitHub Actions OIDC with AWS: complete zero-credential setup
Stop storing AWS access keys as GitHub secrets. Here's the full setup.
Why OIDC wins
Old: AWS_ACCESS_KEY_ID stored forever, rotated manually (or never)
Risk: leaked key = full account access until manually revoked
OIDC: No stored credentials. Token per-job, expires when job ends.
Risk: compromised token expires in minutes
Audit: exact repo, branch, job logged in CloudTrail
Step 1: OIDC provider (Terraform)
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
Step 2: IAM role with trust policy
resource "aws_iam_role" "github_actions" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.github.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = { "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" }
StringLike = { "token.actions.githubusercontent.com:sub" =
"repo:myorg/my-service:ref:refs/heads/main" }
}
}]
})
}
Step 3: Workflow
jobs:
deploy:
permissions: { id-token: write, contents: read }
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-deploy
aws-region: us-east-1
# No access keys needed
Trust policy scoping
Repo + branch (recommended for prod):
"repo:myorg/my-service:ref:refs/heads/main"
Environment-scoped (best with approval gates):
"repo:myorg/my-service:environment:production"
Avoid:
"repo:myorg/*:*" ← too broad
Common errors
-
Not authorized: sts:AssumeRoleWithWebIdentity→ check thumbprint list is current -
Failed to assume role→ verifysubcondition exactly matches the token subject -
id-token: write not set→ add at job level, not just workflow level
Step2Dev provisions OIDC by default for every new project.
Top comments (0)