Terraform in CI/CD

Automate plan and apply in a pipeline so infrastructure changes go through review like any code.

Running Terraform in a CI/CD pipeline turns infrastructure changes into reviewable, auditable pull requests.

A typical pipeline

  1. On a pull request: run fmt -check, validate, and plan, then post the plan for reviewers.
  2. On merge to main: run apply -auto-approve to roll out the change.

Requirements

  • Remote state with locking so parallel runs are safe.
  • Credentials injected securely (OIDC roles beat long-lived keys).
  • -input=false so commands never block waiting for a prompt.

Example

Example Β· bash
# Steps a CI job typically runs (non-interactive)
terraform init -input=false
terraform fmt -check -recursive
terraform validate
terraform plan -input=false -out=tfplan

# On the main branch only, after review:
terraform apply -input=false -auto-approve tfplan

When to use it

  • A GitHub Actions workflow posts the terraform plan output as a pull-request comment so reviewers can see what will change before approving the merge.
  • A GitLab CI pipeline runs terraform apply on the main branch only after a plan succeeds and a manual approval gate is passed in the pipeline UI.
  • An Atlantis server runs plan on every PR and apply on merge, ensuring every infrastructure change is reviewed and applied automatically from Git events.

More examples

GitHub Actions plan on PR

A GitHub Actions workflow that initializes Terraform and runs plan on every pull request targeting main.

Example Β· yaml
name: Terraform Plan
on:
  pull_request:
    branches: [main]

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.9.0"
      - run: terraform init
      - run: terraform plan -no-color
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Apply only on main merge

Triggers terraform apply automatically when code is merged to main, completing the plan-on-PR / apply-on-merge pattern.

Example Β· yaml
name: Terraform Apply
on:
  push:
    branches: [main]

jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform apply -auto-approve
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Save and apply plan file in CI

Demonstrates saving the plan to a file in the plan stage and applying that exact file in the apply stage for deterministic CI deployments.

Example Β· bash
# In the plan job:
terraform plan -out=tfplan

# Upload tfplan as a CI artifact, then in the apply job:
# Download the artifact, then:
terraform apply tfplan
# Applies exactly the diff that was reviewed β€” no surprises

Discussion

  • Be the first to comment on this lesson.
Terraform in CI/CD β€” Terraform | SoundsCode