このような事故は、terraform planの出力を流し読みして実行ボタンを押した時に起きる。planは実行計画を表示してくれるが、差分記号の意味を正確に理解していないと「削除が含まれていた」「リソースが再作成された」ことに気づけない。
この記事では、terraform planが出力する差分記号(
~・+・-・-/+)とknown after applyの意味を実機出力で解説する。planを流し読みせず、本当に安全かを判断できるようになる思考手順を身につけることが目標だ。この記事のポイント
・差分記号(~ ・ + ・ - ・ -/+)は5種類で意味と危険度が異なる
・-/+(再作成)はdestroyカウントに含まれない場合があり最も見落としやすい
・known after applyは正常。依存チェーンの追い方を解説
・想定外の差分は-refresh-onlyでドリフト確認後に対処する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
terraform planを正確に読めるとどう変わるか
terraform planの役割は「変更内容を事前確認する」ことだが、出力を誤読すると計画がセーフガードとして機能しない。特に次の2つのケースで誤読が起きやすい。
・
~(変更)だと思っていたら、実際は-/+(破壊的再作成)だった・known after applyが含まれるリソースを「確認できた」と判断してapplyした
どちらも、applyが完了するまで本番の変化が見えない状態で判断している。planの読み方を習得することは、Terraformを安全に運用するための最初のステップだ。
差分記号が表す5種類の意味
terraform planの出力は、行頭の記号でリソースに何が起きるかを示す。以下はRHEL 9.4 / Ubuntu 24.04 LTS環境のTerraform 1.9で実行した、EC2追加とセキュリティグループ変更のplan出力例だ。
$ terraform plan Terraform will perform the following actions: # aws_instance.web_server will be created + resource "aws_instance" "web_server" { + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t3.micro" + id = (known after apply) + public_ip = (known after apply) + tags = { + "Name" = "web-server-prod" } } # aws_security_group.web will be updated in-place ~ resource "aws_security_group" "web" { id = "sg-0a1b2c3d4e5f67890" ~ ingress = [ - { - from_port = 8080 - to_port = 8080 - protocol = "tcp" }, + { + from_port = 443 + to_port = 443 + protocol = "tcp" }, ] } Plan: 1 to add, 1 to change, 0 to destroy.
-/+(再作成)が含まれることがあるため、サマリーだけで判断するのは危険だ。1. ~(チルダ): インプレース変更
~はリソースを削除せず、属性だけを変更する。セキュリティグループのルール変更、タグの追加など、稼働したまま適用できる変更だ。~の内側にある-と+は属性値の変更前後を示す。2. +(プラス): 新規作成
+は新しいリソースを作成する。既存リソースには影響しない。3. -(マイナス): 削除
-はリソースを削除する。Plan: X to destroy.にカウントされる。意図的な削除でない場合は即座に中断して原因を確認する。4. -/+: 破壊的再作成(最重要)
最も注意が必要な記号だ。-/+は一度削除してから再作成する。サマリーの「X to destroy.」にカウントされない場合があり、見落としやすい。-/+が発生する典型的なケース:・RDSの
engine_versionを変更した・EC2の
amiを変更した・セキュリティグループのVPC参照を変更しようとした
・IAMロールのポリシーを特定の方法で差し替えた
plan出力では変更行の末尾に
# forces replacement と表示される。# aws_db_instance.main must be replaced -/+ resource "aws_db_instance" "main" { ~ engine_version = "8.0.32" -> "8.0.36" # forces replacement id = "db-ABCDEF1234567890" instance_class = "db.t3.medium" ... } Plan: 1 to add, 0 to change, 1 to destroy.
5. <=(less than equal): data sourceの読み込み
<=はdataブロック(data source)で外部リソースを参照する操作を表す。リソースの変更ではないが、apply時にAPIリクエストが発生して最新情報を取得する。known after applyとは何か
plan時点では確定できず、applyが完了した後に初めて値が決まる属性に(known after apply)が表示される。idやpublic_ipのようにAWSが採番・割り当てる値は必ずknown after applyになる。この表示自体は正常だ。問題になるのは、downstream側のリソースがknown after applyの値を参照している場合だ。例えばRoute 53のAレコードに
aws_instance.web_server.public_ipを設定していると、EC2が作成されるまでDNSの値が確定しない。複数リソースが絡む依存チェーンでは、plan出力でknown after applyの伝播範囲を追う必要がある。連鎖的にknown after applyが増える場合は、
terraform graphで依存関係を可視化して全体像を把握するのが有効だ。TerraformのIaC設計やマルチリソース構成のベストプラクティスは、Terraform実践入門講座でまとめて学ぶことができる。
forces replacementに気づくための確認手順
planの出力が長い場合、-/+をスキップしてしまいやすい。applyの前に次の手順で確認する。1. サマリー行を確認する
Plan: 2 to add, 1 to change, 1 to destroy.
-/+が含まれる場合があるため、次のステップも省略しない。2. planをファイルに保存してgrepで絞り込む
planの出力が数百行に及ぶ場合は、ファイルに保存してgrepで確認するのが確実だ。# planをバイナリ形式で保存 terraform plan -out=plan.tfplan # 人が読めるテキスト形式に変換 terraform show -no-color plan.tfplan > plan.txt # 再作成対象だけを抽出 grep -E 'must be replaced|forces replacement' plan.txt
3. 削除対象リソースを本番と照合する
-が付いたリソースのIDやNameタグを確認し、本番の何に対応するかを確かめてから判断する。Terraform stateのIDと本番コンソールのリソースIDを突き合わせることで、意図しない削除を防げる。planで想定外の差分が出た時のトラブルシュート
1. 差分が多すぎる場合は -refresh-only で手動変更を先に確認する
コンソールやAWS CLIで手動変更が加えられた場合、planが大量の差分を出力することがある。この場合は先に-refresh-onlyで現状を把握する。terraform plan -refresh-only
2. 意図しない再作成が含まれる場合は lifecycle ignore_changes を検討する
AutoScalingやECSがAPIで動的に変更する属性(desired_countなど)を毎回differenceとして拾ってしまう場合は、lifecycleブロックのignore_changesで除外する。resource "aws_ecs_service" "api" { # ... lifecycle { ignore_changes = [desired_count] } }
ignore_changesは設定ファイルとstateの乖離を意図的に許容するため、使いどころを絞ること。全属性をallで除外するのは避ける。3. コードを変えていないのにdestroyが含まれる場合
stateファイルとAWSの実リソースのID対応がズレている可能性がある。次のコマンドで確認する。# stateが管理しているリソースを一覧確認 terraform state list # 特定リソースのstate詳細を確認 terraform state show aws_instance.web_server
terraform state rmでstateから除外してからplanを再実行する。本記事のまとめ
| 記号 | 意味 | 注意度 |
|---|---|---|
| + | 新規作成 | 低(既存リソースへの影響なし) |
| ~(チルダ) | インプレース変更 | 中(変更内容を目視確認する) |
| - | 削除 | 高(意図的な削除か確認必須) |
| -/+ | 破壊的再作成 | 最高(本番DBや固定IPは特に注意) |
| <= | data source読み込み | 低(変更ではなく参照) |
| (known after apply) | apply後に確定する属性 | 中(downstream依存を追う) |
・
Plan: X to destroy.がゼロでも、-/+がないかgrepで必ず確認する・known after applyはそれ自体は正常だが、依存チェーンの伝播を追う
・想定外の差分は
-refresh-onlyでドリフト確認を先に行う・大規模なplanはファイルに保存して
grepで絞り込むterraform planを「おまじない」として流し読みするのをやめ、意思決定の根拠として活用できるようになると、applyによる事故は大幅に減る。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformのlocalバックエンドでCI/CDが詰まる構造|backend切替の設計判断軸
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます