Use AWS IAM role in Github Actions
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 }}