DEV Community

Jayrald Dionaldo
Jayrald Dionaldo

Posted on

Authenticating GitHub Actions to AWS Without Access Keys (OIDC)

While being mentored on GitHub Actions recently, my mentor told me to use OIDC to authenticate to AWS. I honestly had no idea you could authenticate without a static access key, so I dug deeper.

It turns out GitHub OIDC lets your workflows get short-lived AWS credentials for each run instead of storing long-lived access keys. It's more secure, and there are no credentials to rotate or manage.

Here's what I learned setting it up.

Prerequisites

  1. An AWS account with permission to create IAM roles and identity providers
  2. A GitHub repository with GitHub Actions enabled
  3. Basic familiarity with GitHub Actions workflows

What you need

  1. OIDC Provider: lets AWS trust GitHub's tokens
  2. IAM Role: the identity your workflow assumes
  3. Trust Policy: who can assume the role
  4. Permission Policy: what the role can do
  5. GitHub Actions workflow: assumes the role

Setup

1. Create the OIDC Provider

In the AWS Console, go to IAM → Identity providers → Add provider and fill in:

identityprovider

  • Provider type: OpenID Connect
  • Provider URL: https://token.actions.githubusercontent.com
  • Audience: sts.amazonaws.com

You only need to do this once per AWS account. Every role your workflows use can share it.

2. Create the IAM Role and Trust Policy

Go to IAM → Roles → Create role, choose Web identity, and select the provider you just created with the sts.amazonaws.com audience.

The trust policy controls which GitHub repo and branch can assume this role. It should look like this:

IAMrole

in custom trust policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>: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:<GITHUB_ORG>/<REPO_NAME>:ref:refs/heads/main"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Don't use a wildcard like repo:* in the sub condition. That would let workflows from other repos assume your role.

3. Attach a Permission Policy

Attach a policy with only the permissions your workflow needs. For example, to let it list and upload files to one S3 bucket:

Permission policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::<BUCKET_NAME>"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

After creating the role, open it in IAM → Roles and copy the ARN from the Summary section. It looks like arn:aws:iam::123456789012:role/github-actions-role.

4. Set Up the GitHub Actions Workflow

Create .github/workflows/aws-oidc.yml:

name: AWS OIDC Demo

on:
  push:
    branches: [main]

permissions:
  id-token: write   # required to request the OIDC token
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>  # your Role ARN
          aws-region: ap-southeast-1

      - name: Check identity
        run: aws sts get-caller-identity
Enter fullscreen mode Exit fullscreen mode

Push to main, and if the get-caller-identity step shows your role's ARN, it worked. No access keys stored anywhere.

Wrapping up

Instead of storing access keys in GitHub secrets, your workflow now gets temporary credentials that expire after the run. Less to leak, nothing to rotate.

Top comments (0)