Step-by-Step Guide to Setting OIDC for GitHub Actions Workflows with AWS Using Terraform

Are you still using AWS access keys and secrets to authenticate your GitHub Actions with AWS in 2025?

Please don’t. Unless you want to wake up someday with 1000 GPU machines mining Bitcoin in your account at your expense, footing a million-dollar bill.

Your GitHub Actions Secrets Are the Weakest Link in Your AWS Security Chain

Using long-term secrets can be a security nightmare that could expose your cloud account to a possible security incident nightmare. You might be just one exposed GitHub secret away from an AWS billing catastrophe.

Then what should you do?

You should use OpenID Connect (OIDC), a modern, more secure way to authenticate your GitHub Actions workflows with AWS without storing long-lived credentials.

You can configure the OIDC to work with certain GitHub org and GitHub repos, or you can go more granular and allow it with certain branches, tag formats, etc.

What the heck is OIDC?

OIDC enables token-based authentication between GitHub Actions and AWS, eliminating the need for storing long-lived access keys. By establishing a trust relationship with temporary credentials, it significantly enhances security while simplifying the authentication process.

The better way to authenticate GitHub Action with AWS is with OpenID Connect (OIDC).

Setting up OIDC for your AWS account is very simple,

  • Step 1: Create an OIDC Identity Provider in AWS
  • Step 2: Create an IAM Role for GitHub Actions
  • Step 3: Attach Permissions your CICD workflow needs to the Role
  • Step 4: Update Your GitHub Actions Workflow to use the Identity provider arn instead of access keys and secrets

You can configure it from the AWS console, with AWS CLI commands or use Terraform to configure it.

I will use AWS CLI to configure the OIDC first and later with Terraform(with more flexible options).

Real-time implementation

Scenario

I have a 3-tier application running on an EKS cluster. I want to build and deploy new versions of applications using the GitHub Action workflow. For simplicity, I will run kubectl commands from the workflow to deploy the new version of the application.

Workflow

  • Look for the change in frontend/backend code and only build the images upon the code change and push them to their respective ECR repos.
  • Configure the kubectl and authenticate to the EKS cluster
  • Use the newly built images (for backend/frontend) and deploy them to the frontend/backend service.

Setting Up OIDC Between GitHub Actions and AWS: A Step-by-Step Guide

Create the file structure below

Run the below command to create the directory structure

touch configure-oidc-github.sh eks-policy.json trust-policy.json

Copy the code to their respective files.

  • trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::$AWS_ACCOUNT_ID:oidc-provider/$OIDC_PROVIDER"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"$OIDC_PROVIDER:sub": "repo:$GITHUB_ORG/$GITHUB_REPO:*"
}
}
}
]
}
  • eks-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"eks:DescribeCluster",
"eks:ListClusters"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "*"
}
]
}
  • configure-oidc-github.sh
#!/bin/bash
set -e

export OIDC_PROVIDER="token.actions.githubusercontent.com"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
export GITHUB_ORG="akhileshmishrabiz" # GitHub Org/Owner
export GITHUB_REPO="DevOpsDojo" # Repo you want to allow to use OIDC with AWS

# Create the OIDC provider
aws iam create-open-id-connect-provider \
--url https://$OIDC_PROVIDER \
--client-id-list sts.amazonaws.com \
--thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"

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


aws iam create-policy \
--policy-name GitHubActionsEKSPolicy \
--policy-document file://eks-policy.json

# Attach the policy to the role
aws iam attach-role-policy \
--role-name GitHubActionsEKSDeployRole \
--policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/GitHubActionsEKSPolicy

This script will configure the OIDC to allow my GitHub workflows in the repo https://github.com/akhileshmishrabiz/DevOpsDojo to use OIDC to authenticate with AWS. Let me explain the steps.

  • Setting the environment variable
export OIDC_PROVIDER=”token.actions.githubusercontent.com”
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity — query “Account” — output text)
export GITHUB_ORG=”akhileshmishrabiz”
export GITHUB_REPO=”DevOpsDojo”
  • Creating the OIDC IAM provider
aws iam create-open-id-connect-provider \
--url https://$OIDC_PROVIDER \
--client-id-list sts.amazonaws.com \
--thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"
  • Creating an IAM Role for GitHub Actions
aws iam create-role \
 --role-name GitHubActionsEKSDeployRole \
--assume-role-policy-document file://trust-policy.json
  • Creating an IAM policy for the GitHub Action role
aws iam create-policy \
--policy-name GitHubActionsEKSPolicy \
--policy-document file://eks-policy.json
  • Attaching IAM policy with the role
aws iam attach-role-policy \
--role-name GitHubActionsEKSDeployRole \
--policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/GitHubActionsEKSPolicy

Configuring the OIDC provider for GitHub Action with AWS CLI.

  • Install the AWS CLI if you haven’t already done
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
  • Configure the AWS credentials — with Access key and security key
aws configure
# This will as you access and secret key
  • Run the script to
chmod u+ x configure-oidc-github.sh
./configure-oidc-github.sh

Note: You can find the entire code for OIDC with AWS CLI here.

This will allow us to use the role arn:aws:iam::366140438193:role/GitHubActionsEKSDeployRole
to authenticate with the GitHub repo https://github.com/akhileshmishrabiz/DevOpsDojo


Terraform implementation

In a production environment, we mostly use Terraform to deploy the OIDC provider for GitHub Action.

Let me show how you can set up the OIDC provider with the help of Terraform and how to allow more than one repository.

  1. Create aproviders.tf file and paste the below code into it. This will configure the Terraform version and AWS provider.
  2. Create the files,variables.tf, output.tf, main.tf, iam.tfand paste the below code into them.
  3. Create a terraform.tfvars file to create a map of repositories and branches.
  4. Deploy the Terraform code, it will return the Role ARN that we can use with GitHub Action to authenticate with AWS.
  5. This will create an IAM role to use with GitHub Action, IAM policies with permission you want your GitHub Action workflow to have, and attach them to the role. IAM roles’s trust policy will allow the particular repositories and branches to use this role to authenticate with AWS.
  • providers.tf
terraform {
required_version = "1.8.1"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "ap-south-1"
}
  • variables.tf
# Variables
variable "aws_region" {
description = "AWS region"
type = string
default = "ap-south-1"
}

variable "github_repositories" {
description = "List of GitHub repositories to grant access to"
type = list(object({
org = string
repo = string
branch = optional(string, "*")
}))
default = [
{
org = "akhileshmishrabiz"
repo = "DevOpsDojo"
branch = "*"
}
]
}
  • main.tf
# OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"

client_id_list = [
"sts.amazonaws.com"
]

thumbprint_list = [
"6938fd4d98bab03faadb97b34396831e3780aea1"
]

tags = {
Name = "GitHub-Actions-OIDC-Provider"
}
}

# IAM Role
resource "aws_iam_role" "github_actions_eks_deploy_role" {
name = "GitHubActionsEKSDeployRole"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = [
for repo in var.github_repositories :
"repo:${repo.org}/${repo.repo}:${repo.branch}"
]
}
}
}
]
})

tags = {
Name = "GitHub-Actions-EKS-Deploy-Role"
}
}
  • iam.tf
# IAM Policy
resource "aws_iam_policy" "github_actions_eks_policy" {
name = "GitHubActionsEKSPolicy"
description = "Policy for GitHub Actions to access EKS and ECR"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"eks:DescribeCluster",
"eks:ListClusters"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
]
Resource = "*"
}
]
})

tags = {
Name = "GitHub-Actions-EKS-Policy"
}
}

# Policy Attachment
resource "aws_iam_role_policy_attachment" "github_actions_eks_policy_attachment" {
role = aws_iam_role.github_actions_eks_deploy_role.name
policy_arn = aws_iam_policy.github_actions_eks_policy.arn
}
  • terraform.tfvars
github_repositories = [
{
org = "akhileshmishrabiz"
repo = "DevOpsDojo"
branch = "*" # All branches
},
{
org = "akhileshmishrabiz"
repo = "Devops-zero-to-hero"
branch = "main" # Only main branch
}
]

You can find the Terraform code to set up OIDC for Github Action here.

Apply the Terraform

This example uses the Terraform version 1.8.1 , make sure you have the same Terraform version installed. If you have some other version of Terraform installed, then update the providers.tf

If you want to use multiple versions of Terrafor,m then use tfenv CLI.

terraform init
terraform apply

This will allow us to use the role arn:aws:iam::366140438193:role/GitHubActionsEKSDeployRole
to authenticate with the GitHub repo https://github.com/akhileshmishrabiz/DevOpsDojo on all branches. and https://github.com/akhileshmishrabiz/Devops-zero-to-hero on the main branch.

Let’s use this OIDC provider in our GitHub Action workflow

Using the OIDC provider to authenticate GitHub Action workflow with AWS to push the docker images to ECR repositories.

For this example, I will use my public GitHub Repo https://github.com/akhileshmishrabiz/DevOpsDojo

This repo has a 3-tier app:
– Flask backend
– React frontend
– RDS Postgres database

I will use GitHub Action to build the backend and frontend images and push them to AWS ECR repositories. GitHub Action will use OIDC to authenticate with AWS.

Let me create 2 ECR repositories

# Create ECR repositories for frontend and backend
aws ecr create-repository --repository-name devopsdozo/frontend
aws ecr create-repository --repository-name devopsdozo/backend

I want to use the workflow to build the backend and frontend docker images and push them to ECR repositories.

If you look at the below GitHub Action workflow, you can see that I have used GitHubActionsEKSDeployRole instead of access and secret keys.
role-to-assume:arn:aws:iam::366140438193:role/GitHubActionsEKSDeployRole
instead of AWS access and security keys.

When GitHub sends an auth request to AWS, it verifies the trust policy attached to the role to check if it is allowed to authenticate the workflow for the specific GitHub repo, and branch. If it finds the conditions to be true, it will generate a short-term token that GH workflow uses to authenticate with AWS.

name: Build and Push Docker Images

on:
push:
branches: [ main ]
workflow_dispatch: # Enable manual triggering

env:
AWS_REGION: ap-south-1
ECR_REPOSITORY_FRONTEND: devopsdozo/frontend
ECR_REPOSITORY_BACKEND: devopsdozo/backend
IMAGE_TAG: latest

jobs:
build-and-push:
name: Build and Push Images
runs-on: ubuntu-latest

permissions:
id-token: write
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-region: ${{ env.AWS_REGION }}
role-to-assume: arn:aws:iam::366140438193:role/GitHubActionsEKSDeployRole

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: Build, tag, and push frontend image to Amazon ECR
working-directory: ./frontend
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY_FRONTEND:$IMAGE_TAG \
--platform=linux/amd64 .
docker push $ECR_REGISTRY/$ECR_REPOSITORY_FRONTEND:$IMAGE_TAG
echo "Frontend image pushed to ECR: $ECR_REGISTRY/$ECR_REPOSITORY_FRONTEND:$IMAGE_TAG"

- name: Build, tag, and push backend image to Amazon ECR
working-directory: ./backend
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY_BACKEND:$IMAGE_TAG \
--platform=linux/amd64 .
docker push $ECR_REGISTRY/$ECR_REPOSITORY_BACKEND:$IMAGE_TAG
echo "Backend image pushed to ECR: $ECR_REGISTRY/$ECR_REPOSITORY_BACKEND:$IMAGE_TAG"

- name: Summary
run: |
echo "### Docker Images Built and Pushed" >> $GITHUB_STEP_SUMMARY
echo "✅ Frontend: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY_FRONTEND }}:${{ env.IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY
echo "✅ Backend: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY_BACKEND }}:${{ env.IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY

Note: You can find a better version of this workflow here.

You can see from the screenshot above that GH workflows authenticated with AWS using the OIDC, built the docker images, and pushed to ECR.

Akhilesh Mishra

Akhilesh Mishra

I am Akhilesh Mishra, a self-taught Devops engineer with 11+ years working on private and public cloud (GCP & AWS)technologies.

I also mentor DevOps aspirants in their journey to devops by providing guided learning and Mentorship.

Topmate: https://topmate.io/akhilesh_mishra/