How to: Deploy Terraform to AWS with GitHub Actions authenticated with OpenID Connect
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
- The aws-actions/configure-aws-credentials action
- Terraform
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