Terraform upgrades can introduce breaking changes at three layers: the core binary, each provider, and modules. Gauging the impact comes down to constraint design, lock-file discipline, and safe state migration techniques.
This article distills the principles most likely to appear on the exam (around the Pro level) into procedures you can apply directly in production. It lays out concrete tactics for validating with minimal diffs and promoting to production reversibly and safely.
Breaking changes can happen at any of the core, provider, or module layers, but the frequency and blast radius differ. Start by isolating risk per layer and clarifying who owns version constraints and validation.
What matters most, both in practice and on the exam, is constraints via required_version and required_providers, leveraging .terraform.lock.hcl, and minimal-diff validation on a feature branch (scoping where init -upgrade is allowed).
| Target | Recommended version constraint example | Signals of a breaking change | Key points when upgrading |
|---|---|---|---|
| Terraform core | ~> 1.6 | Announcements of new types, meta-arguments, or behavior changes | Track conservatively within 1.x if no feature dependencies; confirm zero plan diff in CI. |
| Provider | ~> 5.0 (MAJOR pinned) | Attribute removal/deprecation, default changes causing replacements | init -upgrade on a feature branch, lock-diff review, avoid replacements via moved/state mv. |
| Module | ~> 3.4 | Input/output variable changes, for_each/address changes | Update gradually with pinned versions; add a compatibility layer at the wrapping side. |
Recommended version constraint example
terraform {
required_version = "~> 1.6"
required_providers = {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Pin MAJOR, allow only MINOR/PATCH
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
module "network" {
source = "appcorp/network/aws"
version = "~> 3.4"
# ...
}Always evaluate upgrades on a validation branch by minimizing lock-file and plan diffs, then promote stepwise. As a rule, main should not rewrite the lock file (readonly).
In CI, separate plan from apply and add a human gate. Design diff-driven review, e.g., requiring reviewers only when the lock file changes.
Flow from upgrade validation to production promotion
Example CI job (minimal-diff validation)
# Allow lock updates only on the validation branch
terraform init -upgrade
terraform validate
terraform plan -out=tfplan.bin
# Archive lock file and plan as artifacts
sha256sum .terraform.lock.hcl > lock.sha256
terraform show -json tfplan.bin > tfplan.json
# Reject lock updates on main
# terraform init -lockfile=readonly
.terraform.lock.hcl is created by the initial init and pins providers to exact versions and hashes. On upgrades, init -upgrade refreshes the candidates so the diff is explicit. Always commit the lock to VCS and make it part of review.
For offline use and reproducibility, configure provider mirrors and caches in the CLI configuration. When building across multiple platforms, append per-platform hashes via terraform providers lock -platform.
Example provider mirror in the CLI configuration (~/.terraformrc)
provider_installation {
filesystem_mirror {
path = "/opt/terraform-providers"
include = ["registry.terraform.io/hashicorp/*"]
}
direct {} # Fall back to the registry only when not in the mirror
}
Use moved blocks to avoid destructive replacements when renaming resources or restructuring addresses. Rename in code deliberately, confirm 'N to move' in plan, then apply.
For provider migrations or source-address changes, reconcile state with terraform state replace-provider. When a replacement is unavoidable, use terraform state mv to shrink the unit and localize the impact.
Combined example of moved blocks and state mv
terraform {
required_version = "~> 1.6"
}
# Old: resource "x_service_bucket" "main" -> New: resource "x_bucket" "primary"
moved {
from = x_service_bucket.main
to = x_bucket.primary
}
# CLI assistance (when migration is unavoidable)
# Back up the state
terraform state pull > state.backup.json
# Change the address
terraform state mv x_service_bucket.main x_bucket.primary
# Replace the provider source (example)
terraform state replace-provider registry.terraform.io/oldcorp/storage registry.terraform.io/newcorp/storage
Apply upgrades while securing reproducibility (pinned plan, pinned lock) and reversibility (preserving the previous binary, lock, and state). Resolving drift right before apply also helps stability.
The lowest-risk rollback is to use the exact Terraform binary and lock file from the previous release and restore the pre-apply state backup.
Minimal production rollback procedure (example)
# Preparation
terraform version > TF_VERSION.txt
terraform state pull > state.pre.json
terraform plan -out=tfplan.bin
sha256sum tfplan.bin > tfplan.sha256
# Apply
terraform apply tfplan.bin
# Rollback (if needed)
# 1) Switch to the previously used Terraform binary (e.g., via tfenv)
# 2) Restore the previous .terraform.lock.hcl
# 3) Push state.pre.json
terraform state push state.pre.json
Common breaking changes include replacements driven by default-value changes, type/validation changes from required to optional (or vice versa), address changes from renaming, and key shifts caused by changes to for_each or count expressions. Reading the plan carefully and using lifecycle wisely are critical for detection and avoidance.
Even when replacement is unavoidable, create_before_destroy can suppress downtime. Conversely, sloppy ignore_changes breeds drift; treat it as a time-bounded or interim measure until a permanent fix lands.
lifecycle example for avoiding/controlling replacement
resource "example_resource" "svc" {
name = var.name
immutable_a = var.immutable_a # Pin attributes suspected of default changes by setting explicit values
mutable_b = var.mutable_b
lifecycle {
create_before_destroy = true # Apply where possible to suppress downtime
ignore_changes = [
# Tolerate drift temporarily (remove after the permanent fix)
mutable_b
]
}
}
Pro
問題 1
You want to validate a provider major version while preserving production-equivalent reproducibility and assessing impact with the smallest possible diff. Which procedure is most appropriate?
正解: A
The recommended path is to run init -upgrade only on a validation branch and review the lock-file diff and plan. Applying directly on main or deleting the lock destroys reproducibility and invites unintended breaking changes. Refresh alone cannot determine whether breaking diffs exist.
Should .terraform.lock.hcl be committed? How do you handle multiple platforms?
Yes, you should commit it. It is the cornerstone of reproducibility and a key review artifact. To guarantee reproducibility across execution platforms, run terraform providers lock -platform=<list> on a validation branch to append per-platform hashes, then merge.
Renaming a resource looks like it will force a replacement. Can I migrate without destruction?
Add a moved block to your code and apply will safely shift the state address. For finer-grained control, combine it with terraform state mv. Either way, confirm the move is detected by plan before applying.
What should I watch out for when rolling back?
Use the exact Terraform binary and .terraform.lock.hcl from the previous release, and restore the state backup taken before apply. On main, strictly enforce terraform init -lockfile=readonly to prevent unintended lock updates.
Practice with certification-focused question sets
無料で問題を解いてみる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.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...