Terraform in CI/CD

Terraform · Advanced · 12 min

Applying from laptops doesn't scale; automation makes every change reviewed, logged and repeatable.

A typical pull-request workflow:

1. On every PR: terraform fmt -check, terraform init, terraform validate, linters (TFLint), security scanners (Trivy, Checkov), then terraform plan -out=tfplan, posting a plan summary to the PR. 2. Reviewers read the plan, not only the code. 3. On merge (or approval): terraform apply tfplan with the reviewed plan, or a fresh plan plus a manual approval gate.

Practical details:

  • Set TF_IN_AUTOMATION=1 (less chatty output) and -input=false so nothing waits for a prompt.
  • Credentials: use OIDC federation from the CI system (GitHub Actions, GitLab) to assume a cloud role, not long-lived access keys. Give plan jobs read-only roles and apply jobs the write role.
  • Serialize applies per state (CI concurrency groups) in addition to state locking.
  • Cache provider plugins (TF_PLUGIN_CACHE_DIR) to speed up init.
  • Policies as code (OPA/conftest on terraform show -json tfplan, Sentinel in HCP Terraform) can block forbidden changes such as public buckets.
  • Run a scheduled plan -detailed-exitcode to detect drift: exit code 2 means changes are pending.

Tools like HCP Terraform, Atlantis, Spacelift and env0 implement this workflow for you, including locking and PR comments.

Example

# .github/workflows/terraform.yml (core steps)
name: terraform
on: [pull_request]
permissions: {id-token: write, contents: read, pull-requests: write}
jobs:
  plan:
    runs-on: ubuntu-latest
    env: {TF_IN_AUTOMATION: "1"}
    defaults: {run: {working-directory: envs/prod}}
    steps:
      - uses: actions/checkout@v5
      - uses: hashicorp/setup-terraform@v3
      - uses: aws-actions/configure-aws-credentials@v4
        with: {role-to-assume: "arn:aws:iam::123456789012:role/tf-plan", aws-region: eu-west-1}
      - run: terraform fmt -check -recursive
      - run: terraform init -input=false
      - run: terraform validate
      - run: terraform plan -input=false -out=tfplan
      - run: terraform show -no-color tfplan > plan.txt   # post as a PR comment

Remember

  • Plan on PRs, review the plan, apply the saved plan on merge
  • OIDC short-lived credentials; read-only for plan, write for apply
  • -detailed-exitcode on a schedule detects drift

Watch out for

  • Storing long-lived admin cloud keys as CI secrets for Terraform.

Check yourself

  1. terraform plan -detailed-exitcode exits with code 2. What does that mean?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.hashicorp.com/terraform/cli/commands/plan