Secretless CI/CD with GitHub Actions OIDC
Managing AWS credentials for CI/CD pipelines can quickly become complex and risky. Hardcoding AWS credentials or storing them as GitHub Secrets exposes your organization to credential leakage, auditability challenges, and significant security risks. Similarly, statically binding IAM roles to runners complicates scaling, limits flexibility, and creates management overhead — especially in multi-account environments. In this tutorial, you'll learn how to securely manage AWS access from GitHub Actions using AWS Attribute-Based Access Control (ABAC), GitHub Actions OIDC tokens, and Amazon Cognito as an identity broker — all without static credentials or brittle IAM bindings. This model maps OIDC claims from your CI/CD workflows to a short-lived AWS STS token. This model can be extended to any service provider that you need to connect to from your CI/CD workflows, as long as the service provider is OIDC compliant and supports Identity Federation. Why Avoid Hardcoded Credentials and Static IAM Bindings? Security Risks: Hardcoded secrets can leak through logs, repos, or misconfigurations. Auditability Issues: Static credentials obscure who performed actions and when. Operational Bottlenecks: Static role-to-runner bindings make it hard to scale access across apps or environments. If you’re scaling GitHub Actions across multiple teams, apps, or accounts — you need a better way. A least privileged access model along with dynamic infrastructure scaling. Secure, Scalable Architecture Overview This architecture connects GitHub Actions to AWS securely using temporary session credentials based on identity claims and session tags. GitHub Actions → [OIDC Token] ↓ Cognito Identity Pool → [Maps Claims → Principal Tags] ↓ AWS STS → [AssumeRoleWithWebIdentity → Session with Tags] ↓ AWS Resources (enforced via ABAC) Step-by-Step Setup 1. Create an OIDC Identity Provider for GitHub Resources: GitHubOIDCProvider: Type: AWS::IAM::OIDCProvider Properties: Url: https://token.actions.githubusercontent.com ClientIdList: - "sts.amazonaws.com" ThumbprintList: - "6938fd4d98bab03faadb97b34396831e3780aea1" You can also configure this manually via the IAM Console → Identity Providers. 2. Configure Cognito Identity Pool Map OIDC claims from GitHub into principal tags. Resources: IdentityPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: GitHubActionsABAC AllowClassicFlow: true OpenIdConnectProviderARNs: - !GetAtt GitHubOIDCProvider.Arn PrincipalTags: repository: "repo:repository" environment: "repo:environment" workflow: "repo:job_workflow_ref" IdentityPoolRoleAttachment: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref IdentityPool Roles: authenticated: !GetAtt ABACRole.Arn 3. Define the ABAC IAM Role This IAM role uses session tags for authorization. Resources: ABACRole: Type: AWS::IAM::Role Properties: RoleName: GitHubActionsABACRole AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: cognito-identity.amazonaws.com:aud: !Ref IdentityPool Policies: - PolicyName: ABACDynamicAccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: "*" Resource: "*" Condition: StringEquals: aws:ResourceTag/Repository: "${aws:PrincipalTag/repository}" 4. GitHub Actions Workflow with OIDC → Cognito name: ABAC Deployment on: [push] permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - name: Configure AWS Credentials via Cognito uses: catnekaise/cognito-idpool-auth@v2 with: auth-flow: enhanced cognito-identity-pool-id: ${{ secrets.COGNITO_POOL_ID }} aws-region: us-west-2 set-in-environment: true - name: Deploy Resources run: | aws s3api create-bucket --bucket my-bucket \ --tagging "Repository=${{ github.repository }}" # ✅ Allowed: bucket tagged correctly aws s3 ls s3://my-bucket # ❌ Denied: bucket with incorrect tag aws s3 ls s3://other-bucket How It Works: Identity and Tags in Action GitHub OIDC Claims: { "repository": "org/my-repo", "environment": "prod", "job_workflow_ref": ".github/workflows/deploy.yml" } These map into: Cognito Principal Tags: repository, environment, workflow AWS Se

Managing AWS credentials for CI/CD pipelines can quickly become complex and risky. Hardcoding AWS credentials or storing them as GitHub Secrets exposes your organization to credential leakage, auditability challenges, and significant security risks. Similarly, statically binding IAM roles to runners complicates scaling, limits flexibility, and creates management overhead — especially in multi-account environments.
In this tutorial, you'll learn how to securely manage AWS access from GitHub Actions using AWS Attribute-Based Access Control (ABAC), GitHub Actions OIDC tokens, and Amazon Cognito as an identity broker — all without static credentials or brittle IAM bindings. This model maps OIDC claims from your CI/CD workflows to a short-lived AWS STS token. This model can be extended to any service provider that you need to connect to from your CI/CD workflows, as long as the service provider is OIDC compliant and supports Identity Federation.
Why Avoid Hardcoded Credentials and Static IAM Bindings?
Security Risks: Hardcoded secrets can leak through logs, repos, or misconfigurations.
Auditability Issues: Static credentials obscure who performed actions and when.
Operational Bottlenecks: Static role-to-runner bindings make it hard to scale access across apps or environments.
If you’re scaling GitHub Actions across multiple teams, apps, or accounts — you need a better way. A least privileged access model along with dynamic infrastructure scaling.
Secure, Scalable Architecture Overview
This architecture connects GitHub Actions to AWS securely using temporary session credentials based on identity claims and session tags.
GitHub Actions → [OIDC Token]
↓
Cognito Identity Pool → [Maps Claims → Principal Tags]
↓
AWS STS → [AssumeRoleWithWebIdentity → Session with Tags]
↓
AWS Resources (enforced via ABAC)
Step-by-Step Setup
1. Create an OIDC Identity Provider for GitHub
Resources:
GitHubOIDCProvider:
Type: AWS::IAM::OIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- "sts.amazonaws.com"
ThumbprintList:
- "6938fd4d98bab03faadb97b34396831e3780aea1"
You can also configure this manually via the IAM Console → Identity Providers.
2. Configure Cognito Identity Pool
Map OIDC claims from GitHub into principal tags.
Resources:
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: GitHubActionsABAC
AllowClassicFlow: true
OpenIdConnectProviderARNs:
- !GetAtt GitHubOIDCProvider.Arn
PrincipalTags:
repository: "repo:repository"
environment: "repo:environment"
workflow: "repo:job_workflow_ref"
IdentityPoolRoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
authenticated: !GetAtt ABACRole.Arn
3. Define the ABAC IAM Role
This IAM role uses session tags for authorization.
Resources:
ABACRole:
Type: AWS::IAM::Role
Properties:
RoleName: GitHubActionsABACRole
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated: cognito-identity.amazonaws.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
cognito-identity.amazonaws.com:aud: !Ref IdentityPool
Policies:
- PolicyName: ABACDynamicAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "*"
Resource: "*"
Condition:
StringEquals:
aws:ResourceTag/Repository: "${aws:PrincipalTag/repository}"
4. GitHub Actions Workflow with OIDC → Cognito
name: ABAC Deployment
on: [push]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials via Cognito
uses: catnekaise/cognito-idpool-auth@v2
with:
auth-flow: enhanced
cognito-identity-pool-id: ${{ secrets.COGNITO_POOL_ID }}
aws-region: us-west-2
set-in-environment: true
- name: Deploy Resources
run: |
aws s3api create-bucket --bucket my-bucket \
--tagging "Repository=${{ github.repository }}"
# ✅ Allowed: bucket tagged correctly
aws s3 ls s3://my-bucket
# ❌ Denied: bucket with incorrect tag
aws s3 ls s3://other-bucket
How It Works: Identity and Tags in Action
GitHub OIDC Claims:
{
"repository": "org/my-repo",
"environment": "prod",
"job_workflow_ref": ".github/workflows/deploy.yml"
}
These map into:
Cognito Principal Tags: repository, environment, workflow
AWS Session Tags: Used in IAM policies to enforce access
Real-World Scenario: Multi-Account, Multi-App
ABAC lets you scale your platform securely:
Multi-account CI/CD: Assign different roles for dev/stage/prod accounts.
Shared runners: Let multiple apps use the same runners — securely and in isolation.
No over-permissioning: Runners don’t get ambient access to resources.
GitHub Actions
↓
Cognito Identity Pool
├─ Dev Role → Dev Resources
└─ Prod Role → Prod Resources
Benefits Recap
- No long-lived AWS credentials in GitHub
- Least-privilege access per job, repo, or environment
- Clean audit trail — every access mapped to identity
- Flexible scaling for CI/CD across teams and cloud accounts.
Further Reading
AWS ABAC Overview
GitHub Actions OIDC
AWS Cognito Identity Pools
Secure your GitHub Actions pipelines the right way — with identity, not secrets.