「削除したいリソースだけを消すつもりが、依存先のVPCまで巻き込まれた」
Terraformで本番インフラを管理していると、こうした削除順序のトラブルに直面することがあります。その原因のほとんどは、tfstateファイルが内部に持っている依存関係の情報を正確に把握できていないことにあります。
この記事では、tfstateファイルの構造から依存順序を読み解く方法と、計画どおりリソースを削除するための設計パターンを実践的に解説します。実際のJSONデータとコマンド出力例を示しながら、terraform show -json・terraform graph・lifecycle.prevent_destroyを活用した安全な削除設計を説明します。
Terraform 1.8 / RHEL 9 / Ubuntu 24.04 LTS で動作確認済みです。
この記事のポイント
・tfstateファイルのdependencies配列がリソース削除順序を決定する
・terraform show -jsonで依存関係をJSON形式で完全可視化できる
・lifecycle.prevent_destroyとdepends_onで削除設計を宣言的に制御できる
・stateロック発生時はterraform force-unlockで即座に解除できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
tfstateファイルに刻まれた依存グラフの仕組み
Terraformが「正しい順序でリソースを作り、逆順で削除できる」のは、tfstateファイルが各リソースの依存関係をJSONで記録しているからです。tfstateファイル(terraform.tfstate)はJSON形式のデータファイルです。ルートモジュールの管理対象リソースは"resources"配列に格納され、各エントリがdependenciesという配列を持ちます。これがリソース間の依存関係を明示する要(かなめ)の情報です。
実際のtfstateのresources配列の一例を見てみましょう。
# terraform.tfstate(抜粋) { "version": 4, "terraform_version": "1.8.5", "serial": 12, "resources": [ { "mode": "managed", "type": "aws_instance", "name": "web", "instances": [ { "attributes": { "id": "i-0a1b2c3d4e5f67890", "instance_type": "t3.micro" }, "dependencies": [ "aws_security_group.web_sg", "aws_subnet.public", "module.vpc.aws_vpc.main" ] } ] } ] }
この例の場合:
・作成順序: module.vpc.aws_vpc.main → aws_subnet.public → aws_security_group.web_sg → aws_instance.web
・削除順序: aws_instance.web → aws_security_group.web_sg → aws_subnet.public → module.vpc.aws_vpc.main
この順序がtfstateのdependencies情報から自動的に導き出されます。HCLのコードで属性参照を書くだけで、Terraformは依存関係を暗黙的に記録します。これがTerraformのグラフエンジンの核心です。
tfstateの依存関係を調べる実践手順
1. terraform show -jsonでdependencies配列を確認する
実際のtfstateの内容をJSON形式で確認するにはterraform show -jsonを使います。jqがインストールされていれば、特定リソースの依存情報を整形して取り出せます。# jqで各リソースのアドレスと依存関係を一覧表示 # terraform show -json | jq ".values.root_module.resources[] | {address: .address, deps: .depends_on}"
{ "address": "aws_instance.web", "deps": [ "aws_security_group.web_sg", "aws_subnet.public" ] } { "address": "aws_security_group.web_sg", "deps": [] } { "address": "aws_subnet.public", "deps": [ "module.vpc.aws_vpc.main" ] }
aws_security_group.web_sgのdepsが空配列([])であることに注目してください。これは「他のリソースに依存していない」ことを意味し、terraform destroyでは依存チェーンの末端として最後に削除される対象候補です。jqがない環境では
python3 -m json.toolで整形できます。# terraform show -json | python3 -m json.tool | grep -A 10 "depends_on"
2. terraform graphで依存グラフを可視化する
terraform graphはリソース間の依存をDOT形式(Graphviz)で出力するコマンドです。destroyフェーズに特化したグラフを確認したい場合は-type=plan-destroyを指定します。# destroyフェーズの依存グラフをDOT形式で出力 # terraform graph -type=plan-destroy > destroy_graph.dot # Graphvizをインストールしてからsvgに変換(Ubuntu/Debianの場合) # apt-get install graphviz # dot -Tsvg destroy_graph.dot -o destroy_graph.svg
planファイルがある場合は実際の変更計画に基づいたより正確なグラフが生成されます。
# planファイルを作成してからgraphを生成(より正確なグラフ) # terraform plan -out=tfplan # terraform graph -plan=tfplan -type=plan-destroy
3. 暗黙依存とdepends_onの違い
Terraformの依存関係には2種類あります。HCL内でリソースの属性を参照する式(例:aws_security_group.web_sg.id)を書くだけで自動的に記録される暗黙依存と、depends_onメタ引数を使って明示的に宣言する明示依存です。暗黙依存の例:
resource "aws_instance" "web" { ami = data.aws_ami.amazon_linux.id instance_type = "t3.micro" # セキュリティグループのIDを参照 → tfstateのdependenciesに自動記録される vpc_security_group_ids = [aws_security_group.web_sg.id] }
depends_onを明示します。典型的なのはIAMポリシーのアタッチとLambda関数の間のような「APIの伝播遅延が影響する」パターンです。resource "aws_lambda_function" "processor" { filename = "lambda.zip" function_name = "data-processor" role = aws_iam_role.lambda_exec.arn handler = "index.handler" runtime = "python3.12" # IAMポリシーの伝播を待ってから関数を作成する depends_on = [aws_iam_role_policy_attachment.lambda_policy] }
depends_onを使うとtfstateのdependencies配列に追加されるため、destroyの逆順削除でも順序が保証されます。IAMやSSMのような「作成は成功したが即座に使えない」サービスへの依存には積極的に使うのが実務の鉄則です。計画どおり削除するための設計パターン
1. lifecycle.prevent_destroyで削除ガードをかける
データベース・S3バケット・VPCなど「誤って消えたら取り返しのつかないリソース」にはlifecycle.prevent_destroy = trueを設定します。本番環境での誤操作・CI暴走による意図しない削除を防ぐ重要な設定です。resource "aws_db_instance" "main" { identifier = "prod-mysql" instance_class = "db.t3.small" allocated_storage = 20 engine = "mysql" engine_version = "8.0" lifecycle { prevent_destroy = true } }
terraform applyでこのリソースの削除が発生しようとすると、Terraformは以下のエラーを出して処理を中断します。# terraform plan実行時の出力(削除が発生するケース) │ Error: Instance cannot be destroyed │ │ on main.tf line 12, in resource "aws_db_instance" "main": │ 12: prevent_destroy = true │ │ Resource aws_db_instance.main has lifecycle.prevent_destroy set, │ but the plan calls for this resource to be destroyed. To allow │ this object to be destroyed, update the resource configuration │ and set "prevent_destroy" to false, then review and apply │ your changes.
2. depends_onで削除順序を明示設計する
Terraformはtfstateのdependenciesに従って逆順で削除しますが、data(データソース)を経由した間接参照は自動的に依存記録されないケースがあります。このときdepends_onで明示的に順序を宣言することで、予期しない削除順序を防げます。API GatewayとLambdaの統合で削除順序の問題が起きやすいケースの例です。
resource "aws_lambda_alias" "prod" { name = "prod" function_name = aws_lambda_function.api.function_name function_version = aws_lambda_function.api.version # APIゲートウェイのデプロイが完了してからエイリアスを切り替える depends_on = [ aws_api_gateway_deployment.prod, aws_api_gateway_stage.prod ] }
dependenciesにAPIゲートウェイリソースが記録されます。結果として削除時はLambdaエイリアスが先に消え、APIゲートウェイ関連が後に削除される順序が保証されます。Terraformの体系的な設計ノウハウはTerraformの実践学習コースでも詳しく取り上げています。
3. terraform destroy -targetの使い所と注意点
特定リソースだけを削除したい場面では-targetオプションが使えます。しかし、依存グラフの途中のリソースだけを消すと、stateと実インフラの整合性が崩れる危険があります。# 単体削除(依存関係を事前に確認してから実行) # terraform destroy -target=aws_instance.old_web
-targetを使ってよい場面は、以下の限定的なケースに絞ることを推奨します。・開発環境のリソースを部分的に再作成したいとき
・本番での緊急対応で影響範囲を最小化する必要があるとき
日常的な変更管理に
-targetを多用すると、tfstateが実インフラと乖離したドリフト状態が蓄積し、後のterraform planで予期しない差分が大量に出てくる原因になります。ピンポイント削除は「最終手段」として位置づけ、通常は完全なplanとapplyで管理するのが正しい運用です。tfstateトラブル対処|ロックとドリフトの解消
1. stateロックをterraform force-unlockで解除する
S3+DynamoDBなどのリモートbackendを使う環境では、terraform apply中に他の実行が重ならないようstateがロックされます。CIパイプラインが途中で落ちたり、ネットワーク障害でプロセスが強制終了された場合、ロックが残り続けることがあります。ロックが残っている状態で実行すると、以下のようなエラーが出ます。
│ Error: Error acquiring the state lock │ │ Error message: ConditionalCheckFailedException │ Lock Info: │ ID: 3da4ab12-1234-5678-abcd-ef0123456789 │ Path: s3://mybucket/env:/prod/terraform.tfstate │ Operation: OperationTypeApply │ Who: ubuntu@ci-runner-01 │ Created: 2026-10-01 03:45:12.123456789 +0000 UTC
Lock InfoのIDをコピーして以下のコマンドで解除します。# terraform force-unlock 3da4ab12-1234-5678-abcd-ef0123456789 Do you really want to force-unlock state? Terraform will remove the lock on the remote state. This will allow local Terraform commands to modify this state, even though it may still be in use. Only "yes" will be accepted to confirm. Enter a value: yes Terraform state has been successfully unlocked!
2. terraform plan -refresh-onlyでドリフトを検出する
コンソールや他のツールで手動変更を加えたとき、tfstateと実インフラに乖離(ドリフト)が生じます。Terraform 1.1以降では-refresh-onlyフラグで「実インフラと一致するstateへの更新計画」を生成できます。実インフラには変更を加えず、stateだけを更新する安全な操作です。# terraform plan -refresh-only Note: Objects have changed outside of Terraform # aws_security_group.web_sg has been changed ~ resource "aws_security_group" "web_sg" { ~ ingress { - cidr_blocks = ["10.0.0.0/8"] + cidr_blocks = ["10.0.0.0/8", "172.16.0.0/12"] } } Plan: 0 to add, 0 to change, 0 to destroy.
terraform planを実行して差分を確認し、次のapplyで設定どおりに戻すか、ドリフトを承認してコードに取り込むか判断します。本番運用では定期的に
terraform plan -refresh-onlyを実行してドリフトを検出する習慣をつけると、予期しない差分に驚くことがなくなります。本記事のまとめ
tfstateファイルのdependencies配列はTerraformの削除順序を決定する重要な情報です。terraform show -jsonやterraform graphでこの依存グラフを可視化し、lifecycle.prevent_destroyとdepends_onで削除設計を宣言的に制御することが、計画どおりの安全な削除を実現する基本です。| やりたいこと | コマンド / 設定 |
|---|---|
| 依存関係をJSON確認 | terraform show -json | jq ".values.root_module.resources[]" |
| destroyグラフをDOT出力 | terraform graph -type=plan-destroy |
| 削除ガードをかける | lifecycle { prevent_destroy = true } |
| 削除順序を明示 | depends_on = [リソース参照] |
| stateロック解除 | terraform force-unlock LOCK_ID |
| ドリフト検出 | terraform plan -refresh-only |
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Terraformをはじめとしたクラウドインフラの実務スキルを体系的に学べる環境をご用意しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:ecspresso導入時のtfstate競合問題|Terraformと役割を分けるべき理由
- この記事の属するカテゴリ:Terraformへ戻る

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