Skip to content

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> --delete

Two 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: read

Without 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> --delete

Confirming 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-identity

aws 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.

Last updated on