linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/how/tf-oidc-aws

how/cicd

OIDC: GitHub Actions into AWS without long-lived keys

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:

  • the keys live for years and almost never get rotated;
  • they belong to an IAM user with admin rights (often);
  • a leak of one access key means login to AWS as that user's root;
  • action logs can print them by accident, especially through a broken third-party action.

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.

step 1/6·00 · classic: keys in GitHub Secrets
GITHUB ACTIONSci runnerAWS · IAM + STScloudGITHUB SECRETSAWS_ACCESS_KEY_IDAKIA… · never rotatedAWS_SECRET_ACCESS_KEYживёт годами, лежит в envриски: утечка в логе, лог-action, форккомпрометация = вход в AWS как rootIAM USER · tf-ciaccess key #1created 2 years agopermissions: AdministratorAccessпостоянный credential, blast-radius огроменнаивно: долгоживущие AWS access keys в GitHub Secrets

§ steps

  1. 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:

    yaml
    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:

    • the key lives for years. If it leaked a year ago through a log action, nobody knows, but an attacker is already inside.
    • rotation is a separate process with a human in the loop. Usually nobody does it.
    • the user is usually given broad rights, because it is too much trouble to figure out what exactly Terraform needs.

    That is why in production this scheme is considered legacy.

recap

What to remember:

  • The 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.
  • The 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.
  • AWS returns STS temp credentials with a TTL of up to 12 hours (by default one hour, capped by the role's maximum). After the TTL they stop working. No rotation on your side.
  • The IAM role's permissions should be narrow: exactly what the Terraform project changes. No AdministratorAccess. If CI is compromised, the blast radius is limited to this role.
  • The same pattern works with GCP (Workload Identity Federation), Azure (Workload Identity), HashiCorp Vault (JWT auth). GitHub OIDC is the OpenID Connect standard, not AWS-specific.
  • You can move from the old scheme to OIDC gradually: set up the new role, change the workflow, delete the access keys after the first successful run. No all-or-nothing.

Next: [[tf-policy-gate|Policy gate]] on how to tighten security further with a policy engine between plan and apply.

§ dig into the knowledge base

  • tf-oidc-awsOIDC: setting up trust in IAM
  • aws-providerAWS provider: assume_role and creds
  • tf-plan-apply-ciPlan/Apply pipeline on GitHub Actions
  • tf-policy-as-codePolicy as Code: an extra gate

§ try it hands-on

  • ›tf-production-09-oidc- OIDC: GitHub Actions without long-lived keys
  • ›tf-production-08-github-actions- Production pipeline on GitHub Actions
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies