DEV Community

Yash
Yash

Posted on

GitHub Actions OIDC with AWS: complete zero-credential setup

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
Enter fullscreen mode Exit fullscreen mode

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"]
}
Enter fullscreen mode Exit fullscreen mode

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" }
      }
    }]
  })
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Common errors

  • Not authorized: sts:AssumeRoleWithWebIdentity → check thumbprint list is current
  • Failed to assume role → verify sub condition 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.

👉 step2dev.com

Top comments (0)