Storing AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in GitHub secrets gets the deployment working, but it creates a permanent credential outside AWS. Then come rotation, forgotten consumers, and the healthy question of which job is still using the old key.
OIDC replaces that arrangement with a temporary credential: the job receives a token from GitHub, presents it to AWS STS, and assumes an IAM role. There is no long-lived access key in the repository, and AWS accepts the token only when aud and sub match what the trust policy authorized.
The result we will build is a pipeline with:
- GitHub's OIDC provider registered in the AWS account;
- an IAM role restricted to the repository and the production environment;
- a permissions boundary limiting that role's ceiling;
aws-actions/configure-aws-credentials@v6.2.3in the workflow;- tests that distinguish authentication failures from authorization failures.
Why a long-lived key is an operational risk
An IAM user access key is a long-lived credential made up of an ID and a secret. The secret appears only once when the key is created. If it is lost, there is no button to reveal it again: you must delete the key and create another one.
Each IAM user can have at most two access keys. That is why rotation usually follows this sequence:
- create the second key;
- update every consumer;
- verify that no one is using the first key;
- delete the first key.
If a workflow, script, or secret in another repository gets left behind in step 3, removal becomes an outage. If the key leaks, it remains valid until it is revoked.
The IAM security best practices documentation itself recommends temporary credentials and roles for workloads. With OIDC, the permanent credential disappears from GitHub: each job gets a temporary session for that deployment.
Prerequisites and names used
You need enough administrative permission to create an OIDC provider, policies, and an IAM role. In the examples, replace these values with your own:
- AWS account:
111122223333; - organization:
octo-org; - repository:
octo-repo; - bucket:
meu-site-prod; - CloudFront distribution:
E123EXAMPLE; - region:
us-east-1.
The role will be named gha-deploy-prod. Avoid the name GitHubActions, as noted by the action's own project. A specific name also helps when the ARN appears in STS.
The combination used here is the issuer https://token.actions.githubusercontent.com, audience sts.amazonaws.com, and