how/cicd
The workflow gets a short JWT from GitHub, exchanges it for temporary STS creds, runs Terraform, and an hour later all of it just disappears. No access-key secrets.
The standard way to give CI access to AWS is to put
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY into GitHub Secrets.
This is bad for several reasons:
OIDC replaces this scheme with a short-lived exchange. The idea: GitHub can issue a JWT with information about the repository, branch, and workflow. AWS can trust that JWT (if you set up the trust) and return temporary credentials. None of the secrets live longer than a single CI run.
Hit ▶ to go step by step.
An IAM user tf-ci is created in AWS, it has an access key, and the key
is copied into GitHub Secrets. In the workflow:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}Terraform picks them up and goes to work with AWS. This works. And this is a problem:
That is why in production this scheme is considered legacy.
recap
What to remember:
audience in the trust policy is sts.amazonaws.com.
GitHub signs the JWT with this audience. AWS checks the
match: if the JWT has a different audience, it is rejected.sub in the JWT holds a string like
repo:my-org/my-repo:ref:refs/heads/main. The trust policy on the IAM
role usually restricts it to "only this repo and only this
branch." That way a PR from a fork gets no access.AdministratorAccess. If CI is
compromised, the blast radius is limited to this role.Next: [[tf-policy-gate|Policy gate]] on how to tighten security further with a policy engine between plan and apply.