Overview
OpenID Connect (OIDC) allows your GitHub Actions workflows to access resources in Amazon Web Services (AWS) without storing long-lived AWS credentials as GitHub secrets. Whether you're deploying Lambda functions, updating S3 buckets, managing EC2 instances, or using any AWS service, this guide shows you how to transition from using AWS access keys to OIDC authentication.
Why OIDC Instead of Access Keys?
Access Keys (Traditional Method):
- ❌ Long-lived credentials that never expire
- ❌ Must be rotated manually
- ❌ If leaked, remain valid until manually revoked
- ❌ Stored as GitHub secrets
OIDC (Modern Method):
- ✅ Short-lived tokens that expire automatically
- ✅ No credentials to rotate
- ✅ Tokens are automatically invalidated after use
- ✅ Fine-grained access control with trust policies
Prerequisites
Before proceeding, ensure you have:
- An AWS account with IAM permissions
- A GitHub repository
- Basic understanding of IAM roles and policies
Step 1: Add GitHub OIDC Provider to AWS
- Navigate to the IAM Console in AWS
- Go to Identity providers → Add provider
- Select OpenID Connect
- Configure the provider:
Provider URL:https://token.actions.githubusercontent.com
Audience:sts.amazonaws.com
- Click Add provider
Step 2: Create IAM Role with Trust Policy
Create the IAM Role
- Navigate to IAM → Roles → Create role
- Select Web identity as the trusted entity type
- Choose the identity provider:
token.actions.githubusercontent.com
- Select Audience:
sts.amazonaws.com
Configure the Trust Policy
Edit the trust policy to restrict access to your specific repository:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID: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: Replace YOUR_ACCOUNT_ID, YOUR_ORG, and YOUR_REPO with your actual values.
Understanding the Trust Policy Conditions
token.actions.githubusercontent.com:aud: Ensures the token is intended for AWS STS
token.actions.githubusercontent.com:sub: Restricts access to specific repository and branch
Trust Policy Variations
Allow any branch in the repository:
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:*"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
Allow specific environment:
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:environment:prod"
}
}
Allow pull requests:
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:pull_request"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
Attach Permissions Policy
Attach the necessary permissions for your deployment based on what AWS services you're using:
Common AWS Services:
Lambda & API Gateway:AWSLambdaFullAccess,AmazonAPIGatewayAdministrator
S3:AmazonS3FullAccess
EC2:AmazonEC2FullAccess
ECS/Fargate:AmazonECS_FullAccess
CloudFormation/CDK/Terraform:CloudFormationFullAccess
General deployments:IAMFullAccess(or more restricted custom policy)
Note: In production, create a custom policy with least-privilege permissions specific to your needs.
Copy the Role ARN
After creating the role, copy the Role ARN (e.g., arn:aws:iam::123456789012:role/GitHubActionsRole). You'll need this for the next step.
Step 3: Update GitHub Repository Secrets
- Go to your GitHub repository
- Navigate to Settings → Secrets and variables → Actions
- Add a new repository secret:
Name:AWS_ROLE_ARN
Value: Your IAM role ARN (e.g.,arn:aws:iam::123456789012:role/GitHubActionsRole)
Step 4: Update GitHub Actions Workflow
Update your .github/workflows/deploy.yml to use OIDC instead of access keys:
Before (Access Keys):
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials (Access Keys)
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
After (OIDC):
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for requesting the JWT
contents: read # Required for actions/checkout
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
Key Changes:
Added
permissionsblock:
id-token: writeallows GitHub to create a JWT token
contents: readallows checking out the repository
Replaced credentials:
- Removed
aws-access-key-idandaws-secret-access-key
- Added
role-to-assumewith the IAM role ARN
- Removed
Updated step name: Changed from "Access Keys" to "OIDC" for clarity
Step 5: Clean Up Old Secrets (Optional)
After confirming OIDC works successfully, remove the old access key secrets:
- Go to Settings → Secrets and variables → Actions
- Delete
AWS_ACCESS_KEY_ID
- Delete
AWS_SECRET_ACCESS_KEY
Important: Delete these secrets only after verifying OIDC authentication works!
Complete Workflow Example
Here's a complete workflow example deploying to AWS (this example uses Serverless Framework, but the OIDC configuration works with any deployment tool):
name: Deploy Lambda to AWS
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Install dependencies
run: npm install
- name: Deploy to AWS
run: npx serverless deploy --verbose
Testing the Setup
- Commit and push your changes to the
mainbranch - Navigate to Actions tab in your GitHub repository
- Monitor the workflow run
- Verify the "Configure AWS Credentials (OIDC)" step succeeds
Troubleshooting
Error: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
Solution: Verify your trust policy includes the correct repository path:
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:ref:refs/heads/main"
Error: "User is not authorized to perform action on resource"
Solution: Check the IAM role has the necessary permissions attached for your specific AWS services and deployment tool (CloudFormation, Terraform, CDK, Serverless Framework, etc.).
Error: "An error occurred (AccessDenied) when calling the CreateStack operation"
Solution: Ensure your IAM role has CloudFormationFullAccess or equivalent permissions.
Security Best Practices
Use specific conditions: Restrict the trust policy to specific branches or environments
Least privilege permissions: Attach only the minimum required permissions to the IAM role
Monitor usage: Enable AWS CloudTrail to audit role assumptions
Regular reviews: Periodically review and update trust policies
Use environments: For production deployments, use GitHub environments with protection rules
Comparison Summary
| Feature | Access Keys | OIDC |
|---|---|---|
| Credential lifetime | Permanent | Temporary (1 hour) |
| Rotation required | Yes | No |
| Secrets to manage | 2 per AWS account | 1 per IAM role |
| Security risk if leaked | High | Low |
| Setup complexity | Simple | Moderate |
| Recommended for | Development/testing | Production |
Conclusion
By switching from AWS access keys to OIDC, you've improved your deployment security by:
- Eliminating long-lived credentials
- Reducing secret management overhead
- Implementing fine-grained access control
- Following AWS and GitHub security best practices
The initial setup requires more configuration, but the long-term security and maintenance benefits make OIDC the recommended approach for production deployments.
Additional Resources
Have you made the switch to OIDC? Share your experience in the comments below! 👇