Terraform のアップグレードは、コア本体、各プロバイダ、モジュールの3層でそれぞれ破壊的変更が起こり得ます。影響を見極めるには、制約の設計、ロックファイルの運用、そしてステートの安全な移行手法が鍵になります。
この記事では、試験対策(Pro レベル相当)でも問われやすい原則を、実務でそのまま使える手順に落とし込みます。必要最小限の差分で検証し、可逆で安全に本番へ昇格するための具体策をまとめました。
破壊的変更は、Terraform コア、プロバイダ、モジュールのどこでも発生し得ますが、頻度と影響範囲は異なります。まずは層ごとにリスクを分離し、バージョン制約と検証の責務を明確化します。
実務および試験ともに重要なのは、required_version と required_providers による制約、.terraform.lock.hcl の活用、検証ブランチでの最小差分検証(init -upgrade の適用範囲を限定)です。
| 対象 | 推奨するバージョン制約例 | 破壊的変更の兆候 | アップグレード時の要点 |
|---|---|---|---|
| Terraform コア | ~> 1.6 | 新しい型・メタ引数・挙動変更のアナウンス | 機能依存がなければ 1.x 内で保守的に追随。CI で plan 差分ゼロ確認。 |
| プロバイダ | ~> 5.0(MAJOR 固定) | 属性の削除/非推奨化、デフォルト変更で置換が発生 | feature ブランチで init -upgrade、ロック差分レビュー、moved/state mv で置換回避。 |
| モジュール | ~> 3.4 | 入力/出力変数の変更、for_each/address 変更 | バージョン固定で段階的に更新。ラップ側で互換レイヤーを設ける。 |
推奨のバージョン制約例
terraform {
required_version = "~> 1.6"
required_providers = {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # MAJOR を固定し、MINOR/PATCH のみ許容
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
module "network" {
source = "appcorp/network/aws"
version = "~> 3.4"
# ...
}アップグレードは、必ず検証ブランチでロックファイル差分と plan 差分を最小化して評価し、段階的に昇格させます。main ではロックファイルを書き換えない(readonly)運用が基本です。
CI では、計画と適用を分離し、ヒューマンゲートを設けます。ロックファイルの変更がある場合のみ reviewers を必須化するなど、差分駆動での審査を設計します。
アップグレード検証から本番昇格までの流れ
CI ジョブ例(差分最小の検証)
# 検証ブランチのみロック更新を許可
terraform init -upgrade
terraform validate
terraform plan -out=tfplan.bin
# ロックファイルと plan のアーティファクト化
sha256sum .terraform.lock.hcl > lock.sha256
terraform show -json tfplan.bin > tfplan.json
# main ではロック更新を拒否
# terraform init -lockfile=readonly
.terraform.lock.hcl は初回 init で作成され、プロバイダの厳密なバージョンとハッシュを固定します。アップグレード時は init -upgrade により候補が更新され、差分が明確化されます。ロックは必ず VCS にコミットし、レビュー対象にします。
オフラインや再現性のため、CLI 設定でプロバイダのミラーやキャッシュを使います。複数プラットフォームでビルドする場合は、terraform providers lock -platform で各プラットフォームのハッシュを追記します。
CLI 設定でのプロバイダミラー例 (~/.terraformrc)
provider_installation {
filesystem_mirror {
path = "/opt/terraform-providers"
include = ["registry.terraform.io/hashicorp/*"]
}
direct {} # ミラーにない場合のみレジストリへ
}
リソース名の変更やアドレス体系の変更は、moved ブロックを使うことで破壊的置換を避けられます。コード上の rename を計画的に行い、plan で「1 to move」等の表示を確認してから適用します。
プロバイダの移行やソースアドレス変更では、terraform state replace-provider を用いて state を整合させます。どうしても計画に置換が出る場合でも、terraform state mv で単位を小さくし、影響を局所化します。
moved ブロックと state mv の併用例
terraform {
required_version = "~> 1.6"
}
# 旧: resource "x_service_bucket" "main" → 新: resource "x_bucket" "primary"
moved {
from = x_service_bucket.main
to = x_bucket.primary
}
# CLI での補助(どうしても移行が必要な場合)
# state のバックアップ
terraform state pull > state.backup.json
# アドレス変更
terraform state mv x_service_bucket.main x_bucket.primary
# プロバイダ出所の置換(例)
terraform state replace-provider registry.terraform.io/oldcorp/storage registry.terraform.io/newcorp/storage
アップグレードの適用は、再現可能性(plan の固定、ロックの固定)と可逆性(直前のバイナリ・ロック・state の保全)を確保して行います。apply 直前の drift 解消も安定化に有効です。
ロールバックは、直前に用いた Terraform バイナリとロックファイルをそのまま使用し、apply 前の state バックアップへ戻すのが最小リスクです。
本番ロールバックの最小手順(例)
# 事前
terraform version > TF_VERSION.txt
terraform state pull > state.pre.json
terraform plan -out=tfplan.bin
sha256sum tfplan.bin > tfplan.sha256
# 適用
terraform apply tfplan.bin
# ロールバック(必要時)
# 1) 直前に使った Terraform バイナリへ切替(tfenv など)
# 2) 直前の .terraform.lock.hcl を復元
# 3) state.pre.json を push
terraform state push state.pre.json
よくある破壊的変更は、属性のデフォルト値変更による置換、必須→任意(または逆)への型/検証の変更、名前変更による address 変更、for_each や count の式変更によるキーシフトなどです。検出と回避のため、計画の読み解きと lifecycle の使い分けが重要です。
置換が避けられない場合でも、create_before_destroy の適用で停止時間を抑制できます。逆に、安易な ignore_changes はドリフトの温床になるため、時間制限付きや根本対応までの暫定運用として明示的に扱います。
置換を回避・制御する lifecycle 例
resource "example_resource" "svc" {
name = var.name
immutable_a = var.immutable_a # デフォルト変更が疑われる属性は明示値で固定
mutable_b = var.mutable_b
lifecycle {
create_before_destroy = true # 可能な場合に適用して停止を抑制
ignore_changes = [
# 一時的なドリフト許容(恒久対応後に削除)
mutable_b
]
}
}
Pro
問題 1
プロバイダのメジャーバージョンを検証するために、できるだけ本番と同一の再現性を保ちつつ最小差分で影響を確認したい。最も適切な手順はどれか。
正解: A
検証ブランチでのみ init -upgrade を行い、ロックファイル差分と plan をレビューするのが推奨です。main 直適用やロック削除は再現性が失われ、想定外の破壊的変更を招きます。refresh のみでは破壊的差分の有無を判断できません。
.terraform.lock.hcl はコミットすべきですか?複数プラットフォームをどう扱いますか?
はい、コミットすべきです。再現性の要であり、レビュー対象にもなります。異なる実行プラットフォームでの再現性を担保するには、検証ブランチで terraform providers lock -platform=<一覧> を実行して各プラットフォームのハッシュを追記してからマージします。
リソース名を変えたら置換が出そうです。破壊せずに移行できますか?
コードに moved ブロックを追加すれば、apply 時に state 上のアドレスを安全に移せます。より細粒度に制御したい場合は terraform state mv を併用します。どちらの方法でも、事前に plan で move が検出されていることを確認してください。
ロールバック時の注意点は?
直前に使用した Terraform バイナリと .terraform.lock.hcl をそのまま使い、apply 前に取得した state バックアップへ戻します。main では terraform init -lockfile=readonly を徹底し、意図しないロック更新を防いでください。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...