A deployment key stored in CI is a credential with a long memory and a wide blast radius. OpenID Connect lets GitHub Actions exchange a signed workflow identity for short-lived AWS credentials instead.
Trust the workflow, not the repository name
The IAM trust policy should restrict the repository, branch or environment, and workflow subject. A role that trusts every workflow in an organization has simply moved the problem to a more expensive file.
The problem we inherited
The deployment workflow used one long-lived access key shared by several jobs. It had survived staff changes, lived in more than one secret store, and nobody could prove which pipeline still needed it. Rotation was treated as a risky event because the blast radius was unknown.
The first proposal was to rotate the key more often. That would have reduced exposure time but preserved the same design: a static secret copied into automation. We chose to remove the dependency instead of managing it more carefully.
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
steps:
- uses: aws-actions/configure-aws-credentials@v6.3.0
with:
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
aws-region: eu-west-1Keep deployment roles narrow
Separate build, artifact upload, and production deployment roles when their permissions differ. Review CloudTrail events for the assumed role and set a session name that points back to the workflow run.
Prove the old key is gone
After migration, search repository secrets, organization secrets, runner environments, and local documentation for the old access key. Revoke it and confirm a failed deployment cannot use it as a fallback.
The resolution
We added an OIDC trust limited to the production environment, gave the workflow a short-lived role, and separated read-only build access from deployment access. CloudTrail showed the workflow run in every assumed-role session, which made the next investigation much less archaeological.
The trade-off is that the trust policy is more precise and therefore easier to break when repository structure changes. We made that visible by testing the role assumption in pull requests and documenting the environment subject alongside the workflow.
Test the trust boundary, not just the happy path
Check the audience and subject conditions in the IAM trust policy, then test both an allowed production run and a denied pull-request or wrong-branch run. If the job uses a GitHub environment, protect that environment and make the trust subject match it. Keep id-token: write on the deployment job only; a build that never assumes AWS has no reason to mint an AWS-shaped identity.
Federation does not remove trust. It makes the trust decision explicit, reviewable, and short-lived, which is a much better place to have the argument.
The actual benefit of OIDC
The strongest credential rotation is deleting the credential class you no longer need.