Featured image of post GitHub Actions AWS Deploy using OIDC

GitHub Actions AWS Deploy using OIDC

Introduction

1
2
3
4
5
6
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v6
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ${{ env.AWS_REGION }}

What’s wrong with this step inside the GitHub Actions workflow? Yes, it’s not the best way to authenticate to AWS because AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY were used to configure the credentials. This means having to manage an IAM user with valid access keys. This means that these keys have to be rotated regularly. This means that someone has to copy and paste those static credentials into the secrets and make sure they are not leaked or compromised somewhere else.

What’s the best practice to authenticate to AWS in a pipeline you may ask? The answer is OpenID Connect (OIDC). OpenID Connect allows your workflows to exchange short-lived tokens directly from your cloud provider. Let’s see how to configure this in GitHub Actions and deploy to AWS using this method.

AWS IAM Identity Providers

Before allowing GitHub Actions to authenticate to AWS, we need to configure an OpenID Connect provider in AWS IAM. Inside the AWS IAM console, go to Identity Providers and create a new OpenID Connect provider. Use the following details for the configuration:

  • Provider URL: token.actions.githubusercontent.com
  • Audience: sts.amazonaws.com

After setting up the provider, we can create an IAM role forGitHub Actions to assume it.

AWS IAM Role and Trust Relationship Policy

create a new IAM role with the following trust policy that allows the OIDC provider to assume the role with web identity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "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:GitHubOrg/GitHubRepo:ref:refs/heads/*",
            "repo:GitHubOrg/GitHubRepo:ref:refs/tags/*"
          ]
        }
      }
    }
  ]
}

Let’s break down the trust relationship policy:

  • Federated Principal: the OIDC provider ARN
  • StringEquals Condition: the OIDC token audience (aws sts)
  • StringLike Condition: the GitHub repository and branch patterns that the role can be assumed from. Here we allow the role to be assumed from any branch or tag in the GitHubRepo repository.

We can use wildcards in the repository and branch patterns to allow multiple repositories and branches to assume the role. We can also define only specific branches or tags to allow only specific branches or tags to assume the role. For example:

Allow only the main branch:

1
2
3
"StringLike": {
    "token.actions.githubusercontent.com:sub": ""repo:GitHubOrg/GitHubRepo1:ref:refs/heads/main"
}

Allow any branch:

1
2
3
"StringLike": {
    "token.actions.githubusercontent.com:sub": ""repo:GitHubOrg/GitHubRepo1:*"
}

Allow only tags from multiple repositories:

1
2
3
4
5
6
7
"StringLike": {
    "token.actions.githubusercontent.com:sub": [
        "repo:GitHubOrg/GitHubRepo1:ref:refs/tags/*",
        "repo:GitHubOrg/GitHubRepo2:ref:refs/tags/*",
        "repo:GitHubOrg/GitHubRepo3:ref:refs/tags/*"
    ]
}

These fine-grained permissions should be used to restrict access to the role accordingly. After creating the role with the trust policy, we can attach the IAM policy that allows the role to do whatever we would like to do inside AWS such as write to S3 buckets, update ECS tasks, etc.

GitHub Actions Workflow

After setting up the IAM role and trust relationship policy, we can configure the GitHub Actions workflow to assume the role and deploy to AWS. Here is an example workflow that deploys a Hugo site to S3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
name: 'Deploy to S3'

on:
  workflow_dispatch:
    branches:
      - main

permissions:
  id-token: write
  contents: read

defaults:
  run:
    shell: bash

env:
  AWS_REGION: "eu-central-1"
  AWS_DEFAULT_REGION: "eu-central-1"
  AWS_ROLE_ARN: "arn:aws:iam::123456789012:role/gha-oidc-rolename"
  AWS_ROLE_SESSION_NAME: "GHA-OIDC-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}"
  AWS_S3_BUCKET_NAME: "my-awesome-hugo-website"

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:

      - name: Checkout
        uses: actions/checkout@v5

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: ${{ env.AWS_ROLE_ARN }}
          role-session-name: ${{ env.AWS_ROLE_SESSION_NAME }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Setup Hugo
        run: sudo apt-get update && sudo apt-get install hugo -y

      - name: Build Hugo site
        run: hugo --gc --minify
        working-directory: ./my-awesome-hugo-theme

      - name: Deploy to S3
        run: aws s3 sync --delete ./public/ s3://${{ env.AWS_S3_BUCKET_NAME }}/public/
        working-directory: ./my-awesome-hugo-theme

Let’s break down the workflow:

  • The id-token is used in combination with OpenID Connect. Setting the permissions to write is required in order to request an OpenID Connect JWT Token as described in the docs.
  • The configure-aws-credentials action is used to configure the AWS credentials using the OpenID Connect JWT Token. Notice that we are not using the aws-access-key-id and aws-secret-access-key inputs. We only specified the role ARN and session name.
  • The Setup Hugo step installs Hugo on the runner.
  • After building the Hugo site, the Deploy to S3 step deploys the site to S3 by copying over the generated public folder using the AWS CLI: aws s3 sync ....

Here’s a diagram of the OIDC workflow:

GHA OIDC Workflow

Conclusion

In this post, we saw how to configure GitHub Actions to authenticate to AWS using OpenID Connect. An example Hugo site deployment to S3 was used to demonstrate the workflow. I hope this post was helpful and you learned something new. Let’s stop using IAM users with static credentials inside CI/CD pipelines!

References: