This guide explains how to configure AWS IAM with OIDC (OpenID Connect) for secure CircleCI authentication without storing long-lived credentials.
Overview
Why OIDC?
| Access Keys (Old) | OIDC (Recommended) |
|---|---|
| Long-lived credentials | Temporary tokens (auto-expire) |
| Stored in CircleCI | No stored secrets |
| Can be leaked | Nothing to leak |
| Manual rotation needed | Automatic rotation |
Prerequisites
- AWS Account with Admin access
- CircleCI Organization ID (found in CircleCI → Organization Settings → Overview)
- AWS Account ID
Step 1: Create OIDC Identity Provider
AWS Console
- Go to IAM → Identity Providers → Add Provider
- Fill in:
| Field | Value |
|---|---|
| Provider type | OpenID Connect |
| Provider URL | https://oidc.circleci.com/org/<CIRCLECI_ORG_ID> |
| Audience | <CIRCLECI_ORG_ID> |
- Click Get thumbprint
- Click Add provider
AWS CLI
aws iam create-open-id-connect-provider \
--url "https://oidc.circleci.com/org/<CIRCLECI_ORG_ID>" \
--client-id-list "<CIRCLECI_ORG_ID>" \
--thumbprint-list "9e99a48a9960b14926bb7f3b02e22da2b0ab7280"
Step 2: Create IAM Policy
CircleCI-Deployment-Policy
Create this policy in IAM → Policies → Create Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRAuthToken",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "ECRPushImages",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "arn:aws:ecr:<REGION>:<ACCOUNT_ID>:repository/<ECR_REPO_NAME>"
},
{
"Sid": "AppRunnerDeploy",
"Effect": "Allow",
"Action": [
"apprunner:StartDeployment",
"apprunner:DescribeService"
],
"Resource": "arn:aws:apprunner:<REGION>:<ACCOUNT_ID>:service/<SERVICE_NAME>/*"
},
{
"Sid": "AmplifyDeploy",
"Effect": "Allow",
"Action": [
"amplify:StartJob",
"amplify:GetJob"
],
"Resource": "arn:aws:amplify:<REGION>:<ACCOUNT_ID>:apps/<AMPLIFY_APP_ID>/*"
}
]
}
Note: Replace
<REGION>,<ACCOUNT_ID>,<ECR_REPO_NAME>,<SERVICE_NAME>, and<AMPLIFY_APP_ID>with your actual values. For quick setup, you can use"Resource": "*"but this is less secure.
Step 3: Create IAM Role
AWS Console
- Go to IAM → Roles → Create Role
- Select Web identity as trusted entity type
- Select your OIDC provider from the dropdown
- Set Audience to your CircleCI Org ID
- Click Next
- Attach
CircleCI-Deployment-Policy - Name the role:
CircleCI-OIDC-Role - Click Create role
Trust Policy
After creating the role, update the trust policy for additional security:
IAM → Roles → CircleCI-OIDC-Role → Trust relationships → Edit
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/oidc.circleci.com/org/<CIRCLECI_ORG_ID>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.circleci.com/org/<CIRCLECI_ORG_ID>:aud": "<CIRCLECI_ORG_ID>"
}
}
}
]
}
Optional: Restrict to Specific Project
Add this condition to limit access to a specific CircleCI project:
"StringLike": {
"oidc.circleci.com/org/<CIRCLECI_ORG_ID>:sub": "org/<CIRCLECI_ORG_ID>/project/<PROJECT_ID>/user/*"
}
Step 4: Update CircleCI Configuration
Update .circleci/config.yml to use OIDC authentication:
version: 2.1
orbs:
node: circleci/node@5.2.0
aws-cli: circleci/aws-cli@4.1.2
aws-ecr: circleci/aws-ecr@9.0.2
executors:
node-executor:
docker:
- image: cimg/node:20.11
working_directory: ~/project
jobs:
push-backend-to-ecr:
executor: node-executor
steps:
- checkout
- setup_remote_docker:
version: 20.10.24
- aws-cli/setup:
role_arn: ${AWS_ROLE_ARN}
region: ${AWS_REGION}
- run:
name: Build and Push Backend Docker Image
command: |
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ECR_REGISTRY
docker build -t $AWS_ECR_REGISTRY/demo-backend:$CIRCLE_SHA1 -t $AWS_ECR_REGISTRY/demo-backend:latest ./backend
docker push $AWS_ECR_REGISTRY/demo-backend:$CIRCLE_SHA1
docker push $AWS_ECR_REGISTRY/demo-backend:latest
deploy-backend-to-apprunner:
executor: node-executor
steps:
- aws-cli/setup:
role_arn: ${AWS_ROLE_ARN}
region: ${AWS_REGION}
- run:
name: Trigger App Runner Deployment
command: |
aws apprunner start-deployment --service-arn $AWS_APP_RUNNER_SERVICE_ARN
deploy-frontend-to-amplify:
executor: node-executor
steps:
- checkout
- aws-cli/setup:
role_arn: ${AWS_ROLE_ARN}
region: ${AWS_REGION}
- run:
name: Trigger Amplify Deployment
command: |
aws amplify start-job --app-id $AWS_AMPLIFY_APP_ID --branch-name main --job-type RELEASE
Step 5: Configure CircleCI Environment Variables
Add These Variables
In CircleCI → Project Settings → Environment Variables:
| Variable | Value | Description |
|---|---|---|
AWS_ROLE_ARN |
arn:aws:iam::<ACCOUNT_ID>:role/CircleCI-OIDC-Role |
IAM role ARN |
AWS_REGION |
us-east-1 |
AWS region |
AWS_ECR_REGISTRY |
<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com |
ECR registry URL |
AWS_APP_RUNNER_SERVICE_ARN |
arn:aws:apprunner:... |
App Runner service ARN |
AWS_AMPLIFY_APP_ID |
<APP_ID> |
Amplify app ID |
Remove These Variables (After Testing)
| Variable | Reason |
|---|---|
AWS_ACCESS_KEY_ID |
No longer needed with OIDC |
AWS_SECRET_ACCESS_KEY |
No longer needed with OIDC |
Step 6: Test OIDC Authentication
Add a test job to verify the setup:
jobs:
test-oidc-auth:
docker:
- image: cimg/base:current
steps:
- aws-cli/setup:
role_arn: ${AWS_ROLE_ARN}
region: ${AWS_REGION}
- run:
name: Verify AWS Identity
command: aws sts get-caller-identity
Expected Output
{
"UserId": "AROA...:circleci-test-oidc-auth",
"Account": "<ACCOUNT_ID>",
"Arn": "arn:aws:sts::<ACCOUNT_ID>:assumed-role/CircleCI-OIDC-Role/circleci-test-oidc-auth"
}
Troubleshooting
Error: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
- Verify the OIDC provider URL matches exactly
- Check the audience claim matches your CircleCI Org ID
- Ensure the trust policy has the correct Principal ARN
Error: "Token is expired"
- OIDC tokens are short-lived; ensure the job runs promptly
- Check for clock skew issues
Error: "Invalid identity token"
- Verify CircleCI Org ID is correct
- Check OIDC provider thumbprint
Security Best Practices
| Practice | Description |
|---|---|
| Restrict resources | Use specific ARNs instead of * in policies |
| Limit by project | Add project ID condition to trust policy |
| Enable CloudTrail | Audit all role assumptions |
| Separate environments | Use different roles for staging/production |
| Regular review | Periodically audit IAM policies and roles |
Developer Access (Separate from CI/CD)
Developers should have their own IAM users/roles with appropriate permissions. See AWS_DEVELOPER_ACCESS.md for developer IAM setup.
Cost
OIDC authentication is free. No additional AWS charges for:
- OIDC Identity Provider
- IAM Roles and Policies
- STS AssumeRoleWithWebIdentity calls
Top comments (0)