Terraform

Terraform CI/CD: Designing Robust plan/apply Automation

2026-04-19
NicheeLab Editorial Team

This article lays out the practical guidelines for embedding Terraform plan/apply into CI/CD so you get both clear change visibility and safe deployments.

We break down the key topics that the exam often tests — freezing the change plan, state locking, approval gates, and version pinning — in a form you can actually apply in the field.

Goals and Scope

The goal is to build a pipeline that freezes the Terraform plan output in a form a human can review, and then applies only that exact same plan to production. This guarantees that even if code or external state changes after review, the applied content cannot be swapped out.

The intended reader is an intermediate-to-advanced practitioner who already understands cloud privilege separation and the basics of running a remote backend. Terraform version management, the provider lock file, and remote backend locking are the foundations for everything that follows.

  • Pin Terraform and provider versions (commit .terraform.lock.hcl to VCS)
  • Manage state remotely with locking enabled (e.g., S3 + DynamoDB, azurerm, gcs)
  • CI generates and stores the plan; CD applies that exact plan after approval

Pipeline Design Principles

Always separate plan and apply into distinct phases, and have apply consume the stored plan file as-is. It is critical that the apply phase never re-runs plan.

Keep Terraform and provider versions identical between the CI and CD environments. Because a plan file depends on Terraform/provider versions and the OS, the entire workflow must assume version parity.

  • Immutability: emit the plan as a binary (-out) and apply only that exact file
  • Reproducibility: pin Terraform/provider versions and commit the lock file
  • Safety: -input=false, least-privilege service principals, remote state with locking
  • Observability: use -detailed-exitcode to detect changes programmatically, and store plan as an artifact
  • Approval: place apply downstream of a human review, and control permissions and concurrency per environment

Baseline pipeline that separates plan from apply

Devcommit / pushCIfmt / init / validate / planPlan Artifactplan.bin (review)CDapply plan (after approval)CI reads from Remote State (with lock); CD applies after Manual Approval

Reference Implementation: Minimal CI/CD Staging

The following is a minimal example that proceeds in order: 1) static checks, 2) initialization, 3) validation, 4) saving the plan, 5) manual approval, 6) applying the plan. We express it as shell steps so it stays independent of any specific CI/CD product.

Inject backend configuration and sensitive variables from the CI secret store or environment variables, and disable interactivity with -input=false.

  • Save the plan as plan.bin and hash it so you can detect tampering
  • Apply by passing plan.bin directly (terraform apply plan.bin); never regenerate the plan
  • Use -detailed-exitcode to skip the apply stage when there are no diffs

CI/CD reference script (pseudo-example)

# CI: 計画の生成
set -euo pipefail

# 1) フォーマットと静的検査
terraform fmt -check -recursive
terraform init \
  -input=false

# 2) バリデーション
terraform validate -no-color

# 3) ワークスペース/環境の選択
# export TF_WORKSPACE=staging
# terraform workspace select "$TF_WORKSPACE" || terraform workspace new "$TF_WORKSPACE"

# 4) 変更計画の作成(詳細終了コードで差分検出)
set +e
terraform plan \
  -out=plan.bin \
  -lock=true \
  -input=false \
  -no-color \
  -detailed-exitcode
rc=$?
set -e

if [ "$rc" -eq 0 ]; then
  echo "差分なし。apply は不要"; exit 0
elif [ "$rc" -eq 2 ]; then
  echo "差分あり。plan.bin を保存して承認待ちへ";
  sha256sum plan.bin > plan.bin.sha256
  # ここで CI のアーティファクトに plan.bin とハッシュを保存
else
  echo "plan 失敗"; exit $rc
fi

# --- 手動承認ゲート(人のレビュー)---

# CD: plan の適用(同一バージョンの Terraform/Provider 前提)
sha256sum -c plan.bin.sha256
terraform init -input=false
terraform apply -input=false -auto-approve plan.bin

Environment Separation and Approval Flow

Separate environments via directory splits (state separated per env directory), workspaces (one configuration, multiple states), or repository splits. In every case, state must be physically separated, and locking must be in place so concurrent apply runs cannot interfere with one another.

Center review and approval on the plan's visibility. The review target is not just the HCL diff but also the plan's summary of resource adds, changes, and deletes. Post-approval apply must use the plan.bin generated at approval time — never re-run plan immediately before apply.

  • Directory split: envs/prod, envs/stg, etc. — the most intuitive option, with clearly separated state
  • Workspaces: a fit when you want to switch tenants/environments while keeping the same configuration
  • Approval gate: human approval, RBAC, recorded change rationale, and hash verification of the plan artifact

State Management, Locking, and Concurrency Control

Remote backends give you centralized state management and locking. Major backends — S3 + DynamoDB locking, azurerm, gcs — all ship with a locking mechanism. -lock defaults to true, but in CI/CD it's worth setting it explicitly to make intent clear.

Apply concurrency control at two layers. First, the backend lock. Second, CI/CD queuing that serializes apply runs against the same environment. Together, they prevent state corruption from parallel apply runs. Parallelism across separate environments and separate states becomes much easier to allow.

  • Design so that terraform init -migrate-state is unnecessary unless the backend itself changes
  • Reference outputs from other states via data "terraform_remote_state" so dependencies stay explicit
  • Handle rollback via Git revert plus a fresh plan/apply — avoid editing state directly

Operational Pattern Comparison and Exam Prep Points

Terraform automation broadly splits into DIY general-purpose CI/CD, Terraform Cloud/Enterprise VCS integration, and API-driven execution. Compare them on state management, policy, approval UX, and cost when choosing.

From an exam perspective, frequently tested topics include plan freezing and apply separation, remote state and locking, version pinning, least privilege, the meaning of -detailed-exitcode, and where policy enforcement (such as Sentinel) fits in.

  • Humans review the plan; machines execute apply faithfully
  • Applying the identical plan makes "what was reviewed" and "what was applied" match exactly
  • Policies like Sentinel/OPA are evaluated at the plan stage or as a pre-apply gate
AspectGeneral-purpose CI/CD (DIY)Terraform Cloud: VCS-integrated runsTerraform Cloud: API/CLI-driven
State management / lockingSelf-configured S3+DynamoDB / azurerm / gcs, etc.TFC manages it (locking built in)TFC manages it (locking built in)
Plan storage / approvalCI artifact storage plus manual approvalReview and approve plans in the UI (policy checks integrate)Fetch plans via API and externalize the approval flow
PolicyIntegrate OPA/conftest, etc. (operated separately)Sentinel (edition-dependent)Sentinel (edition-dependent)
Execution environment setupMaintain Terraform/provider version pinning yourselfTFC handles version management (per workspace setting)TFC handles version management (per workspace setting)
Permissions and secretsConnect to the cloud via OIDC/short-lived credentials (managed CI-side)Managed safely via workspace variablesManaged safely via workspace variables
Learning / operational costFlexible but heavy initial build and maintenance burdenUI and guardrails make operations easyFlexible integration, but you need to design the API surface

Check Your Understanding

Pro

問題 1

A team wants to embed Terraform changes into CI/CD. Which approach most strongly guarantees that, even if code or external state changes after review, no change other than what was approved will be applied to production?

  1. Save the plan as a binary with -out and apply that file after approval. Do not regenerate the plan at apply time.
  2. Always re-run plan immediately before apply, and apply only if there are no diffs.
  3. Save the text output of plan and re-run apply with the same command line.
  4. Branch protection on main is sufficient; separating plan and apply is unnecessary.

正解: A

Applying the stored plan file as-is is the strongest way to guarantee that what was approved at review time and what gets applied are identical. If you re-run plan right before apply, code or external state changes can alter the plan.

Frequently Asked Questions

Can a plan file be applied in any environment?

No. A plan file depends on the exact Terraform core version, provider versions, and OS/architecture. Apply it only against the same version set, the same configuration, and the same remote state. Do not treat a plan as something you can ship across environments generically.

Should production apply be fully automated (unattended)?

The recommended pattern is a human approval gate. Automated apply after manual approval is common, but combine -auto-approve with RBAC and audit logs so you have a record of who approved what.

How should sensitive variables be passed?

Use the CI/CD secret store or Terraform Cloud's sensitive variables. Avoid passing secrets in plain text via -var. Inject them as TF_VAR_name environment variables or encrypted tfvars files, and never commit secrets to the repository.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Terraform

HCL Syntax: Terraform's Configuration Language (2026)

HCL2 fundamentals for Terraform — blocks, attributes, expres...

Terraform

Terraform Authoring & Operations Pro: Complete Guide (2026)

Tactics for the Terraform Pro exam — module authoring, works...

Terraform

Terraform Providers: Plugin Management Fundamentals (2026)

Provider mechanics — required_providers, versions, mirrors, ...

Terraform

Terraform Resource Blocks: Declarative Infra Units (2026)

Resource block fundamentals — addresses, references, common ...

Terraform

Terraform Data Sources: Read-Only External Data (2026)

Data source basics — declaration, refresh behavior, dependen...

Browse all Terraform articles (102)
© 2026 NicheeLab All rights reserved.