The Problem
Every DevOps engineer knows the credential management nightmare. AWS access keys stored in GitHub secrets. Rotation schedules nobody follows. That moment of panic when someone leaves the team and you realize they could still have access. And worst of all, giving CI/CD pipelines AdministratorAccess because figuring out exact permissions takes forever.
I needed a better way. Something secure, maintainable, and actually practical for real projects.
The Solution: GitHub Actions OIDC
What is OIDC?
OpenID Connect (OIDC) allows GitHub Actions to authenticate directly with AWS without storing any credentials. GitHub generates a signed JWT token for each workflow run, AWS verifies it came from GitHub, and grants temporary credentials that expire in minutes, not years.
The Authentication Flow
No secrets stored. No rotation needed. Just cryptographic proof that the request came from your GitHub repository.
Architecture Overview
I built a three-tier permission model:
- Global Environment: Contains OIDC provider and base IAM roles (manually managed)
- Environment Roles: Separate roles for dev, staging, and production
- Branch Restrictions: Production only from main, dev from any branch
Implementation Details
Step 1: Setting Up the OIDC Provider
The OIDC provider is the trust relationship between GitHub and AWS. Here’s my actual Terraform configuration:
|
|
The conditional creation prevents conflicts when multiple environments try to create the same provider. Create once, reference everywhere.
Step 2: Creating Environment-Specific IAM Roles
Each environment gets its own IAM role with tailored permissions:
|
|
The wildcard pattern (repo:owner/repo:*
) accepts any branch or tag. For production, you’d restrict to ref:refs/heads/main
.
Step 3: Configuring GitHub Actions Workflows
Here’s the workflow:
|
|
Notice: No AWS credentials anywhere. The role-to-assume
ARN is public information.
My Permission Discovery Method
The Trial-and-Error Approach That Actually Works
Instead of guessing permissions or using overly broad policies, I discovered exactly what my Terraform deployments need through systematic failure analysis:
|
|
The Discovery Process
- Start with minimal permissions (just S3 for state)
- Run terraform apply in GitHub Actions
- Read the error message: It tells you exactly what’s missing
- Add that permission to the IAM policy
- Deploy the updated policy (manual terraform apply in global environment)
- Re-run the failed job
- Repeat until success
Example error message that guides you:
|
|
Clear, actionable, and no guessing required.
Environment-Specific Security Controls
Different environments need different security postures:
|
|
Key Implementation Challenges & Solutions
Challenge 1: Docker & ECR Authentication
Docker builds need to push to ECR, but docker login expects credentials. Here’s how OIDC handles it:
|
|
The OIDC credentials are automatically used by AWS CLI to generate the docker login token.
Challenge 2: Managing Multiple Environments
I use Terraform workspaces for environments. Each workflow selects its workspace:
|
|
Benefits:
- Single S3 bucket for all environments
- Isolated state files prevent cross-environment conflicts
- Easy environment promotion with consistent workflows
Challenge 3: Avoiding Unnecessary Deployments
Don’t trigger infrastructure deploys for docs changes:
|
|
This saves CI/CD minutes and prevents infrastructure changes from documentation updates.
Results & Benefits
Security Improvements
Metric | Before OIDC | After OIDC | Improvement |
---|---|---|---|
Stored Credentials | ~8 secrets | 0 secrets | 100% reduction |
Credential Lifetime | Years/Never expire | 15 minutes | Effectively temporary |
Permission Scope | Often overly broad | Discovered via errors | Right-sized |
Audit Trail | Basic | CloudWatch with session IDs | Full traceability |
Rotation Burden | Manual quarterly | None needed | Zero maintenance |
Operational Benefits
Zero Credential Management
- No rotation schedules
- No panic when employees leave
- No secrets in developer environments
Precise Permissions
- Discovered through actual usage
- Environment-specific restrictions
- Branch-based access control
Complete Audit Trail
- Every action tagged with workflow run ID
- CloudWatch logs for compliance
- Clear attribution of all changes
Developer Experience
- No local AWS credentials needed
- Same workflows across all environments
- Clear error messages guide permission fixes
Cost Analysis
- Setup Time: 1 weekend (8 hours)
- Monthly Savings: 2-3 hours of credential management
- Security Value: Eliminated credential compromise risk
Lessons Learned
What Worked Well
The trial-and-error permission discovery.
Sounds hacky, but terraform errors are so clear that it’s actually faster than trying to predict permissions upfront.
Global environment pattern.
Having OIDC infrastructure separate from application infrastructure prevents circular dependencies.
Workspace-based environment isolation.
Simple, effective, and works with existing Terraform patterns.
What I’d Do Differently
Start with dev environment only.
Get the pattern working before adding staging and production complexity.
Document discovered permissions immediately.
I had to rediscover some permissions when setting up new services.
Consider AWS SSO for human access too.
OIDC solved CI/CD, but developers still use long-lived credentials locally.
Implementation Guide
Quick Start for Your Project
Create OIDC Provider (one-time setup)
|
|
Create IAM Role with trust relationship to GitHub
|
|
Update GitHub Workflow
|
|
Iterate on Permissions using the failure messages
Common Pitfalls to Avoid
- Don’t forget
id-token: write
permission in workflow - Thumbprints occasionally change (GitHub provides updates)
- Role session names help with CloudWatch debugging
- Start with wildcards, tighten after it works
Next Steps
This OIDC implementation is just the beginning. Future enhancements could include:
- Federated access for developers using GitHub as identity provider
- Cost allocation tags based on workflow metadata
- Automated permission discovery using CloudTrail analysis
- Multi-cloud support (Azure and GCP have similar OIDC capabilities)
“Security doesn’t have to be complex. Sometimes the simplest solution (stop storing secrets) is the best solution. OIDC proved that for me.”
Want to implement this in your infrastructure? Check out the complete code: terraform-playground on GitHub
Need help with your CI/CD security? Contact me for consultation.