Using AWS OIDC Roles in GitHub Actions for CI/CD
🤝 Intro : GHA + AWS Without Leaking Your Secrets
I wanted to migrate away from using AWS secrets in GitHub Actions — not because it wasn’t working, but because storing long-lived credentials in CI just felt… sketchy. Like, “I really shouldn’t be doing this but I am anyway” kind of sketchy.
But now with OIDC, I can securely assume AWS IAM roles in GitHub Actions without needing to store any AWS secrets in my repo. It’s like giving GitHub a VIP pass to AWS, but only for the right actions and repos.
Here’s how I set it up using CloudFormation and wired it into my workflow.
🛠️ Step 1: Set Up the OIDC Provider in AWS
First, we need to tell AWS about GitHub’s OIDC provider. This is like giving AWS a heads-up that GitHub is going to come knocking with an identity token. This is needed only once per AWS account, so this step can be skipped if it's already done.
In CloudFormation, define the OIDC provider:
1Resources:2 GithubOidcProvider:3 Type: AWS::IAM::OIDCProvider4 Properties:5 Url: https://token.actions.githubusercontent.com6 ClientIdList:7 - sts.amazonaws.com8 ThumbprintList:9 - '6938fd4d98bab03faadb97b34396831e3780aea1' # GitHub’s root CA thumbprint
🔐 Step 2: Create the IAM Role for GHA
a.k.a. Give GitHub the Keys, but Only a Copy
In CloudFormation, Define a role that GitHub Actions can assume via OIDC. The idea is: let GitHub knock on the AWS door, show its identity token, and get in — but only if it’s the right repo, branch, and vibes.
1Type: AWS::IAM::Role2Properties:3 RoleName: your-service-gha4 AssumeRolePolicyDocument:5 Version: 2012-10-176 Statement:7 - Effect: Allow8 Action: sts:AssumeRoleWithWebIdentity9 Principal:10 Federated: arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com11 Condition:12 StringEquals:13 token.actions.githubusercontent.com:aud: sts.amazonaws.com14 ForAnyValue:StringLike:15 token.actions.githubusercontent.com:sub:16 - repo:your-org/your-repo:ref:refs/heads/main17 - repo:your-org/your-repo:pull_request18 Policies:19 - PolicyName: your-service-gha20 PolicyDocument:21 Version: 2012-10-1722 Statement:23 - Sid: AllowECR24 Effect: Allow25 Action:26 - ecr:GetAuthorizationToken27 - ecr:BatchCheckLayerAvailability28 - ecr:GetDownloadUrlForLayer29 - ecr:PutImage30 - ecr:InitiateLayerUpload31 - ecr:UploadLayerPart32 - ecr:CompleteLayerUpload33 Resource: '*'
Customize this to restrict to specific ECR repos, S3 buckets, or even different branches.
📘 Helpful Docs:
⚙️ Step 3: Use the Role in GitHub Actions
In GHA workflow file:
1env:2 GITHUB_ACTIONS_ROLE_ARN: arn:aws:iam::123456789012:role/your-service-gha34jobs:5 deploy:6 runs-on: ubuntu-latest7 steps:8 - name: Configure AWS credentials9 uses: aws-actions/configure-aws-credentials@v410 with:11 role-to-assume: ${{ env.GITHUB_ACTIONS_ROLE_ARN }}12 aws-region: ap-northeast-1
Once the role is assumed, all subsequent aws
CLI or SDK commands in the job are authenticated with this role — no secrets required 🎉
🧠 Gotchas to Watch Out For
- Make sure GitHub’s OIDC provider is added to IAM
- Be super specific with
Condition
blocks to avoid privilege creep - OIDC tokens expire quickly — assume the role once per job
✅ Wrap Up
- Ditch long-lived AWS secrets in GitHub
- Use
sts:AssumeRoleWithWebIdentity
and GitHub’s OIDC token instead - Lock roles down to specific branches or repos for security
- Simpler, safer CI/CD setup 💪