While being mentored on GitHub Actions recently, my mentor told me to use OIDC to authenticate to AWS. I honestly had no idea you could authenticate without a static access key, so I dug deeper.
It turns out GitHub OIDC lets your workflows get short-lived AWS credentials for each run instead of storing long-lived access keys. It's more secure, and there are no credentials to rotate or manage.
Here's what I learned setting it up.
Prerequisites
- An AWS account with permission to create IAM roles and identity providers
- A GitHub repository with GitHub Actions enabled
- Basic familiarity with GitHub Actions workflows
What you need
- OIDC Provider: lets AWS trust GitHub's tokens
- IAM Role: the identity your workflow assumes
- Trust Policy: who can assume the role
- Permission Policy: what the role can do
- GitHub Actions workflow: assumes the role
Setup
1. Create the OIDC Provider
In the AWS Console, go to IAM → Identity providers → Add provider and fill in:
- Provider type: OpenID Connect
-
Provider URL:
https://token.actions.githubusercontent.com -
Audience:
sts.amazonaws.com
You only need to do this once per AWS account. Every role your workflows use can share it.
2. Create the IAM Role and Trust Policy
Go to IAM → Roles → Create role, choose Web identity, and select the provider you just created with the sts.amazonaws.com audience.
The trust policy controls which GitHub repo and branch can assume this role. It should look like this:
in custom trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<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:<GITHUB_ORG>/<REPO_NAME>:ref:refs/heads/main"
}
}
}
]
}
Don't use a wildcard like
repo:*in thesubcondition. That would let workflows from other repos assume your role.
3. Attach a Permission Policy
Attach a policy with only the permissions your workflow needs. For example, to let it list and upload files to one S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
}
]
}
After creating the role, open it in IAM → Roles and copy the ARN from the Summary section. It looks like arn:aws:iam::123456789012:role/github-actions-role.
4. Set Up the GitHub Actions Workflow
Create .github/workflows/aws-oidc.yml:
name: AWS OIDC Demo
on:
push:
branches: [main]
permissions:
id-token: write # required to request the OIDC token
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME> # your Role ARN
aws-region: ap-southeast-1
- name: Check identity
run: aws sts get-caller-identity
Push to main, and if the get-caller-identity step shows your role's ARN, it worked. No access keys stored anywhere.
Wrapping up
Instead of storing access keys in GitHub secrets, your workflow now gets temporary credentials that expire after the run. Less to leak, nothing to rotate.



Top comments (0)