GitHub Actions OIDC in 2026: Passwordless AWS Deployments with Least Privilege

Hardcoded cloud keys in CI pipelines are still one of the fastest ways to turn a small leak into a major breach. In 2026, the safer baseline is short-lived credentials issued just-in-time through OpenID Connect (OIDC). In this guide, you will build a production-ready GitHub Actions to AWS deployment flow with zero long-lived secrets, branch-level trust boundaries, and auditable least-privilege IAM policies.

Why OIDC is the default in 2026

Traditional CI setups store AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in repository or org secrets. Even when encrypted at rest, these are static credentials with broad blast radius. OIDC replaces them with signed identity tokens from GitHub that AWS validates at assume-role time.

  • No long-lived AWS keys in GitHub secrets.
  • Credentials are short-lived and scoped to one workflow run.
  • IAM trust can be tied to repo, branch, environment, and workflow context.
  • CloudTrail captures role assumption events for cleaner audits.

Architecture overview

The flow is simple:

  1. GitHub Actions job requests an OIDC token.
  2. AWS IAM role trust policy validates token claims (aud, sub).
  3. AWS STS returns temporary credentials.
  4. Workflow deploys using those credentials and expires automatically.

Step 1: Create the GitHub OIDC provider in AWS

Run this once per AWS account (if not already configured):

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

Many accounts already have this provider. If the command says it exists, reuse it.

Step 2: Create a tightly scoped IAM role trust policy

Here is a trust policy that allows only one repository and one branch to assume the role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

Important claim patterns

  • repo:org/repo:ref:refs/heads/main for branch-based deployments.
  • repo:org/repo:environment:production for GitHub Environment-based controls.
  • Use StringLike only when you truly need wildcards.

Step 3: Attach least-privilege permissions

Create a role policy for only what deployment needs. Example for shipping a container to ECR and updating ECS service:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload",
        "ecr:PutImage"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecs:DescribeServices",
        "ecs:UpdateService"
      ],
      "Resource": [
        "arn:aws:ecs:ap-south-1:123456789012:service/prod-cluster/api-service"
      ]
    }
  ]
}

Avoid giving this role wildcard admin privileges. Keep one role per deployment target to reduce lateral movement risk.

Step 4: Configure GitHub Actions workflow

Add this workflow file at .github/workflows/deploy.yml:

name: Deploy API

on:
  push:
    branches: ["main"]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials from OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gha-prod-deploy
          aws-region: ap-south-1
          role-session-name: gha-prod-${{ github.run_id }}

      - name: Login to ECR
        run: |
          aws ecr get-login-password --region ap-south-1             | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-south-1.amazonaws.com

      - name: Build and push image
        run: |
          IMAGE=123456789012.dkr.ecr.ap-south-1.amazonaws.com/my-api:${{ github.sha }}
          docker build -t $IMAGE .
          docker push $IMAGE

      - name: Deploy service
        run: |
          aws ecs update-service             --cluster prod-cluster             --service api-service             --force-new-deployment

Step 5: Add production safety rails

OIDC removes key management pain, but safe delivery still needs guardrails:

  • Use GitHub Environments with required reviewers for production.
  • Pin third-party actions by commit SHA, not floating tags.
  • Protect main with required checks and signed commits if possible.
  • Set session duration low (for example, 15 to 60 minutes).
  • Enable CloudTrail alerts for unusual AssumeRoleWithWebIdentity events.

Troubleshooting common OIDC failures

1) “Not authorized to perform sts:AssumeRoleWithWebIdentity”

Your trust policy claim matching is likely wrong. Verify repo owner, repo name, and exact branch ref in the sub condition.

2) Workflow has no OIDC token

Ensure workflow permissions include id-token: write. Without that, token minting is blocked.

3) Wrong audience claim

For AWS, the expected audience is sts.amazonaws.com. Mismatch causes silent trust failures.

Hardening pattern for multi-account AWS

For serious environments, use one deployment account role that can assume narrowly scoped target roles in each environment account. This lets you centralize GitHub trust while preserving account isolation.

GitHub OIDC Role (Deploy Account) -> sts:AssumeRole -> Env Account Deploy Role

Keep explicit external IDs and resource tags where possible, and separate dev/staging/prod roles.

Final checklist

  1. OIDC provider exists in AWS account.
  2. Trust policy matches exact repository and branch/environment.
  3. Role policy is least privilege for deployment actions only.
  4. Workflow sets permissions.id-token: write.
  5. Production uses environment approval and branch protections.

Once this is in place, your pipeline is safer, cleaner, and easier to audit. In 2026, passwordless CI/CD is not an advanced option, it is the operational baseline every serious engineering team should adopt.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Privacy Policy · Contact · Sitemap

© 7Tech – Programming and Tech Tutorials