Use AWS IAM role in Github Actions

3 minute read

This post will describe how to use AWS’s OIDC identity provider to give Github action workflows access to AWS resources. I’ll use Terraform to set this up on AWS and an example GHA workflow to build and push a Docker image.

Providers/Versions

providers.tf

 1locals {
 2  env    = "sandbox"
 3  region = "us-east-1"
 4}
 5
 6provider "aws" {
 7  region = local.region
 8  default_tags {
 9    tags = {
10      env       = local.env
11      terraform = true
12    }
13  }
14}

versions.tf

1terraform {
2  required_providers {
3    aws = {
4      source  = "hashicorp/aws"
5      version = "~> 5.0"
6    }
7  }
8  required_version = "~> 1.5.7"
9}

Module

Initialize the module where needed.

1module "github_actions" {
2  source = "../../modules/github_actions"
3  env    = local.env
4}

Module files

Here we’re creating the OpenID provider for the GHA url and automatically getting the thumbprint using the TLS data source. You will need to set your Github org in the assume role policy and you could be more specific by giving a specific repo and/or branch. This example is only for building images and pushing to ECR, so you may need to adjust it for your needs.

iam.tf

 1locals {
 2  irsa_oidc_provider_url = replace(aws_iam_openid_connect_provider.gha.arn, "/^(.*provider/)/", "")
 3  account_id             = data.aws_caller_identity.current.account_id
 4}
 5
 6resource "aws_iam_openid_connect_provider" "gha" {
 7  client_id_list  = ["sts.amazonaws.com"]
 8  thumbprint_list = [data.tls_certificate.gha.certificates[0].sha1_fingerprint]
 9  url             = "https://token.actions.githubusercontent.com"
10}
11
12data "aws_iam_policy_document" "assume_role" {
13  statement {
14    effect  = "Allow"
15    actions = ["sts:AssumeRoleWithWebIdentity"]
16
17    principals {
18      type        = "Federated"
19      identifiers = [aws_iam_openid_connect_provider.gha.arn]
20    }
21    condition {
22      test     = "StringLike"
23      variable = "${local.irsa_oidc_provider_url}:sub"
24      values   = ["repo:<github-org>/*"]
25    }
26    condition {
27      test     = "StringEquals"
28      variable = "${local.irsa_oidc_provider_url}:aud"
29      values   = ["sts.amazonaws.com"]
30    }
31  }
32}
33
34resource "aws_iam_role" "gha" {
35  name               = "github-actions-${var.env}"
36  assume_role_policy = data.aws_iam_policy_document.assume_role.json
37  managed_policy_arns = [
38    aws_iam_policy.gha.arn
39  ]
40}
41
42resource "aws_iam_policy" "gha" {
43  name        = "github-actions-${var.env}"
44  description = "Policy for Github actions"
45  policy      = data.aws_iam_policy_document.policy.json
46}
47
48data "aws_iam_policy_document" "policy" {
49  statement {
50    actions = [
51      "ecr:BatchCheckLayerAvailability",
52      "ecr:BatchGetImage",
53      "ecr:CompleteLayerUpload",
54      "ecr:GetDownloadUrlForLayer",
55      "ecr:InitiateLayerUpload",
56      "ecr:PutImage",
57      "ecr:UploadLayerPart"
58    ]
59    resources = [
60      "arn:aws:ecr:*:${data.aws_caller_identity.current.account_id}:repository/*"
61    ]
62  }
63  statement {
64    actions = [
65      "ecr:GetAuthorizationToken"
66    ]
67    resources = [
68      "*"
69    ]
70  }
71}

data.tf

1data "aws_caller_identity" "current" {}
2
3data "tls_certificate" "gha" {
4  url = "https://token.actions.githubusercontent.com"
5}

variables.tf

1variable "env" {
2  type = string
3}

Github Action Workflow

In this workflow example, we’re using the “configure-aws-credentials” provider to specify the role ARN to assume that was created earlier and the ECR login step will automatically used the credentials from the previous step.

 1name: Build and Push to ECR
 2on:
 3  push:
 4    branches: [ main ]
 5env:
 6  AWS_REGION: us-east-1
 7  ECR_REPOSITORY: image-app
 8  IMAGE_TAG: latest
 9permissions:
10      id-token: write
11      contents: read
12jobs:
13  build-and-push:
14    runs-on: ubuntu-latest
15    steps:
16    - name: Checkout code
17      uses: actions/checkout@v3
18    - name: configure aws credentials
19      uses: aws-actions/configure-aws-credentials@v4
20      with:
21        role-to-assume: arn:aws:iam::111111111111:role/github-actions-sandbox
22        aws-region: ${{ env.AWS_REGION }}
23    - name: Login to Amazon ECR
24      id: login-ecr
25      uses: aws-actions/amazon-ecr-login@v2
26    - name: Build and push Docker image
27      uses: docker/build-push-action@v5
28      with:
29        context: .
30        push: true
31        tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}