terraform planは、状態と実際のリソース、そして現在の構成から導かれる“これからの変更”を可視化します。誤解しやすいのは、planはあくまでシミュレーションであり、適用(apply)しない限りインフラは変わらないことです。
この記事では、差分記号の正しい読み方、主要オプションの使いどころ、CI/CDでの自動判定、ドリフト検出のコツまで、試験(Associate)と実務の両軸で整理します。
terraform planは、現在の状態(state)と実環境の読み取り(デフォルトでrefresh)を踏まえ、構成(HCL)から導かれる実行計画を出力します。出力は“何がどの順で、なぜ変わるのか”を示すため、レビューとリスク低減の中心的な道具になります。
差分出力では、+が作成、-が削除、~がインプレース更新、-/+や+/-が置換(再作成)を意味します。属性単位でも~が付きますが、リソース行頭が-/+の場合は破壊と再作成が必要な変更であることを示します。
| 記号/表示 | 意味 | 代表例 |
|---|---|---|
| + | 新規作成 | 新しいaws_s3_bucketの追加 |
| - | 削除 | 不要になったaws_security_groupの削除 |
| ~ | 設定のインプレース更新 | タグの値変更、retention_periodの更新 |
| -/+ または +/- | 置換(破壊→作成) | immutable属性変更、インスタンスタイプ変更で再作成が必要な場合 |
planの位置づけ(構成→計画→適用)
最小手順と出力保存
terraform init
terraform plan -out=plan.tfplan
# レビュワーは plan.tfplan を
# terraform show plan.tfplan で確認可能運用では、ドリフト検出や限定的な影響確認のためにオプションを使い分けます。-refresh=falseで外部の再読み取りを抑制すると、ネットワーク不安定時の暫定評価に役立ちますが、最新の実態を反映しないためリスクが上がります。
-refresh-onlyは、実リソースに変更を加えずにstateだけを最新化する計画を作ります。変更の適用はterraform apply -refresh-onlyで行われ、外形的には“差分ゼロ化(state同期)”の動きになります。
| オプション | 目的 | 注意点 |
|---|---|---|
| -refresh=false | 外部読み取りを抑制 | ドリフトを見落とすリスク。最終判断には不向き |
| -refresh-only | stateのみ最新化する計画 | apply -refresh-onlyで適用。リソース自体は変更しない |
| -replace=addr | 特定リソースの再作成を強制 | 影響範囲をよく確認。依存リソースへの波及に注意 |
| -destroy | 破壊計画の作成 | 意図しない削除防止のため、レビューを厳格化 |
| -target=addr | 限定スコープで計画 | 恒常運用は非推奨。依存解決の抜け漏れを招きやすい |
refresh-onlyのイメージ
代表的な組み合わせ例
# ドリフト確認を厳密に
terraform plan
# ネットワーク不調時の暫定差分確認(最終判断は避ける)
terraform plan -refresh=false
# stateを最新化するだけの計画
terraform plan -refresh-only
# 特定リソースの置換
terraform plan -replace=aws_instance.webCIでは-detailed-exitcodeで差分の有無を機械判定します。0は差分なし、2は差分あり、1はエラーです。PRにplan出力をコメント化する運用も一般的です。
計画の再現性を高めるため、-outでバイナリ計画ファイルを生成し、terraform show -jsonで機械可読化します。計画ファイルはTerraformのバージョン・バックエンド・ワークスペースに依存するため、別環境への持ち回りは避けます。
| ユースケース | コマンド | 戻り値/出力 |
|---|---|---|
| PRで差分有無を判定 | terraform plan -detailed-exitcode | 0:なし / 2:あり / 1:失敗 |
| 計画の固定と共有 | terraform plan -out=plan.tfplan | apply時に承認なしで適用 |
| 機械可読なレビュー | terraform show -json plan.tfplan | JSONで差分詳細を抽出可能 |
CIパイプラインの典型
Bash例: 差分ありのみ失敗させる
set -euo pipefail
terraform init
if terraform plan -detailed-exitcode -out=plan.tfplan; then
echo "No changes"
else
code=$?
if [ "$code" -eq 2 ]; then
echo "Changes detected" >&2
terraform show -json plan.tfplan > plan.json
exit 2
else
echo "Plan failed" >&2
exit $code
fi
fi既存リソースが手作業で変更されている場合、planは~または-/+としてドリフトを示します。既存資産の取り込みはterraform importでstateに紐付けてから行うのが安全です。
データソースや未確定の値(apply時決定値)はplanでunknownとして扱われることがあります。unknownが置換要件を引き起こさないか、注意深く読みます。
| 状況 | planの症状 | 対処 |
|---|---|---|
| 手作業変更あり | ~ または -/+ が多数 | terraform planで差分確認→構成を修正 or import |
| 既存資産を管理下へ | リソース新規作成提案 | terraform importでstateに取り込み |
| 未確定値が多い | unknown値が散見 | depends_onやoutput/variable設計の見直し |
ドリフト検出の流れ
インポートと最小差分の作り方
# 既存S3バケットをstateへ取り込み
# 事前にHCLを既存設定に合わせておく
terraform import aws_s3_bucket.logs my-existing-logs-bucket
terraform plan # ドリフトが解消されていることを確認planは提案であり、applyのみが変更を実行すること。-detailed-exitcodeの意味、-outで保存した計画をapplyすると承認なしで適用されることは頻出です。
-targetは恒常運用の推奨ではない、-destroyは破壊計画を作る、-refresh-onlyはstate同期のための計画を作る、といった“意図の違い”が問われます。
| トピック | 正答要点 | よくある誤答/混同 |
|---|---|---|
| planの役割 | 変更のシミュレーション | planが変更を実行する |
| -detailed-exitcode | 0/2/1の三値で判定 | 0と2の違いを混同 |
| 保存計画の適用 | apply plan.tfplanは承認なし | -auto-approveが必須と誤解 |
| JSON出力 | show -json plan.tfplan | planに直接 -json があると誤解 |
試験での意思決定チャート(簡略)
試験頻出コマンドサマリ
# 差分の有無だけ知る
terraform plan -detailed-exitcode
# 破壊計画
terraform plan -destroy
# 保存→適用
terraform plan -out=plan.tfplan
terraform apply plan.tfplan
# JSONでレビュー
terraform show -json plan.tfplan > plan.jsonplanで“forces replacement”が出たら、immutable属性が変わっていないか確認します。仕様上の再作成が必要な場合は、-replaceで明示的に意図を示すと安全です。
リソース名の変更やモジュール再編ではmovedブロックを使うと、destroy/createを避けてstateを自動移行できます。バックエンドのロック競合や認可エラーはplan以前の問題として検出されるため、まずは資格情報とロック状態を確認します。
| 事象 | 原因 | 対策 |
|---|---|---|
| forces replacement | immutable属性変更/仕様 | -replaceで意図を明示、メンテナンス計画 |
| destroy提案(改名時) | state未移行 | movedブロックで自動移行 |
| Lock情報で失敗 | 他のプロセスがロック | 完了待ち/CIジョブの直列化 |
| 認可エラー | クレデンシャル不足 | プロバイダ認証を見直し |
改名時のmovedブロック適用イメージ
movedブロックと置換の明示
# main.tf(Terraform 1.x系)
moved {
from = aws_instance.old_name
to = aws_instance.new_name
}
# 不可避の再作成を明示
# terraform plan -replace=aws_instance.webAssociate
問題 1
CIでterraform planを実行し、差分がある場合だけPRを失敗させたい。最小限の実装として適切なのはどれか?
正解: A
公式にサポートされる方法は-detailed-exitcodeでの機械判定。終了コード0は変更なし、2は変更あり、1は失敗。出力のgrepは不安定で非推奨。applyは計画の実行でありCIで失敗検知目的に使うべきではない。-refresh=falseは差分の正確性を損なう。
terraform planは本当に何も変更しませんか?
はい。planは実行計画の算出と表示のみです。実リソースが変更されるのはterraform apply(またはdestroy)を実行したときだけです。
保存した計画ファイル(plan.tfplan)は別の環境で再利用できますか?
推奨されません。計画ファイルはTerraformのバージョン、バックエンド、ワークスペース、変数に依存します。生成したのと同じ環境で速やかに適用する前提で使います。
JSONで差分を取りたいのですが、terraform planに-jsonはありますか?
plan自体には-jsonはありません。terraform plan -outで計画を保存し、terraform show -json plan.tfplanでJSONを取得します。
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を用いた既存リソース参照の基本、選択基準、評価順序、...