Sitemap

Github Actions, CDK and OIDC

4 min readMay 17, 2023

Continuous Integration, delivery and deployment can all be wonderful tools for removing a lot of the manual effort in getting code from your machine and into the real world. We also know that storing long lived credentials is also a bad thing. By harnessing the power of OIDC and GitHub Actions in this post I’ll show you how to create the correct IAM Roles with the AWS CDK to set up the link between your AWS Account and GitHub Actions to allow you to build a CI/CD pipeline to easily ship your code.

The Octocat and AWS

The good news is that’s it’s really easy. The CDK contains the two L2 Constructs you need to put it all together. First we need to define the Identity Provider:

import * as iam from 'aws-cdk-lib/aws-iam';

const githubOidcProvider = new iam.OpenIdConnectProvider(this, `GithubOidcProvider`, {
url: 'https://token.actions.githubusercontent.com',
thumbprints: ['6938fd4d98bab03faadb97b34396831e3780aea1'],
clientIds: ['sts.amazonaws.com'],
});

Here we’re defining the URL that GitHub Actions uses to issue the tokens. If you want to check yourself you can find the /.well-known jwks and OpenID Configuration here. The Thumbprint is correct for the GitHub certificate at the time of writing, but I’d strongly recommend validating the correct Thumbprint following the AWS Docs, here, or using the IAM Console. Finally we’re creating the Client ID that we’ll be the subject when we GitHub Actions goes on to obtain some AWS credentials.
Next we need the IAM Role itself that GitHub Actions will assume when making API calls into your account:

const githubActionsDeploymentRole = new iam.Role(this, `GithubActionsDeploymentUser`, {
assumedBy: new iam.WebIdentityPrincipal(
githubOidcProvider.openIdConnectProviderArn,
{
StringLike: {
'token.actions.githubusercontent.com:sub': 'repo:ryancormack/*',
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
},
},
),
roleName: 'github-actions-deployment-role',
inlinePolicies: {
'github-actions-deployment-policy': new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:ListObjects'],
resources: ['arn:aws:s3:::my-bucket', 'arn:aws:s3:::my-bucket/*'],
})
]
})
},
});

The CDK contains the L2 Construct we need to make the Role as well as the Construct we need for the Web Identity Principal. Under the hood the WebIdentityPrincipal is going to make a Federated Principal for us, using the ARN of the OIDC Provider we just created. We’re then scoping this down to limit what users/tokens can actually assume this role. In this example I’m limiting it to all sub’s (or subjects) that are GitHub repo’s belonging to me. In this case my GitHub Account/Organisation is my personal one, ryancormack and then all branches and git refs inside there. I’m then defining a Role Name, which can be helpful when configuring the GitHub Action later, and giving it a specific inline policy. There are many AWS Managed Policies available, but as with most things, it’s often best to ensure you are adhering to the practice of least privilage and ensuring the Role can only do exactly what it needs to do.

You can then deploy this into your AWS Account.

The resulting Identity Provider in the AWS
The resulting IAM Role with permission set in the AWS Console
The resulting IAM Role and Trust Permission in the AWS Console

Once this is all deployed you’re ready to set up your GitHub Action. In your repo, you’ll need a folder called .github/ and a sub folder called workflows. Inside there you can create a file called ci-cd.yml:

name: AWS example workflow
on:
push:
branches: [ "main" ]
env:
AWS_REGION : "eu-west-1"

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

jobs:
Test:
runs-on: ubuntu-latest
steps:
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: 'arn:aws:iam::11111111:role/github-actions-deployment-role'
aws-region: 'eu-west-1'
- name: list s3 objects
run: |
aws s3 list-objects --bucket 'my-bucket' --region 'eu-west-1'

In this GitHub Actions Workflow, it will run on every commit to the ‘main’ branch. The Permissions block is better described in the GitHub Documentation. The first job that is running is using GitHub’s official Action for assuming an AWS Role via the OIDC trust we set up. The Role To Assume is the ARN of the AWS Role we created, and I’m adding in the region.
Then I’m just running a CLI command to demonstrate the runner has access to the Role and can perform AWS API calls.

In this post we’ve looked at how we can create an Identity Provider in AWS to allow GitHub Actions to assume IAM Roles via OIDC. This allows us to build full CI/CD pipelines in GitHub Actions to deploy your code into your AWS Account without needing any long lived AWS credentials 🎉

--

--

Ryan Cormack
Ryan Cormack

Written by Ryan Cormack

Serverless engineer and AWS Community Builder working on event driven AWS solutions

No responses yet