A new feature from GitHub now allows Federation with AWS accounts using Open ID Connect, which allows you to assume an IAM role within your account to deploy services into AWS.

This is a pretty big deal, since if you’re currently using GitHub Actions you’re probably storing credentials using GitHub secrets, which is great but can tend to lead to either long-lived credentials, or having to rotate keys on a regular basis depending on your security policies. And more often than anyone would like to admit, you might be using the same keys for everything, usually with God-like admin powers because someone couldn’t work out exactly what permissions were needed that one time at 3 in the morning on a Sunday.

Grab the code

Everything that I’ll be going through here, including code snippets and Terraform configuration files, can be cloned from this github repository if you’d like to test it out or use it as a base to improve on it.

If you are planning on running it yourself, you’ll need to change the following values:

  • 1234567890: This should be your AWS account number
  • github_account: Your GitHub account (user name)
  • github_repo: The name of your GitHub repository

Sidenote - Getting the Server Thumbprint

As an aside, I’m going to add this here as I wasn’t sure how to get the thumbprint when looking into it. If you want to skip this step, the server thumbprint is included in the code examples below and there’s no need to read this section.

The OIDC provider from Github can be found at https://token.actions.githubusercontent.com/, and as it turns out there is a simple way of querying the server to get the relevant details - you’ll need openssl, which we’ll use here, or a similar tool.

The first thing that we need to do is to append .well-known/openid-configuration to the above URL, giving us


Visiting that address will return a number of values - the one that you need is jwks_uri, which in this case is “jwks_uri”:”https://token.actions.githubusercontent.com/.well-known/jwks”.

You’ll need to remove the https:// prefix and everything after the top-level domain before using the URI in the following command:

openssl s_client -servername token.actions.githubusercontent.com -showcerts -connect token.actions.githubusercontent.com:443

Amongst other things, the command will return the server certificate as shown below (if there are more than one, use the final one that’s returned).


Copy the certificate to certificate.crt, and run the following command:

openssl x509 -in certificate.crt -fingerprint -noout

This returns the following,

SHA1 Fingerprint=A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E

which, once you remove all of the colon characters will give you the thumbprint that you need:


Terraform Code

The Terraform code that makes this possible is pretty straightforward. We need to create an assumable IAM role with the relevant permissions that you’ll require for deployments, and define the Open ID Connect Provider, which in this case is GitHub.

IAM Role

resource "aws_iam_role" "ExampleGithubOidcRole" {
  name = "ExampleGithubOidcRole"
  managed_policy_arns = [

  assume_role_policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "ExampleGithubOidcRole",
        "Effect": "Allow",
        "Principal": {"Federated": "arn:aws:iam::1234567890:oidc-provider/token.actions.githubusercontent.com"},
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {"StringLike": {"token.actions.githubusercontent.com:sub": "repo:github_account/github_repo:*"}}

As you can see, we’re attaching a policy to the role that will allow it to create a VPC. The other things to call out in this Terraform block are:

"Principal": {"Federated": "arn:aws:iam::1234567890:oidc-provider/token.actions.githubusercontent.com"},

where 1234567890 is your AWS account number, and

"Condition": {"StringLike": {"token.actions.githubusercontent.com:sub": "repo:github_account/repo_name:*"}}

where github_account and repo_name correspond to the repository in your GitHub account - in my example, this would be thetestlabs and aws-federation-with-github-actions respectively.

The “StringLike” condition is particularly important, as it ensures that the role can only be assumed from that particular repository rather than from any repository on GitHub.

The condition can be manipulated in a number of ways, for example for any repo in your GitHub account:

token.actions.githubusercontent.com:sub: repo:github_account/*

Or scoped to a specific branch:

token.actions.githubusercontent.com:sub: repo:github_account/*:ref:refs/heads/main

Identity Provider

The final resource that needs to be defined for Federation to work is the OIDC provider, and this is where we will use the thumbprint that we got from the server earlier on in this post.

resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"

  client_id_list = [

  thumbprint_list = [

So now that we have all of that defined in Terraform, and presuming that we’ve run hcl apply to deploy them, we can create a GitHub Action to deploy a service into our AWS account.

GitHub Action

Obviously, if you’re doing anything other than playing around with this functionality, you’ll want to configure all of the niceties around Terraform such as a remote backend and structuring your code, but we’re not going to cover those types of things in this post.

Since we have set the role up to have permission to deploy a VPC into the account, we’ll do that. The following is just lifted wholesale from the Terraform Registry, and we’re saving it in a subfolder called hcl_vpc_example.

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "my-vpc"
  cidr = ""

  azs             = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
  private_subnets = ["", "", ""]
  public_subnets  = ["", "", ""]

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform = "true"
    Environment = "dev"

Of course, we will also need to define a GitHub Action in order to make this all work. The workflow here looks like this:

name: Example

    runs-on: ubuntu-latest
      id-token: write
      contents: read

      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS
        run: |
          export AWS_ROLE_ARN=arn:aws:iam::1234567890:role/ExampleGithubOidcRole
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          export AWS_DEFAULT_REGION=aws_region


          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - name: Run Terraform
        run: |
          cd terraform_vpc_example/
          terraform init
          terraform plan
          terraform apply -auto-approve

That’s all the code that we need, so let’s see it in action. Now that we have a GitHub Workflow defined which runs on push events, all we need to do is commit a change to our repository and push it to the main branch to kick it off. And there we have it - deploying to AWS using GitHub Actions without the need for storing any credentials anywhere!