Writing about Cloud, architecture, AWS and software engineering.

How to: Deploy Terraform to AWS with GitHub Actions authenticated with OpenID Connect

May 14, 2023
Source code: GitHub

In the past it was very common to use AWS credentials (access token and secret) in your GitHub actions pipeline. This poses a security risk because most of the time these AWS credentials are long-lived credentials with a lot of permissions. If these credentials get leaked or misused the damage done could be huge.

In my previous blog I wrote about leaked credentials and the events after the leak, read more about it here: What happens when you leak AWS credentials and how AWS minimizes the damage

In this blog I will show you how to set up the OpenID Connect role, set the permissions, create the GitHub actions workflow and deploy the Terraform code to AWS.

OpenID Connect

OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It allows third-party applications to verify the identity of end-users or in our case an AWS role.

AWS role

Within AWS, you have to create an identity providers for GitHub and a role for web identity or OIDC with the created identity provider attached to it.

Create the GitHub identity provider

Navigate to IAM > Identity providers and create a new provider. Select OpenID Connect and add the following:

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

Create the AWS role

Navigate to IAM > Roles and create a new role. Select Web Identity and choose the just created identity provider. Add the permissions you want to role to have, in this example we will use the AWS managed permission ReadOnlyAccess.

After the role has been created we are going to add the GitHub repo to the Trust relationships. After editing the trusted entities JSON should look something like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::12345678: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:tiborhercz/terraform-openid-connect-example:*"
                }
            }
        }
    ]
}

We have to manually add the sub repo part, this tells the role which repositories are allowed to assume this role. In this example we allow the repo tiborhercz/terraform-openid-connect-example to assume the role, and we added the * wildcard operator to allow every branch.

"StringLike": {
    "token.actions.githubusercontent.com:sub": "repo:tiborhercz/terraform-openid-connect-example:*"
}

This should be enough to allow us to assume the created role from our GitHub action and deploy the Terraform code to AWS.

GitHub Action

In the GitHub action we need a few things:

Assigning the right permissions to the job

For the OpenID Connect integration to work we have to add the following permissions to the job:

permissions:
  id-token: write # This is required for requesting the JWT
  contents: read  # This is required for actions/checkout

Add the configure-aws-credentials action

In the GitHub job add the configure-aws-credentials action and configure the role-to-assume and the AWS region.

- name: Configure aws credentials
  uses: aws-actions/configure-aws-credentials@v2
  with:
    role-to-assume: arn:aws:iam::12345678:role/github_action_readonly
    aws-region: eu-west-1

Add the Terraform steps

Add the steps to set up, init, format and deploy the Terraform code to your GitHub Action.

- name: Setup Terraform
  uses: hashicorp/setup-terraform@v2
  with:
    terraform_version: 1.4.6

- name: Terraform Init
  run: terraform init

- name: Terraform Format
  run: terraform fmt -check

- name: Terraform Plan
  run: terraform plan -input=false

  # On push to "main", deploy the Terraform infrastructure
- name: Terraform Apply
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  run: terraform apply -auto-approve -input=false

Complete GitHub Action

Here you will find the complete GitHub Action to deploy the Terraform code to AWS using OpenID Connect:

File name: .github/workflows/terraform.yml

name: 'Terraform'

on:
  push:
    branches: [ "main" ]
  pull_request:
  
permissions:
  id-token: write
  contents: read

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production
    defaults:
      run:
        shell: bash

    steps:
    - name: Configure aws credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        role-to-assume: arn:aws:iam::12345678:role/github_action_readonly
        aws-region: eu-west-1

    - name: Checkout
      uses: actions/checkout@v3

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2
      with:
        terraform_version: 1.4.6

    - name: Terraform Init
      run: terraform init

    - name: Terraform Format
      run: terraform fmt -check

    - name: Terraform Plan
      run: terraform plan -input=false

      # On push to "main", deploy the Terraform infrastructure
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve -input=false

Conclusion

By using OpenID Connect to authenticate with AWS you don’t have to use long-lived AWS credentials anymore removing the risk of the credentials getting leaked.

The complete GitHub example can be found here: https://github.com/tiborhercz/terraform-openid-connect-example