Step-by-Step Guide to Setting OIDC With Terraform for GitHub Actions Workflows with AWS
Your GitHub Actions Secrets Are the Weakest Link in Your AWS Security Chain
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.
Using long-term secrets can be a security nightmare that could expose your cloud account to a possible security incident.
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 to store long-lived access keys. Establishing a trusting relationship with temporary credentials significantly enhances security while simplifying the authentication process.
The better way to authenticate GitHub Actions 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 the 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.
-
Create aproviders.tf file and paste the below code into it. This will configure the Terraform version and AWS provider.
-
Create the files,variables.tf, output.tf, main.tf, iam.tfand paste the below code into them.
-
Create a terraform.tfvars file to create a map of repositories and branches.
-
Deploy the Terraform code, it will return the Role ARN that we can use with GitHub Action to authenticate with AWS.
-
This will create an IAM role to use with GitHub Action, IAM policies with permissions 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 Terraform 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 are authenticated with AWS using the OIDC, build the Docker images, and push to ECR.
This is all for this blog post.
Let’s connect
- Connect with me on Linkedin
**
**- Connect with me on twitter (@livingdevops)