aws-oidc
helps configure and implement GitHub OIDC authentication with AWS for secure, keyless access from GitHub Actions to AWS resources
GitHub OIDC Authentication for AWS
Overview
OpenID Connect (OIDC) allows your GitHub Actions workflows to access resources in AWS without needing to store AWS credentials as long-lived GitHub secrets. This eliminates the security risks associated with static credentials and provides temporary, scoped access tokens.
Core Concepts
What is OIDC?
- Federated Identity: AWS trusts GitHub's OIDC provider to authenticate workflows
- Temporary Tokens: Short-lived tokens replace static credentials
- Scoped Access: Fine-grained permissions based on repository, branch, and environment
- Security: No secrets stored in GitHub; tokens are generated on-demand
Key Components
- OIDC Provider: AWS trusts
https://token.actions.githubusercontent.com - IAM Role: AWS role that GitHub Actions can assume
- Trust Policy: Defines which repositories/branches can assume the role
- Permissions Policy: Defines what AWS actions the role can perform
Setup Methods
Method 1: Manual Configuration
Step 1: Create OIDC Provider in AWS
- Sign in to AWS Management Console
- Navigate to IAM → Identity Providers
- Click Add Provider
- Select OpenID Connect as provider type
- Configure:
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com
- Provider URL:
- Click Add Provider
Step 2: Create IAM Role with Trust Policy
- Go to IAM → Roles
- Click Create Role
- Select Web identity as trusted entity
- Choose the OIDC provider created earlier
- Configure trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRoleWithWebIdentity",
"Principal": {
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
},
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": [
"sts.amazonaws.com"
]
},
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:<ORG>/<REPO>:ref:refs/heads/main"
]
}
}
}
]
}
Step 3: Attach Permissions Policies
Attach necessary AWS policies (e.g., AmazonS3ReadOnlyAccess, custom policies)
Method 2: Terraform Infrastructure as Code
ensure the aws account has a terraform state bucket, utilize dojo chat mode to help create that first if one is not present.
Repository Structure
Based on the OptumInsight-Platform/dp-nucleus-aws-bootstrap-account repository:
tf/
├── main.tf # Core OIDC and IAM resources
├── variables.tf # Input variables
├── output.tf # Output values
├── policies/
│ └── github_action_policy.tftpl # IAM policy template
└── tfvars/
└── dev_variables.tfvars # Environment-specific values
Key Terraform Resources
1. OIDC Provider
resource "aws_iam_openid_connect_provider" "github" {
client_id_list = [
"sts.amazonaws.com"
]
thumbprint_list = [
data.tls_certificate.github.certificates.0.sha1_fingerprint
]
url = "https://token.actions.githubusercontent.com"
tags = local.required_tags
}
2. IAM Role with Trust Policy
resource "aws_iam_role" "github_action_role" {
name = "${var.team}-github-action-role-${var.namespace}"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement : [
{
Effect = "Allow",
Principal : {
Federated : aws_iam_openid_connect_provider.github.arn
},
Action : "sts:AssumeRoleWithWebIdentity",
Condition : {
StringEquals : {
"token.actions.githubusercontent.com:aud" : "sts.amazonaws.com"
},
StringLike : {
"token.actions.githubusercontent.com:sub" : "repo:${var.github_org}/${var.github_repo_prefix}*"
}
}
}
]
})
tags = local.required_tags
}
3. Custom IAM Policy
resource "aws_iam_policy" "github_action_policy" {
name = "${var.team}-github-action-policy-${var.namespace}"
description = "Policy for GitHub Actions"
policy = templatefile("${path.module}/policies/github_action_policy.tftpl", local.custom_template_vars)
tags = local.required_tags
}
Required Variables
variable "namespace" {
default = "dev"
type = string
description = "Environment stage (prod, dev, qa)"
}
variable "team" {
type = string
description = "Team name"
}
variable "github_org" {
type = string
description = "GitHub organization name"
}
variable "github_repo_prefix" {
type = string
description = "GitHub repository prefix pattern"
}
variable "s3_primary_bucket" {
type = string
description = "Primary S3 bucket name"
}
variable "aws_managed_policy_attachment" {
type = list(string)
description = "List of AWS managed policies to attach"
default = []
}
Example Configuration
# tfvars/dev_variables.tfvars
namespace = "dev"
team = "nucleus"
github_org = "OptumInsight-Platform"
github_repo_prefix = "dp-nucleus"
s3_primary_bucket = "dp-nucleus-us-east-1-dev"
ask_id = "AIDE_0086287"
aws_managed_policy_attachment = [
"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess",
"arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
]
Deployment Commands
cd tf
terraform init
terraform plan -var-file=tfvars/dev_variables.tfvars -out=tfplan
terraform apply tfplan
GitHub Actions Integration
Required Permissions
permissions:
id-token: write # Required for OIDC token generation
contents: read # Required for repository access
Basic Workflow Example
name: Deploy to AWS
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: uhg-runner
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::<AWS_ACCOUNT_ID>:role/<ROLE_NAME>
role-session-name: GitHubOIDCSession
aws-region: us-east-1
- name: Verify authentication
run: |
aws sts get-caller-identity
aws s3api list-buckets --query 'Buckets[].Name'
Advanced Workflow with Multiple Environments
name: Multi-Environment Deployment
on:
push:
branches:
- main
- develop
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: uhg-runner
strategy:
matrix:
environment: [dev, staging, prod]
include:
- environment: dev
aws-role: arn:aws:iam::123456789012:role/github-action-role-dev
condition: github.ref == 'refs/heads/develop'
- environment: staging
aws-role: arn:aws:iam::123456789012:role/github-action-role-staging
condition: github.ref == 'refs/heads/main'
- environment: prod
aws-role: arn:aws:iam::987654321098:role/github-action-role-prod
condition: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
if: ${{ matrix.condition }}
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ matrix.aws-role }}
role-session-name: GitHubOIDCSession-${{ matrix.environment }}
aws-region: us-east-1
- name: Deploy to ${{ matrix.environment }}
run: |
echo "Deploying to ${{ matrix.environment }}"
aws sts get-caller-identity
Integration with Optum Artifactory
name: Build, Scan, and Deploy
on:
push:
branches: [main]
permissions:
actions: read
contents: write
pull-requests: write
security-events: write
checks: write
id-token: write
jobs:
build-and-deploy:
runs-on: [uhg-runner]
steps:
- uses: actions/checkout@v4
# Configure Artifactory
- name: Configure Artifactory Connection
id: artifactory-setup
uses: uhg-pipelines/epl-jf/configure-saas-connection@latest
with:
jfrog-project-key: your-project-key
npm-setup: true
# Build and publish to Artifactory
- name: Build and Scan
uses: optum-eeps/epl-actions/node-build-scan@v1
with:
jfrog-project-key: your-project-key
jfrog-build-name: ${{ steps.artifactory-setup.outputs.jfrog-build-name }}
jfrog-build-number: ${{ steps.artifactory-setup.outputs.jfrog-build-number }}
npm-publish: true
# Configure AWS credentials
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: GitHubOIDCSession
aws-region: us-east-1
# Deploy to AWS
- name: Deploy to AWS
run: |
aws s3 sync ./dist s3://${{ secrets.S3_BUCKET }}/
aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_ID }} --paths "/*"
Security Best Practices
1. Least Privilege Access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::specific-bucket/*"
]
}
]
}
2. Restrict Repository Access
{
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:OptumInsight-Platform/specific-repo:ref:refs/heads/main",
"repo:OptumInsight-Platform/specific-repo:ref:refs/heads/develop"
]
}
}
3. Environment-Specific Roles
{
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:environment": "production"
}
}
4. Time-Limited Sessions
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: GitHubOIDCSession
role-duration-seconds: 3600 # 1 hour maximum
aws-region: us-east-1
Troubleshooting
Common Issues
1. "No OpenIDConnect provider found"
# Verify OIDC provider exists
aws iam list-open-id-connect-providers
2. "AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity"
- Check trust policy conditions
- Verify repository name and branch in trust policy
- Ensure workflow has
id-token: writepermission
3. "Invalid identity token"
- Verify audience is set to
sts.amazonaws.com - Check token expiration settings
- Ensure workflow runs on supported runner
4. "Role session name is invalid"
# Use valid session name format
role-session-name: GitHubOIDCSession-${{ github.run_id }}
Debugging Steps
- name: Debug OIDC Token
env:
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${{ env.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
ACTIONS_ID_TOKEN_REQUEST_URL: ${{ env.ACTIONS_ID_TOKEN_REQUEST_URL }}
run: |
# Get the token for debugging
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq .
Advanced Patterns
1. Cross-Account Access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT-A:role/github-action-role"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id"
}
}
}
]
}
2. Conditional Access Based on PR Labels
- name: Configure AWS credentials for production
if: contains(github.event.pull_request.labels.*.name, 'deploy-prod')
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_PROD_ROLE_ARN }}
aws-region: us-east-1
3. Multi-Region Deployment
strategy:
matrix:
region: [us-east-1, us-west-2, eu-west-1]
steps:
- name: Configure AWS credentials for ${{ matrix.region }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ matrix.region }}
Getting Latest Versions
GitHub CLI Commands
# Get latest aws-actions/configure-aws-credentials version
gh api repos/aws-actions/configure-aws-credentials/releases/latest --jq '.tag_name'
# Check Optum bootstrap repository updates
gh api repos/OptumInsight-Platform/dp-nucleus-aws-bootstrap-account/commits/main --jq '.[0].sha'
# List available example repositories
gh api orgs/OptumInsight-Platform/repos --jq '.[] | select(.name | contains("bootstrap")) | .name'
Implementation Checklist
Pre-Setup
- AWS account with IAM permissions
- GitHub repository in OptumInsight-Platform organization
- Terraform installed (if using IaC approach)
- AWS CLI configured locally
OIDC Configuration
- OIDC provider created in AWS
- IAM role created with trust policy
- Permissions policies attached
- Trust policy restricts to specific repositories/branches
GitHub Actions
- Workflow includes
id-token: writepermission - Role ARN configured as repository secret
- Test workflow validates authentication
- Error handling implemented
Security Validation
- Least privilege principle applied
- Repository access properly restricted
- Session duration appropriate
- Monitoring and alerting configured
Documentation
- Team training on OIDC concepts
- Runbooks for troubleshooting
- Security policies documented
- Regular review schedule established
Support Resources
- AWS Documentation: Using OpenID Connect identity providers
- GitHub Documentation: About security hardening with OpenID Connect
- Optum Bootstrap Repository: dp-nucleus-aws-bootstrap-account
- Security Best Practices: AWS Security Best Practices for GitHub Actions
Remember: OIDC eliminates the need for long-lived AWS credentials in GitHub, significantly improving security posture while maintaining automation capabilities.

