Skip to content

Terraform Automation with GitHub Actions & AWS OIDC

Using AWS IAM OpenID Connect (OIDC) allows GitHub Actions workflows to assume IAM roles securely without storing long-lived AWS Access Keys or Secrets in GitHub repository settings.


Architecture

sequenceDiagram
    participant GH as GitHub Actions Workflow
    participant OIDC as GitHub OIDC Provider (token.actions.githubusercontent.com)
    participant STS as AWS STS (AssumeRoleWithWebIdentity)
    participant AWS as AWS IAM Role (Terraform Deployer)

    GH->>OIDC: Request JWT Token for Workflow Run
    OIDC-->>GH: Signed JSON Web Token (OIDC Token)
    GH->>STS: AssumeRoleWithWebIdentity (passes JWT)
    STS->>AWS: Validate Subject (repo:org/repo:ref) & Audience (sts.amazonaws.com)
    STS-->>GH: Temporary short-lived AWS Credentials (1 hour)

Step 1. Configure GitHub OIDC Provider in AWS IAM

  1. In the AWS Console → IAM → Identity Providers → Add Provider.
  2. Select OpenID Connect.
  3. Provider URL: https://token.actions.githubusercontent.com
  4. Audience: sts.amazonaws.com
  5. Click Get Thumbprint and Add Provider.

Step 2. Create IAM Role with OIDC Trust Policy

Create an IAM role with a trust policy restricting access to your specific GitHub repository and branch:

github-oidc-trust-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>: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:<GITHUB_ORG>/<GITHUB_REPO>:*"
        }
      }
    }
  ]
}

Attach policies granting permissions for Terraform state management (S3 bucket and DynamoDB lock table) and targeted infrastructure provisioning.


Step 3. GitHub Actions Workflow

Create .github/workflows/terraform.yml:

name: "Terraform Automation"

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

permissions:
  id-token: write # Required for requesting the OIDC JWT
  contents: read
  pull-requests: write

jobs:
  terraform:
    name: "Terraform Plan & Apply"
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::<ACCOUNT_ID>:role/github-actions-terraform-role
          role-session-name: githubactions-tf-${{ github.run_id }}
          aws-region: eu-west-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Format Check
        run: terraform fmt -check

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate -no-color

      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color
        if: github.event_name == 'pull_request'

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -input=false