GitHub Actions to AWS OIDC Integration Setup

John  AjeraJohn Ajera
4 min read

GitHub Actions to AWS OIDC Integration Setup

Securely deploy to AWS from GitHub Actions using OpenID Connect (OIDC), eliminating the need for long-lived AWS credentials.


✅ Step 1: Create IAM Role for GitHub (via AWS CLI)

Run the following AWS CLI commands to manually bootstrap the IAM role and OIDC provider:

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1

aws iam create-role \
  --role-name github-oidc-role \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name github-oidc-role \
  --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

Note: Replace AdministratorAccess with a least-privilege policy if possible.


✅ Step 2: Folder Structure (Bootstrap Setup)

Minimal layout for bootstrapping (no remote backend):

my-terraform-project/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── main.tf
├── .gitignore
└── README.md

Example .gitignore:

.terraform/

Note: This is a bootstrap setup and does not use a remote backend. For production, it's recommended to configure a remote backend such as an S3 bucket.


✅ Step 3: Minimal Terraform Configuration (main.tf)

This main.tf contains only a minimal resource for testing. Terraform is not responsible for creating the IAM role in this bootstrap scenario.

provider "aws" {
  region = var.aws_region
}

variable "aws_region" {
  description = "AWS region"
  default     = "ap-southeast-2"
}

variable "aws_account_id" {
  description = "AWS Account ID"
}

variable "github_owner" {
  description = "GitHub organization or username"
}

variable "repo_name" {
  description = "GitHub repository name"
}

# Minimal test resource
data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "test_bucket" {
  bucket = "test-bucket-${data.aws_caller_identity.current.account_id}"
  acl    = "private"

  tags = {
    Name = "Test Bucket"
    Env  = "Bootstrap"
  }
}

✅ Step 4: Configure GitHub Secrets and Variables

Go to Repo → Settings → Secrets and variables → Actions, and set:

  • Secrets:
  • AWS_ACCOUNT_ID: Your 12-digit AWS account ID (e.g. 123456789012)

Variables:

  • AWS_REGION: AWS region to deploy resources (e.g. ap-southeast-2)
  • TERRAFORM_VERSION: Terraform version to use (e.g. 1.7.5)
  • OIDC_ROLE_NAME: IAM role name that GitHub Actions will assume (must match the name in main.tf, e.g. github-oidc-role)

✅ Step 5: GitHub Actions Workflows

🔹 plan.yml – for Terraform Plan (on PR)

name: Terraform Plan

permissions:
  id-token: write
  contents: read

concurrency:
  group: terraform-plan-${{ github.head_ref || github.ref }}
  cancel-in-progress: true

on:
  pull_request:
    branches: [main]

env:
  AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
  AWS_REGION: ${{ vars.AWS_REGION }}
  TERRAFORM_VERSION: ${{ vars.TERRAFORM_VERSION }}
  OIDC_ROLE_NAME: ${{ vars.OIDC_ROLE_NAME }}

jobs:
  terraform:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.OIDC_ROLE_NAME }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TERRAFORM_VERSION }}

      - name: Terraform Init
        run: terraform init

      - name: Terraform Format
        run: terraform fmt -check
        continue-on-error: true

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: terraform plan

🔹 deploy.yml – for Terraform Apply

name: Terraform Apply

permissions:
  id-token: write
  contents: write

concurrency:
  group: terraform-apply-${{ github.head_ref || github.ref }}
  cancel-in-progress: true

on:
  push:
    branches: [main]

env:
  AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
  AWS_REGION: ${{ vars.AWS_REGION }}
  TERRAFORM_VERSION: ${{ vars.TERRAFORM_VERSION }}
  OIDC_ROLE_NAME: ${{ vars.OIDC_ROLE_NAME }}

jobs:
  terraform:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.OIDC_ROLE_NAME }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TERRAFORM_VERSION }}

      - name: Terraform Init
        run: terraform init

      - name: Terraform Format
        run: terraform fmt -check
        continue-on-error: true

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: terraform plan

      - name: Terraform Apply
        id: apply
        run: terraform apply -auto-approve
        continue-on-error: true

      - name: Commit and force push if changed
        if: always()
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          if ! git diff --quiet || ! git diff --staged --quiet; then
            git add .
            git commit -m "Update terraform state and lock file"
            git push --force
          fi

      - name: Fail if Terraform Apply errored
        if: steps.apply.outcome == 'failure'
        run: exit 1

✅ Summary

ComponentStatusNotes
OIDC ProviderOne-time setup per AWS account
IAM RoleDefined inline in main.tf
Policy PermissionsStart with AdministratorAccess, scope down later
GitHub WorkflowUses configure-aws-credentials for OIDC login
No Secrets RequiredSecure, short-lived tokens – no static credentials

You’re now ready to securely run terraform apply from GitHub Actions using short-lived OIDC credentials with a minimal bootstrap setup.

0
Subscribe to my newsletter

Read articles from John Ajera directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

John  Ajera
John Ajera