tfstateファイルから読み解くTerraformリソース依存順序|計画どおり壊せる削除設計

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Terraform > tfstateファイルから読み解くTerraformリソース依存順序|計画どおり壊せる削除設計
「terraform destroyを実行したら、RDSインスタンスが先に消えてEC2だけ残ってしまった」
「削除したいリソースだけを消すつもりが、依存先の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で即座に解除できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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" ] } ] } ] }

この"dependencies"配列に列挙されたリソースは「このリソースより先に作成される必要がある」ことを意味します。削除時は逆順になり、dependenciesに記載されたリソースが後に削除されます。

この例の場合:
・作成順序: 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}"

出力例(実機:RHEL 9 / Terraform 1.8.5):

{ "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

生成された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.

【重要】本番環境のRDS・ElastiCache・Redshift・重要なS3バケットには常にこの設定を入れることを強く推奨します。「消す前に一手間かかる」という制約が、不可逆なデータ損失を防ぐ最後の砦です。

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 ] }

このdepends_onにより、tfstateの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!

注意:ロック解除は、そのLock IDを保持していたプロセスが確実に終了していることを確認してから実行してください。実行中のapplyと競合すると二重変更が発生し、stateが壊れる恐れがあります。

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.

このプランをapplyするとtfstateが実インフラ状態に更新されます。その後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をはじめとしたクラウドインフラの実務スキルを体系的に学べる環境をご用意しています。

>> Terraformコースの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。