Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

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

By Kokil Thapa | Last reviewed: August 2026

Storing long-term AWS access keys in repository secrets is a security liability that most teams accept out of habit rather than necessity. When you configure CI/CD pipelines for production systems, the standard approach of pasting AWS_ACCESS_KEY_ID into GitHub Secrets creates permanent credentials that can leak through logs, forks, or compromised workflows. The correct solution in 2026 is to deploy to AWS from GitHub Actions with OIDC (No Keys), replacing static secrets with ephemeral tokens issued directly by AWS STS based on cryptographic proof of workflow identity.

Why should you deploy to AWS from GitHub Actions with OIDC (No Keys) instead of access keys?

The fundamental problem with long-term IAM user credentials is their persistence. An access key generated today remains valid indefinitely until manually rotated or deleted. In my experience maintaining deployment infrastructure for legal-tech portals and eCommerce platforms, I have seen these keys accidentally committed to git history, exposed in pull request checks on public forks, or left active long after a developer departed. Even with strict secret scanning, the window between exposure and detection is often enough for automated scanners to find and abuse them.

OpenID Connect (OIDC) federation eliminates this entire class of risk by removing the credential entirely. Instead of GitHub possessing a secret that grants access to AWS, GitHub possesses only a signed JWT assertion about the current workflow run. AWS validates this signature against GitHub's public JWKS endpoint and issues temporary credentials scoped to exactly what the trust policy allows. These credentials expire automatically, typically within an hour, and cannot be reused outside the specific workflow context that requested them.

Legacy: Static Access KeysGitHub SecretsAWS IAM UserPermanent Key • Leak RiskOIDC: Ephemeral TokensGitHub JWTSTSIAM RoleNo Secrets • Auto-Expiry • Scoped
Legacy access key authentication versus OIDC token exchange when deploying to AWS from GitHub Actions with no keys

Beyond security, OIDC simplifies operational overhead. There are no rotation schedules to maintain, no IAM users to provision for each repository, and no risk of a single leaked key compromising multiple environments. For agencies managing dozens of client projects, as I do with Laravel applications deployed across shared infrastructure, this reduction in credential management complexity compounds significantly across the portfolio.

How do you configure the AWS IAM Identity Provider for GitHub OIDC?

The first step in setting up OIDC federation is creating an IAM Identity Provider resource in AWS that trusts GitHub's token issuer. This is a one-time configuration per AWS account, not per repository. You can create this via the console, but infrastructure-as-code is strongly preferred for reproducibility and auditability.

Terraform configuration for the identity provider

This Terraform resource defines GitHub as a trusted OIDC provider. The client_id_list must contain exactly sts.amazonaws.com, which is the audience value GitHub uses when minting tokens for AWS. The URL points to GitHub's fixed OIDC issuer endpoint.

resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [
    "6938fd4d98bab03faadb97b34396831e3780aea1",
    "1c58a3a8518e8759bf075b76b750d4f2df264fcd"
  ]
}

The thumbprint_list contains the SHA-1 fingerprints of GitHub's signing certificates. Including both the current and previous thumbprints ensures continuity during certificate rotations without downtime. AWS uses these to validate the JWT signature before accepting any token. Never omit this field or use placeholder values; the trust relationship will silently fail validation.

Verifying the provider configuration

After applying, confirm the provider exists and has the correct audience:

aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com

If you manage multiple AWS accounts through an organization, create this provider in each target account. It cannot be shared across accounts via RAM or cross-account references because the trust evaluation happens at the STS level within the receiving account.

What trust policy conditions secure the IAM role for GitHub Actions?

Creating the identity provider alone grants nothing. You must create an IAM role with a trust policy that explicitly restricts which GitHub workflows can assume it. This is where most misconfigurations occur. A trust policy that only checks the provider URL without additional conditions effectively grants any GitHub repository in the world permission to assume your role.

GitHub Workflow RunsValidate OIDC Provider + AudienceCheck repo:owner/name ConditionCheck ref:refs/heads/main ConditionAssumeRole SuccessDENY: Wrong RepoDENY: Wrong Branch
Trust policy condition evaluation flow when deploying to AWS from GitHub Actions with OIDC

Production-ready trust policy template

This trust policy restricts assumption to the main branch of a specific repository. Adjust the ref condition for environment branches or tag-based deployments.

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

The StringLike operator with the sub claim is critical. The sub claim contains the full workflow context including repository, branch, environment, and actor. Using StringEquals here would require matching the exact string including the job ID, which changes every run. StringLike with the prefix pattern matches all runs on the specified branch regardless of the trailing job identifier.

Environment-scoped roles for multi-stage deployments

For projects with staging and production environments, create separate IAM roles with different ref or environment conditions. GitHub's OIDC token includes the environment name when a workflow job specifies one:

"token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:environment:production"

This prevents a staging deployment workflow from assuming the production role even if someone modifies the workflow file. On cloud-hosted projects where staging and production share an AWS account, this separation is essential for preventing accidental cross-environment deployments.

How do you write the GitHub Actions workflow to assume the OIDC role?

With the AWS side configured, the workflow needs three elements: the id-token: write permission, the aws-actions/configure-aws-credentials action with role-to-assume, and no access key inputs whatsoever.

Complete workflow example for Laravel deployment

This workflow deploys a Laravel application using Deployer 7 after authenticating via OIDC. Note the explicit permissions block at the job level — this is mandatory.

name: Deploy to Production

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

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

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::ACCOUNT_ID:role/GitHubActionsDeployRole
          aws-region: ap-south-1

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          tools: composer:v2, deployer

      - name: Deploy with Deployer
        run: dep deploy production

The aws-actions/configure-aws-credentials@v4 action handles the entire OIDC exchange internally. It fetches the JWT from GitHub's internal endpoint, calls AssumeRoleWithWebIdentity, and exports the resulting temporary credentials as environment variables for subsequent steps. You never see or handle the token directly.

Common permission errors and fixes

The most frequent failure mode is forgetting id-token: write. Without it, the credentials action cannot request the JWT and fails with an opaque "Could not load credentials" error. Setting permissions at the workflow level applies to all jobs; setting them at the job level provides finer control. Always prefer job-level permissions to follow least privilege.

Another common issue is specifying aws-access-key-id alongside role-to-assume. The action prioritizes static credentials when present, silently bypassing OIDC. Remove all key-related inputs to ensure OIDC is actually used. Audit existing workflows for leftover key references from pre-OIDC configurations.

How does OIDC compare to other AWS authentication methods for CI/CD?

Understanding the trade-offs between authentication approaches helps justify the migration effort to stakeholders who may be comfortable with existing key-based setups.

CriteriaLong-Term Access KeysOIDC FederationIAM User + Rotation Script
Credential lifetimeIndefinite until manual deletion15–60 minutes, auto-expiresVaries, typically 30–90 days
Leak impactFull access until detected and rotatedLimited to single workflow runFull access until next rotation cycle
Rotation burdenManual or custom automation requiredNone — automatic by designCustom scripts, cron jobs, monitoring
Repository scope enforcementImpossible — keys are bearer tokensNative via trust policy conditionsImpossible — keys are bearer tokens
Multi-environment isolationSeparate IAM users per environmentSeparate roles with branch/environment conditionsSeparate IAM users per environment
Setup complexityLow initial, high ongoingModerate initial, near-zero ongoingHigh initial, high ongoing
Audit trail granularityCloudTrail shows IAM user, not workflowCloudTrail includes repo, branch, actor, run IDCloudTrail shows IAM user, not workflow

OIDC wins on every dimension except initial setup complexity, and that complexity is front-loaded and solved once per account. For teams already using infrastructure-as-code, the Terraform or CloudFormation templates take perhaps thirty minutes to write and apply. The ongoing savings in rotation automation, incident response, and compliance audits dwarf that investment.

GitHub ActionsWorkflow RunJWT IssuerConfigure CredsAWS SecurityOIDC ProviderSTS ServiceIAM RoleTarget ResourcesEC2 / ECSS3 ArtifactsRDS / ElastiCacheSigned JWTValidate + AssumeTemp CredsDeploy CodeUpload AssetsRun Migrations
End-to-end architecture for deploying to AWS from GitHub Actions with OIDC across security and resource layers

For Nepal-based teams working with international clients, OIDC also simplifies compliance conversations. When a client's security team asks how you manage cloud credentials, demonstrating that no long-term secrets exist in your CI/CD system is far more convincing than explaining your rotation schedule. This matters increasingly for building trust with global customers who evaluate vendor security posture before engagement.

Secure your AWS deployments now

Migrating to OIDC federation is a bounded, well-understood task that permanently eliminates an entire category of credential risk. The AWS IAM Identity Provider takes five minutes to create. The trust policy requires careful thought about branch and environment scoping but is straightforward once the condition syntax is understood. The workflow change is a single parameter swap. If you are still storing AWS access keys in GitHub Secrets in 2026, prioritize this migration before your next feature sprint. For teams needing hands-on implementation support for CI/CD pipelines, AWS infrastructure, or Laravel deployment automation, reach out to discuss your project.

Frequently Asked Questions

OpenID Connect allows GitHub Actions to assume an AWS IAM role directly using short-lived credentials, eliminating the need to store long-term access keys as repository secrets.

AWS IAM and STS are free; you pay only for underlying resources like EC2 or S3. GitHub Actions includes 2,000 free minutes monthly for private repos, sufficient for most Nepal-based SMB deployments.

Immediately if storing AWS_ACCESS_KEY_ID in GitHub Secrets. OIDC removes credential leakage risk entirely and is now the baseline security standard for any production CI/CD pipeline.

Create an IAM role with a trust policy referencing token.actions.githubusercontent.com as the OIDC provider. Include StringEquals conditions for repo and environment to restrict access. The audience must be sts.amazonaws.com. In my experience setting up Deployer 7 pipelines, getting this condition wrong is the most common failure point during initial setup. Always validate the policy JSON before applying.

This usually means the IAM trust policy conditions do not match the workflow context. Verify the repo owner/name casing matches exactly, as GitHub OIDC tokens are case-sensitive. Check that the environment name in your workflow matches the trust policy condition. On client projects, I have seen this fail silently when branches were renamed but the trust policy still referenced the old branch name. Use aws sts get-caller-identity in your workflow to debug token claims.

Yes. You must add token.actions.githubusercontent.com as an OpenID Connect identity provider in AWS IAM with audience sts.amazonaws.com. This is a one-time account-level configuration. After creating the provider, individual roles reference it in their trust policies. I configure this once per AWS account when onboarding new infrastructure. The provider itself has no cost and does not rotate; only the role trust policies require maintenance when repositories change.

Yes. Create separate IAM roles per environment (staging, production) with distinct trust policy conditions targeting specific GitHub environments or branches. Each role assumes independently via the same OIDC provider. On legal-tech portals I maintain, staging deploys from develop branch while production requires main plus manual approval. This separation prevents accidental production deployments and enforces least privilege without managing multiple credential sets across repositories.

OIDC issues temporary credentials valid only for the workflow run duration, typically under one hour. Stored keys persist indefinitely until manually rotated and grant access to anyone with repository secret visibility. If a key leaks through logs or forked repos, attackers gain full access. OIDC eliminates this attack surface entirely. For Nepal-based teams with shared repository access, OIDC provides auditability through CloudTrail showing exactly which workflow triggered each AWS action.

Yes. Configure the aws configure command in your deploy.php to use the OIDC-provided credentials automatically injected by aws-actions/configure-aws-credentials. Deployer 7 then uses these temporary credentials for S3 artifact uploads or EC2 operations. On sister sites sharing a GitLab-to-AWS pipeline, I migrated from static keys to OIDC without changing any Deployer task definitions. Ensure your IAM role permissions cover all Deployer operations including SSM Parameter Store if you fetch runtime configuration during deployment.

Minimum required permissions depend on your deployment strategy. For S3-based artifact storage, include s3:PutObject and s3:GetObject on the deployment bucket prefix. For EC2 deployments via SSM, add ssm:SendCommand and ec2:DescribeInstances. If using RDS, include rds-db:connect. Avoid AdministratorAccess. On production Laravel applications, I scope permissions to specific resource ARNs rather than wildcards. Test with restrictive policies first and expand based on CloudTrail AccessDenied errors during actual deployment runs.

Add StringEquals or StringLike conditions to your IAM trust policy matching github_actions:ref or github_actions:environment claims. For example, restrict production role assumption to refs/heads/main only. Environment protection rules in GitHub add a second layer requiring manual approval before the workflow runs. In practice, combining both IAM conditions and GitHub environment protections provides defense in depth. Never rely solely on branch naming conventions without corresponding IAM restrictions, as renamed branches bypass workflow-level checks.

Yes. Self-hosted runners receive OIDC tokens identically to GitHub-hosted runners. The token injection happens at the workflow level regardless of runner infrastructure. However, ensure your self-hosted runner can reach AWS STS endpoints. On Ubuntu servers in Kathmandu data centers, I have encountered connectivity issues where firewall rules blocked sts.amazonaws.com. Verify network egress before assuming OIDC failures are configuration problems. Self-hosted runners also require the actions/oidc-provider permission enabled in your organization settings.

OIDC providers do not require rotation since they validate signatures against GitHub's published JWKS endpoint. Updates are only needed when changing repository ownership, renaming repos, or modifying environment names. Create the new IAM role with updated conditions before deleting the old one to avoid deployment downtime. On active client projects, I test the new role in a non-blocking workflow step first. There is no secret rotation burden with OIDC, which is precisely why it replaces static credentials.

Yes. Both CDK and Terraform automatically detect AWS credentials configured by aws-actions/configure-aws-credentials using OIDC. No additional authentication configuration is required within your IaC code. For Terraform, ensure the backend S3 bucket and DynamoDB state lock table permissions are included in your IAM role. On infrastructure projects, I version-control the IAM role definition alongside the Terraform code so trust policy changes undergo the same review process as infrastructure changes. This prevents drift between deployment permissions and actual resource definitions.

Three issues appear repeatedly. First, cached AWS credentials from previous steps override OIDC tokens; always configure credentials immediately before AWS operations. Second, matrix strategies generate different token claims per job, requiring wildcard conditions in trust policies. Third, third-party actions may request explicit credentials as inputs rather than using the environment; audit all actions for hardcoded key parameters. During migration on production systems, I run OIDC and static-key workflows in parallel briefly to validate identical behavior before removing legacy secrets entirely.

Share this article

Quick Contact Options
Choose how you want to connect me: