Assuming AWS Role From a Workflow
Last chapter built the AWS side: an OIDC provider that trusts GitHub, and a role scoped to your repository’s main branch with exactly the permissions the S3 deploy needs. Now it’s time to actually use it — modifying the real deploy workflow from the previous section, confirming it still works, and then deleting the access-key secrets it used to depend on.
What The Workflow Looked Like Before
The previous section’s S3 deploy job authenticated the way every deploy job in that section did: an access key and secret key, pulled from repository secrets, handed to aws-actions/configure-aws-credentials.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: <YOUR_AWS_REGION>
- name: Deploy to S3
run: aws s3 sync ./dist s3://<YOUR_S3_BUCKET_NAME> --deleteTwo repository secrets, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, made this work — and both are risky long-lived credentials that keep working until someone manually revokes them.
The Change: Three Edits, No New Secrets
Getting this workflow onto OIDC takes three specific changes, and notably, adds zero new repository secrets.
1. Grant the job permission to request an OIDC token. By default, a job can’t ask GitHub for an identity token at all — that ability is itself gated behind a permissions: scope:
permissions:
id-token: write
contents: readWithout id-token: write here, configure-aws-credentials has no token to hand to AWS, and the step fails immediately with a credentials error — that’s usually the first thing to check if this stops working later.
2. Replace the access-key inputs with role-to-assume. This is the actual authentication swap. configure-aws-credentials uses OIDC automatically when you give it role-to-assume without also giving it aws-access-key-id:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/github-actions-deploy-role
aws-region: <YOUR_AWS_REGION>That role ARN is the exact role you created in previous chapter — the one whose trust policy only accepts tokens whose sub claim matches repo:<YOUR_GITHUB_ORG>/<YOUR_REPO_NAME>:ref:refs/heads/main.
3. Leave the deploy step itself untouched. This is worth noticing: aws s3 sync doesn’t change at all. configure-aws-credentials exports temporary credentials as environment variables the same way it always did — every later step in the job keeps working exactly as before, because as far as the AWS CLI is concerned, credentials are credentials. Only how they got there changed.
Put together, here’s the full workflow:
name: Deploy to S3
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/github-actions-deploy-role
aws-region: <YOUR_AWS_REGION>
- name: Deploy to S3
run: aws s3 sync ./dist s3://<YOUR_S3_BUCKET_NAME> --deleteConfirming it Actually Works
Before trusting this, verify the role assumption succeeded rather than assuming it did. A one-line addition right after the credentials step makes this explicit in your logs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/github-actions-deploy-role
aws-region: <YOUR_AWS_REGION>
- name: Confirm identity
run: aws sts get-caller-identityaws sts get-caller-identity prints back exactly which role the current credentials belong to. Push a small change to main, watch the workflow run, and check that step’s output — it should show an assumed-role ARN referencing github-actions-deploy-role, not an IAM user. Once you’ve confirmed the ARN is correct, feel free to remove that step; it was only there to prove the switch worked.
If it fails instead, the error almost always points at one of two things: id-token: write missing from the permissions: block, or the trust policy’s sub condition not matching — double-check the branch you pushed to against what chapter 61’s trust policy actually allows.
Closing The Loop: Deleting The Old Keys
This is the step past few chapters were building toward, and it’s easy to skip once the workflow is working — but skipping it defeats most of the point. The access keys sitting in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are still fully valid AWS credentials right now, whether or not anything still uses them.
In the IAM console, find the IAM user those keys belonged to, open its Security credentials tab, and deactivate — then delete — both access keys. If nothing else in your AWS setup still depends on that IAM user, delete the user itself too.
aws iam list-access-keys --user-name <YOUR_IAM_USER_NAME>
aws iam update-access-key \
--user-name <YOUR_IAM_USER_NAME> \
--access-key-id <ACCESS_KEY_ID> \
--status Inactive
aws iam delete-access-key \
--user-name <YOUR_IAM_USER_NAME> \
--access-key-id <ACCESS_KEY_ID>Then remove the now-unused secrets from your repository: Settings → Secrets and variables → Actions, delete AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. If your deploy workflow still runs clean after this — and it will, since it no longer references either secret — that’s your confirmation the migration is actually complete, not just working alongside a credential you forgot to remove.
Warning
Deactivate the keys before deleting them, and push a deploy to confirm everything still works, before deleting the IAM user itself. Deactivating is instantly reversible if something unexpected still depends on those keys; deleting the user is not.
Applying The Same Pattern To The ECR Push
The previous section’s practical on ECR push workflow authenticated the identical way — access keys from repository secrets, handed to configure-aws-credentials. The mechanics of switching it over are exactly what you just did here, not a new technique: add permissions: id-token: write, swap the access-key inputs for role-to-assume pointed at the same role from the last chapter (or a second, more narrowly scoped role if you’d rather ECR and S3 not share one), and delete the corresponding IAM user’s keys once you’ve confirmed the push still works.
Worth doing yourself as practice rather than copying a second full walkthrough — by this point you have everything you need: the role ARN, the permission block, and the one-line swap in configure-aws-credentials’s inputs.