Git, CI/CD & GitOpsCVIII · Infrastructure-as-Code IntegrationTerraform
Terraform in the pipeline — plan as artefact, apply as gate
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
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 planagainst 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:
- 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
-outfile as a pipeline artefact. - 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.
- 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
Q1. What does `terraform plan -out=tfplan` produce in a production pipeline?
Q2. A `terraform plan` saved with `-out` can be applied without re-reading the live state at apply time.
Q3. Where should `terraform apply` run in a production pipeline?
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.