Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026Batch 01 · Aarambh — AWS + Agentic AI starts 28 June 2026
All templates
CI/CD · Template

GitHub Actions — Keyless AWS Deploy via OIDC

Stop storing long-lived AWS access keys in GitHub. Use OIDC to assume an IAM role from your workflow — the modern, secure way.

GitHub ActionsAWS IAMOIDCTerraform
Updated 2026-05-21 7 min View on GitHub

OIDC means every workflow run gets a short-lived federated token instead of a static AWS key. If your repo is compromised, attackers get nothing useful. This template is the GH Actions + IAM trust policy + sample deploy job — everything you need.

1. Create the OIDC provider in AWS (one-time)

You only need ONE per AWS account, regardless of how many repos use it.

iam-oidc.tfhcl
resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}

2. IAM role that GitHub can assume

The trust policy locks who can assume this role: only workflows from one repo, optionally only from main branch / specific environment.

iam-role.tfhcl
data "aws_caller_identity" "current" {}

variable "repo" { default = "gangadharure/cloudadhar" } # OWNER/REPO

resource "aws_iam_role" "gha_deploy" {
  name = "github-actions-deploy"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Federated = aws_iam_openid_connect_provider.github.arn }
      Action    = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
        }
        StringLike = {
          # Lock to one repo. Tighten further with refs/heads/main or environment:prod
          "token.actions.githubusercontent.com:sub" = "repo:${var.repo}:*"
        }
      }
    }]
  })
}

# Attach managed/inline policies as needed:
resource "aws_iam_role_policy_attachment" "ecr" {
  role       = aws_iam_role.gha_deploy.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser"
}

output "gha_role_arn" { value = aws_iam_role.gha_deploy.arn }

3. The GitHub Actions workflow

Set `id-token: write` permissions, then use the official aws-actions/configure-aws-credentials action.

.github/workflows/deploy.ymlyaml
name: Build & Deploy

on:
  push:
    branches: [main]

permissions:
  id-token: write   # required for OIDC
  contents: read

env:
  AWS_REGION: ap-south-1
  ECR_REPO:   cloudadhar/api

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

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

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

      - name: Build & push image
        run: |
          IMAGE=${{ steps.ecr.outputs.registry }}/${{ env.ECR_REPO }}:${{ github.sha }}
          docker build -t $IMAGE .
          docker push $IMAGE
          echo "IMAGE=$IMAGE" >> $GITHUB_ENV

      - name: Trivy scan
        uses: aquasecurity/trivy-action@0.24.0
        with:
          image-ref: ${{ env.IMAGE }}
          severity: CRITICAL,HIGH
          exit-code: '1'

      - name: Deploy via Helm
        run: |
          aws eks update-kubeconfig --name prod-eks
          helm upgrade --install api ./chart \
            -n app -f chart/values-prod.yaml \
            --set image.tag=${{ github.sha }} \
            --wait --timeout 5m

4. Verify

Run the workflow. You should see `Successfully assumed role` in the AWS step — no keys involved. Check CloudTrail: events are tagged with the GitHub repo + ref.

Tighten further (production)

Replace `:*` with `:ref:refs/heads/main` to only allow main branch, or `:environment:production` if you use GitHub Environments with required reviewers. Add a `branch-protection` rule requiring approval for main.

iam-role.tf (snippet)hcl
"token.actions.githubusercontent.com:sub" = [
  "repo:gangadharure/cloudadhar:ref:refs/heads/main",
  "repo:gangadharure/cloudadhar:environment:production"
]

Want me to implement this in your environment?

Cloudadhar offers paid setup engagements: I bring the template above into your AWS account, wire it up to your CI/CD, and walk your team through it.