Deploy to AWS from GitHub Actions with OIDC (No Keys)

Khimananda Oli 6 min read Database
Deploy to AWS from GitHub Actions with OIDC (No Keys)

By Khimananda Oli | Last reviewed: August 2026

Managing long-lived access keys is a security liability that most teams can no longer afford. When you deploy to AWS from GitHub Actions with OIDC (No Keys), you replace static credentials with ephemeral, session-based tokens tied directly to your repository and branch. This approach eliminates key rotation headaches and significantly reduces the blast radius of compromised secrets. For teams building automated CI/CD pipelines, adopting OpenID Connect is now the baseline standard for production-grade infrastructure.

GitHub ActionsOIDC Token Issuertoken.actions.githubusercontent.comAWS STSAssumeRoleWithWebIdentityReturns Temp CredentialsIAM RoleTrust Policy + Permissions1. JWT Token ExchangeDeploy to AWS from GitHub Actions with OIDC (No Keys)
High-level flow of OIDC token exchange between GitHub Actions and AWS STS for keyless authentication.

How do you configure AWS IAM for GitHub Actions OIDC?

The foundation of this architecture is the IAM Identity Provider. Unlike traditional federation which requires complex SAML setup, GitHub’s OIDC endpoint is public and standardized. You must define this provider exactly once per AWS account. In practice, I manage this via Terraform to ensure the URL and thumbprint are immutable and version-controlled, aligning with infrastructure as code best practices.

Create the OIDC Identity Provider

You need to register GitHub’s token issuer with AWS. The thumbprint is critical; it validates that the token actually came from GitHub. As of 2026, the stable thumbprint remains consistent, but always verify against official documentation before applying.

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        = "GitHubActionsOIDC"
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

Define a Least-Privilege Trust Policy

This is where most security failures occur. Never use a wildcard (*) for the sub claim. Always scope access to a specific repository, environment, or branch. This ensures that even if another repo in your organization is compromised, it cannot assume this role.

data "aws_iam_policy_document" "github_assume_role" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRoleWithWebIdentity"]

    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github_actions.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values   = ["sts.amazonaws.com"]
    }

    # CRITICAL: Restrict to specific repo and environment
    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values   = ["repo:khimananda/my-app:environment:production"]
    }
  }
}

What is the correct GitHub Actions workflow syntax for OIDC?

Your workflow file must explicitly request the id-token: write permission. Without this, GitHub will not generate the OIDC token, and the authentication step will fail silently or throw a cryptic 403 error. This permission grants the workflow run the ability to mint a signed JWT for the current execution context.

Minimal Secure Workflow Configuration

Below is a battle-tested snippet. Note that we never reference AWS_ACCESS_KEY_ID. The configure-aws-credentials action handles the entire STS handshake internally when provided with a role ARN.

name: Deploy to AWS
on:
  push:
    branches: [main]

permissions:
  id-token: write   # REQUIRED for OIDC
  contents: read    # Needed to checkout code

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # Matches IAM trust policy condition
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
          aws-region: ap-south-1
          # Optional: Reduce session duration for tighter security
          role-duration-seconds: 900

      - name: Verify Identity
        run: aws sts get-caller-identity
GitHub RunnerGitHub OIDCAWS STSRequest JWT (id-token: write)Return Signed JWTAssumeRoleWithWebIdentity(JWT)Temp Access Key + Session TokenExecute AWS CLI
Step-by-step sequence of JWT issuance and temporary credential retrieval during a workflow run.

How does OIDC compare to long-lived access keys for CI/CD?

If you are still debating whether to migrate, consider the operational overhead. Long-lived keys require manual rotation, secure storage in secrets managers, and immediate revocation procedures upon staff departure. OIDC shifts this burden to cryptographic verification. For teams managing AWS IAM least privilege access, OIDC is not just safer; it is operationally superior.

CriteriaLong-Lived Access KeysOIDC Federation (Recommended)
Credential LifespanIndefinite (until rotated)Ephemeral (15m – 1hr default)
Rotation RequirementManual / Scheduled (90 days max)Automatic per workflow run
Blast RadiusHigh (keys often shared/reused)Low (scoped to repo/branch/env)
Audit TrailGeneric IAM user activitySpecific workflow run ID + commit SHA
Secret StorageRequired (GitHub Secrets/Vault)None (Zero secrets stored)
Compliance (SOC2/ISO)Harder to justifyPreferred control for automated access

How do you troubleshoot common OIDC authentication failures?

Even with perfect configuration, OIDC can fail due to subtle mismatches. In my experience supporting teams across Nepal and globally, 90% of issues stem from three specific areas. Debugging requires checking both the GitHub runner logs and AWS CloudTrail simultaneously.

  • Missing id-token: write Permission: This is the most frequent error. If your job uses a matrix strategy or reusable workflows, permissions defined at the top level do not automatically cascade. You must explicitly declare permissions in every job block that calls the AWS credential action.
  • Trust Policy Condition Mismatch: The sub claim is case-sensitive and format-specific. A common mistake is using repo:org/repo:* when the actual token contains ref:refs/heads/main. Use the GitHub OIDC debugger or inspect the raw token payload in a test step to see the exact claims being issued.
  • Environment Protection Rules: If your IAM role trusts environment:production, but your workflow job doesn't specify environment: production, authentication fails. The environment name in YAML must match the IAM condition string exactly.

Verify Token Claims Before Deploying

Add this diagnostic step temporarily to print the token claims without exposing sensitive data. It helps confirm what AWS will actually receive.

- name: Debug OIDC Claims (Safe)
  run: |
    curl -sS \
      -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
      "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" \
      | jq '.value | split(".")[1] | @base64d | fromjson' \
      | grep -E '"(sub|aud|repository)"'
Auth FailureIs id-token: write set?NOYESAdd PermissionCheck Sub Claim MatchEnvironment Name Exact Match?NOYESFix Env YAML / IAM CondCheck CloudTrail
Troubleshooting decision tree for resolving OIDC authentication failures in GitHub Actions workflows.

Next Steps for Secure AWS Deployments

Migrating to deploy to AWS from GitHub Actions with OIDC (No Keys) is a definitive step toward mature, audit-ready DevOps. Once implemented, rotate and delete all existing long-lived keys immediately. Pair this with centralized secrets management for application-level credentials that OIDC cannot address. If your team needs assistance designing compliant CI/CD architectures or hardening existing AWS environments, reach out to discuss your infrastructure. Security is not a feature you add later; it is the foundation everything else rests on.

Frequently Asked Questions

OpenID Connect allows GitHub Actions to request short-lived AWS credentials directly from STS without storing long-term access keys as repository secrets.

No, AWS STS AssumeRoleWithWebIdentity and GitHub OIDC provider endpoints are free. You only pay for the underlying AWS resources provisioned during your deployment workflow runs.

Set the Principal to token.actions.githubusercontent.com and add StringEquals conditions for repo and ref to restrict access to specific repositories and branches.

Yes, create separate IAM roles in each target account with trust policies pointing to the same GitHub OIDC identity provider ARN for cross-account deployments.

Add id-token: write permission at the job or workflow level to allow the runner to request the JWT token required for the STS credential exchange.

Static keys never expire and pose theft risks. OIDC issues temporary credentials valid only for the workflow run duration, eliminating secret rotation overhead.

Use aws-actions/configure-aws-credentials@v4 or later. This version automatically detects the OIDC token when role-to-assume is set without providing access keys.

Verify the IAM trust policy conditions match the exact repository owner, name, and branch. Check CloudTrail logs for the failed AssumeRoleWithWebIdentity event details.

Yes, add the environment claim to your IAM trust policy conditions. This ensures only workflows running in approved production or staging environments can assume the role.

Yes, self-hosted runners receive OIDC tokens identically to GitHub-hosted runners. Ensure network egress allows connectivity to both GitHub and AWS STS endpoints.

The default session duration is one hour. Configure longer durations in the IAM role maxSessionDuration or implement credential refreshing for extended deployment tasks.

Yes, but you must use the GovCloud-specific OIDC provider endpoint and ensure your IAM identity provider ARN references the correct partition and region.

Yes, CloudTrail logs capture the web identity subject including repository, branch, workflow name, and commit SHA for every AssumeRoleWithWebIdentity call made through OIDC.

It replaces IAM credentials only. You still need secrets for third-party APIs, database passwords, or any non-AWS service authentication within your deployment workflows.

Trust policies do not require rotation since they rely on cryptographic verification rather than shared secrets. Review and update conditions only when changing repository structure or access requirements.