This project uses OpenID Connect (OIDC) to authenticate GitHub Actions with AWS, eliminating the need for long-lived AWS access keys stored as GitHub secrets.
- Enhanced Security: No static credentials stored in GitHub
- Least Privilege: IAM role with minimal required permissions
- Automatic Rotation: Temporary credentials expire automatically
- Audit Trail: CloudTrail logs show which GitHub workflow assumed the role
- Reduced Risk: Compromised GitHub secrets cannot access AWS
GitHub Actions Workflow
↓ (OIDC Token)
AWS IAM OIDC Identity Provider
↓ (Validates)
IAM Role with Trust Policy
↓ (Assumes)
Temporary AWS Credentials
↓ (Uses)
AWS Services (ECS, ECR, S3, etc.)
# Using AWS CLI
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1Or via AWS Console:
- Go to IAM → Identity providers → Add provider
- Provider type: OpenID Connect
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com - Click "Add provider"
Create a new IAM role with this trust policy (replace YOUR_GITHUB_ORG and YOUR_REPO):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_AWS_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_GITHUB_ORG/YOUR_REPO:*"
}
}
}
]
}For production-only deployments, restrict to specific branches:
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_ORG/YOUR_REPO:ref:refs/heads/main"Attach this policy to the role (adjust based on your deployment needs):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRAccess",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
},
{
"Sid": "ECSDeployment",
"Effect": "Allow",
"Action": ["ecs:UpdateService", "ecs:DescribeServices", "ecs:DescribeTasks", "ecs:ListTasks"],
"Resource": [
"arn:aws:ecs:*:YOUR_AWS_ACCOUNT_ID:service/production/brain-storm-backend",
"arn:aws:ecs:*:YOUR_AWS_ACCOUNT_ID:task/production/*"
]
},
{
"Sid": "PassRoleToECS",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/ecsTaskExecutionRole",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
},
{
"Sid": "CloudWatchLogs",
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:*:YOUR_AWS_ACCOUNT_ID:log-group:/aws/ecs/brain-storm-*"
}
]
}Remove old secrets:
(delete)AWS_ACCESS_KEY_ID(delete)AWS_SECRET_ACCESS_KEY
Add new secrets (Settings → Secrets and variables → Actions):
AWS_ROLE_ARN: The ARN of the IAM role created in step 2- Example:
arn:aws:iam::123456789012:role/GitHubActionsDeploymentRole
- Example:
AWS_REGION: Your AWS region (e.g.,us-east-1)
The deployment workflow has been updated to use OIDC. Key changes:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: GitHubActions-BrainStorm-Production
aws-region: ${{ secrets.AWS_REGION }}Ensure the workflow has id-token: write permission:
permissions:
id-token: write
contents: readTest the OIDC setup:
# Trigger a workflow manually or push to main
# Check workflow logs for:
# ✅ "Assuming role: arn:aws:iam::..."
# ✅ "Credentials successfully configured"- Verify the trust policy includes your repository
- Check the OIDC provider thumbprint is correct
- Ensure
id-token: writepermission is set in workflow
- Review IAM policy attached to the role
- Ensure resource ARNs match your AWS account and resources
- Check CloudTrail logs for specific denied actions
- Verify the OIDC provider URL is exactly
https://token.actions.githubusercontent.com - Check the audience is
sts.amazonaws.com - Ensure the workflow is running from the correct repository
- Restrict by Branch: Limit role assumption to
mainbranch only - Least Privilege: Grant only permissions needed for deployment
- Resource-Specific: Use specific ARNs instead of wildcards
- Session Duration: Set maximum session duration to 1 hour
- Audit Regularly: Review CloudTrail logs for unexpected role assumptions
- Rotate Regularly: Update OIDC thumbprint if GitHub rotates certificates
- Create AWS IAM OIDC identity provider
- Create IAM role with trust policy
- Attach least-privilege IAM policy to role
- Add
AWS_ROLE_ARNsecret to GitHub - Add
AWS_REGIONsecret to GitHub - Update deployment workflows to use OIDC
- Test deployment workflow
- Delete old
AWS_ACCESS_KEY_IDsecret - Delete old
AWS_SECRET_ACCESS_KEYsecret - Document setup in team wiki