Skip to main content
RunBook Academy

Git, CI/CD & GitOpsCVIII · Infrastructure-as-Code IntegrationTerraform

Terraform in the pipeline — plan as artefact, apply as gate

Advanced⏱ ~28 mingitterraform

What you'll learn

  • Explain why the Terraform plan is an artefact, not a side effect
  • Describe the role of remote state in a production pipeline
  • Identify the boundary between plan-time and apply-time credentials
  • Recognise drift and the three ways a pipeline can detect or prevent it

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Terraform belongs in the pipeline as a plan-then-apply gate. The plan is the artefact that CI produces, that policy engines inspect, that humans approve, and that the apply step consumes. Treating the plan as a side effect of the apply is the most common architectural mistake teams make with Terraform in CI/CD.

Where Terraform sits in the flow

Terraform runs in a pipeline job that talks to the cloud control plane through a provider. The pipeline is the only place Terraform should be invoked in production; an engineer’s laptop invoking terraform apply against production is exactly the workflow the rest of this course is designed to replace.

flowchart LR
    A["Git commit"] --> B["terraform fmt and validate"]
    B --> C["terraform plan -out=tfplan"]
    C --> D["Policy gate - OPA or Sentinel"]
    D --> E["Human approval on PR"]
    E --> F["terraform apply tfplan"]
    F --> G["Remote state update"]
    G --> H["Drift detection on schedule"]

Three properties of this flow are worth pulling out:

  • The plan file is the artefact. It is a binary representation of the exact set of changes Terraform intends to make. The apply reads the same file and performs the same changes - no re-interpretation, no re-reading of live state.
  • Plan and apply use different credentials. Plan-time credentials are read-only; apply-time credentials can write. This is why a PR can show a plan without the power to apply it.
  • State lives in a remote backend. S3 with DynamoDB locking, Terraform Cloud, GCS, or any backend that supports locking and versioning.

The plan command

The single most important command in the Terraform pipeline is the plan with -out:

terraform plan -out=tfplan

The -out flag writes the plan to a binary file on disk. The apply step then consumes that exact file:

terraform apply tfplan

Without -out, the plan is recomputed at apply time, and human-approved intent can silently change because live state moved in the seconds between plan and apply. With -out, the apply is a mechanical execution of the reviewed plan.

State, drift, and the third job

State lives in a remote backend with locking. The pipeline reads state on plan, writes state on apply, and locks during both.

Drift is what happens when the cloud state diverges from the recorded state - a manual console change, a stray terraform destroy, or a provider returning unexpected data. The pipeline can address drift in three ways:

  • Scheduled plan with no changes. A nightly terraform plan against production that alerts if the plan is non-empty.
  • Continuous reconciliation. Terraform Cloud’s drift detection polls resources and surfaces divergence.
  • Policy-as-code on data sources. OPA or Sentinel rules that reject changes contradicting a desired invariant.

A production pipeline picks at least one and treats empty-plan success as a first-class signal: the world matches the code.

Production discipline

The production framing of Terraform in a pipeline has three rules:

  1. The plan is the deliverable. A PR without a posted plan is a PR that cannot be reviewed. Wire the plan job to comment on the PR with the plan output as text, and store the -out file as a pipeline artefact.
  2. Credentials are scoped per phase. Plan-time credentials read; apply-time credentials write. The two must not be the same identity, and the apply credential should only be obtainable from the protected branch.
  3. State is versioned, locked, and encrypted. S3 with versioning and DynamoDB locking, or Terraform Cloud, or anything that gives you all three. A state file without locking is a state file that will be corrupted the first time two engineers run apply at once.

Cross-course references

  • Terraform for Production Sysadmins - Parts IX-XII (State) cover the state backend, locking, and migration in depth.
  • Linux for Production Sysadmins - Parts XXVIII-XXXI (Secrets) cover the credential scoping this lesson assumes.
  • Observability for Production Sysadmins - Parts XIX-XXII (Drift alerting) cover the monitoring side of scheduled plans.
  • Kubernetes for Production Sysadmins - Parts XI-XIV (GitOps controllers) cover plan-then-apply at the workload layer.

Quiz

Knowledge check · 4 questions

  1. Q1. What does `terraform plan -out=tfplan` produce in a production pipeline?

  2. Q2. A `terraform plan` saved with `-out` can be applied without re-reading the live state at apply time.

  3. Q3. Where should `terraform apply` run in a production pipeline?

  4. Q4. Diagnose a pipeline that applies Terraform without storing the plan, and prescribe the correction.

    A team has a CI job that runs `terraform plan` for human review and then runs `terraform apply -auto-approve` in a separate job. The two jobs share a remote state backend with locking, and the apply uses a deploy identity stored in the CI secret store. Six weeks in, an engineer notices that the staging environment has resources that were never shown in any PR plan. The audit trail shows what was applied but not what was reviewed.

Passing score: 75%. Answers are checked in this browser.