AWS: GitHub OIDC and IAM Roles
Set up keyless GitHub Actions authentication to AWS using OIDC, IAM roles, Atmos auth profiles, and GitHub workflows.
This guide walks you through setting up keyless authentication between GitHub Actions and AWS for Terraform CI/CD. By the end, your GitHub Actions workflows will authenticate to AWS using short-lived OIDC tokens — no static credentials required.
- 1GitHub OIDC Identity Provider — Create the trust relationship between GitHub and your AWS account.
- 2IAM Roles — Create IAM roles that GitHub Actions assumes to run Terraform, scoped to your repository.
- 3Atmos Auth Profiles — Configure the Atmos CLI to use the OIDC provider and roles at runtime.
- 4GitHub Workflows — Wire up your GitHub Actions workflows to use the auth profiles.
Everything is defined inline using Atmos source provisioning — no pre-vendored components or imports required.
Curious how all the pieces connect? Jump to How It All Fits Together.
The OIDC provider tells AWS to trust tokens issued by GitHub Actions. Deploy this once per AWS account.
components:
terraform:
github-oidc-provider:
source:
uri: github.com/cloudposse-terraform-components/aws-github-oidc-provider.git//src
version: v1.535.1
provision:
workdir:
enabled: true
vars:
enabled: trueatmos terraform apply github-oidc-provider --stack devAfter deploying, note the provider ARN from the Terraform output — you'll need it for the IAM roles in the next step.
Create IAM roles that GitHub Actions assumes via the OIDC provider. The role's trust policy ensures only your repository can assume it.
One role with full access for both
terraform plan and terraform apply. Best for small teams or single-account setups.components:
terraform:
iam-role/terraform:
metadata:
component: iam-role
source:
uri: github.com/cloudposse-terraform-components/aws-iam-role.git//src
version: v1.537.0
provision:
workdir:
enabled: true
vars:
enabled: true
name: terraform
role_description: |
IAM role for GitHub Actions CI/CD.
Trusts the GitHub OIDC provider for keyless authentication.
managed_policy_arns:
- arn:aws:iam::aws:policy/AdministratorAccess
# GitHub OIDC trust
github_oidc_provider_enabled: true
github_oidc_provider_arn: arn:aws:iam::111111111111:oidc-provider/token.actions.githubusercontent.com # Replace with your OIDC provider ARN
trusted_github_org: acme # Replace with your GitHub org
trusted_github_repos:
- "acme/infra-live" # Replace with your repo
policy_statements:
TerraformStateBackendAssumeRole:
effect: "Allow"
actions:
- "sts:AssumeRole"
- "sts:TagSession"
- "sts:SetSourceIdentity"
resources:
- arn:aws:iam::111111111111:role/my-tfstate-role # Replace with your TF state backend role ARNBy default,
trusted_github_repos: ["acme/infra-live"] trusts any workflow run from that repository — a pull request preview can assume the same role as a production deploy. To restrict which workflow contexts can assume a role, append a constraint suffix to the repo entry. The suffix maps to a match pattern used in the IAM trust policy's StringLike condition against the OIDC sub claim.trusted_github_repos entry | IAM sub match pattern | Scopes to |
|---|---|---|
"acme/infra-live" | repo:acme/infra-live:* | Any ref, PR, or environment |
"acme/infra-live:main" | repo:acme/infra-live:ref:refs/heads/main | main branch only |
"acme/infra-live:environment:production" | repo:acme/infra-live:environment:production | GitHub Environment production only |
"acme/infra-live:ref:refs/heads/release/*" | repo:acme/infra-live:ref:refs/heads/release/* | Any release/* branch |
"acme/infra-live:ref:refs/tags/v*" | repo:acme/infra-live:ref:refs/tags/v* | Any v* tag |
For the Advanced setup with separate plan and apply roles, this means:
- The planner role can stay broadly scoped — it's read-only, so it's safe for any PR or branch
- The terraform (apply) role should be scoped to a GitHub Environment like
production, so only workflows running in that environment can assume it
# Planner: broadly scoped (read-only, safe for any PR)
trusted_github_repos:
- "acme/infra-live"
# Terraform: scoped to the 'production' GitHub Environment
trusted_github_repos:
- "acme/infra-live:environment:production"For this to work, your apply workflow must set the job's
environment to the matching GitHub Environment name — this is what causes GitHub to include environment:production in the OIDC token's sub claim. The environment is typically passed as a workflow input (e.g., github_environment) so the workflow stays reusable across stages. See Step 4 for the workflow configuration.Verify your OIDC setup with a minimal workflow
Before configuring Atmos profiles and workflows, you can test that your OIDC provider and IAM role are working with a minimal workflow that just calls
sts get-caller-identity:name: Test AWS OIDC
on:
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111111111111:role/acme-gbl-dev-terraform # Replace with your role ARN
aws-region: us-east-2 # Replace with your region
- name: Verify Identity
run: aws sts get-caller-identityRun this workflow manually from the Actions tab. If the output shows your assumed role ARN, the OIDC trust is working correctly and you can proceed to configure Atmos.
Auth profiles tell the Atmos CLI how to obtain cloud credentials at runtime. Set
ATMOS_PROFILE in your workflow to activate a profile.Each identity in a profile has a name like
acme/admin or acme/planner. These names appear in your stack configuration when selecting which identity a component should use, so choose a convention that is clear and consistent.Name identities after permission boundaries, not deployment contexts. The profile already captures the context (e.g.,
github for CI, local for development) — the identity name should describe what level of access is being granted, not where it's being used. This keeps identity names stable across profiles: acme/admin means the same thing whether you're authenticating via GitHub OIDC or local SSO.We recommend the pattern
{org}/{boundary}:| Identity | Permission boundary | Typical use |
|---|---|---|
acme/admin | Full administrative access | Terraform apply, infrastructure changes |
acme/planner | Read-only access | Terraform plan, drift detection |
acme/network | Scoped to networking resources | Network-specific components |
This convention makes stack configuration self-documenting — when you see a component selecting
acme/planner, you immediately know it only needs read-only access, regardless of which profile is active.A single profile used for both plan and apply:
auth:
providers:
github-oidc:
kind: github/oidc
region: us-east-2 # Replace with your region
spec:
audience: sts.amazonaws.com
identities:
acme/admin:
default: true
kind: aws/assume-role
via:
provider: github-oidc
principal:
assume_role: arn:aws:iam::111111111111:role/acme-gbl-dev-terraform # Replace with your role ARNWire up your GitHub Actions workflows to use the auth profiles. The key requirements are:
permissions: id-token: writeso GitHub issues OIDC tokensATMOS_PROFILEenvironment variable to activate the correct auth profile
The examples below focus on the plan and apply workflows that use OIDC authentication. A complete Atmos Pro setup also requires workflows for detecting affected stacks and uploading instances. For the full set of workflows, see Configure GitHub Workflows.
With a single profile, both plan and apply workflows use the same
ATMOS_PROFILE:name: Atmos Terraform Plan
on:
workflow_dispatch:
inputs:
component:
description: "Component"
required: true
type: string
stack:
description: "Stack"
required: true
type: string
permissions:
id-token: write
contents: read
jobs:
plan:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- name: Terraform Plan
env:
ATMOS_PROFILE: github
COMPONENT: ${{ inputs.component }}
STACK: ${{ inputs.stack }}
run: |
atmos terraform plan "${COMPONENT}" -s "${STACK}"The GitHub OIDC Identity Provider is deployed once per AWS account. It establishes a trust relationship so AWS accepts tokens issued by GitHub Actions.
IAM roles reference that OIDC provider and include a trust policy scoped to one or more specific repositories. Only workflows running in those repositories can assume the role.
Atmos auth profiles tell the Atmos CLI how to exchange a GitHub OIDC token for temporary AWS credentials by assuming a specific IAM role. You use multiple profiles so the same Atmos configuration works in different contexts — a developer profile for local usage and a CI profile for GitHub Actions.
In your GitHub Actions workflows, the
permissions block is what enables the token exchange:permissions:
id-token: write
contents: readSetting
id-token: write tells GitHub to issue a short-lived OIDC token for the workflow run. The Atmos CLI picks up that token automatically and uses the active auth profile to assume the corresponding IAM role.Finally, identities in the auth profile determine which IAM role is assumed. When an identity has
default: true, it is used automatically for all components:identities:
acme/admin:
default: true # Used for all components unless overridden
kind: aws/assume-role
via:
provider: github-oidc
principal:
# This is the IAM role you provisioned with the iam-role component in Step 2
assume_role: arn:aws:iam::111111111111:role/acme-gbl-dev-terraformIdentities follow the Atmos inheritance model, so you can override them at any level when specific components need different credentials.
This guide covers the OIDC and IAM foundation. To complete your Atmos Pro setup, you'll also need to:
- Import your repositories into Atmos Pro so it can monitor pull requests and dispatch workflows
- Configure repository permissions in Atmos Pro to control who can approve and apply changes
- Set up the remaining GitHub workflows — the examples above cover plan and apply, but you also need workflows for detecting affected stacks and uploading instances
Next: Configure GitHub Workflows
Set up the complete set of workflows including affected stacks detection, plan, apply, and instance uploads.