Terraformはリソース間のHCL参照式から依存関係を自動的に解決します。しかし、参照式では捕捉できない「副作用依存」が存在するとき、depends_onを明示的に指定しないと期待した順序でリソースが作成されません。
この記事では、terraform depends_on の正しい使い所と、暗黙依存(Implicit Dependency)が機能するしくみ、terraform graphを使った依存関係の可視化方法、さらにdepends_on過剰使用のリスクまで、実コードを交えて解説します。実務でTerraformを使うエンジニアが知っておくべき「依存関係設計の勘所」を体系的にまとめます。
この記事のポイント
・HCL参照式(aws_vpc.main.id など)を書くだけで暗黙依存が自動生成される
・depends_onは暗黙依存が捕捉できない副作用依存にのみ使う最後の手段
・terraform graph | dot -Tpng で依存関係グラフを画像として確認できる
・depends_onの過剰使用は計画精度を下げるため最小限にとどめる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Terraformの依存関係とは何か|暗黙依存と明示的依存の違い
Terraformがリソースを作成・変更・削除するとき、内部では「依存関係グラフ(Dependency Graph)」を構築してから処理順序を決めます。このグラフには2種類の依存があります。・暗黙依存(Implicit Dependency):HCLの参照式から自動生成される依存関係。手書き不要で、Terraformが自動的にグラフを構築します。
・明示的依存(Explicit Dependency):depends_onで手動指定する依存関係。暗黙依存が捕捉できない副作用依存の場面でのみ使います。
Terraformの公式ドキュメントにも「depends_onは最後の手段(last resort)」と明記されています。暗黙依存を正しく使いこなすことが、依存関係設計の基本です。まずは暗黙依存のしくみをしっかり理解しましょう。
暗黙依存(Implicit Dependency)のしくみ|参照式が自動的に順序を決める
暗黙依存は、あるリソースの属性値を別のリソースの引数として参照するときに自動的に生成されます。例として、VPCとサブネットを作成するコードを見てみましょう。# VPCを定義する resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags = { Name = "main-vpc" } } # サブネットを定義する(VPCのIDを参照している) resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id # ここに参照式がある cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "public-subnet" } }
暗黙依存の重要なポイントをまとめます。
・参照式(resource_type.resource_name.attribute)を書くだけで依存関係が確立する
・depends_onを追加で書く必要はない
・Terraformは依存していないリソースを自動的に並列処理するため、実行が速い
・モジュールのoutputを介した参照でも同様に暗黙依存が生成される
暗黙依存が生成される参照形式の例を表で確認しましょう。
| 参照の種類 | 記法例 | 依存元 → 依存先 |
|---|---|---|
| リソース属性の参照 | aws_vpc.main.id |
サブネット → VPC |
| モジュールoutputの参照 | module.network.vpc_id |
アプリモジュール → ネットワークモジュール |
| data sourceの参照 | data.aws_ami.latest.id |
EC2インスタンス → データソース |
| locals経由の参照 | local.subnet_id |
localsが参照するリソースへの間接依存 |
1. terraform planの出力で依存関係の順序を確認する
terraform planを実行すると、依存関係グラフに基づいた実行計画が表示されます。実際の出力例を見てみましょう。$ terraform plan Terraform will perform the following actions: # aws_vpc.main will be created + resource "aws_vpc" "main" { + id = (known after apply) + cidr_block = "10.0.0.0/16" ... } # aws_subnet.public will be created + resource "aws_subnet" "public" { + id = (known after apply) + vpc_id = (known after apply) ← VPCのID確定後に設定される + cidr_block = "10.0.1.0/24" ... } Plan: 2 to add, 0 to change, 0 to destroy.
depends_onの使い所|暗黙依存が効かないケースを理解する
暗黙依存が機能するのは「HCLの参照式として値を引き渡している場合」だけです。AWS上での設定適用などの「副作用」による依存は、HCLコードからは見えないため、Terraformは自動検出できません。1. IAMポリシーアタッチ後の権限反映待ち
EC2インスタンスにIAMロールをアタッチし、そのロールのポリシーに特定権限が必要な場合を考えます。インスタンスプロファイルとIAMポリシーアタッチメントの間にはHCL参照式がない場合があります。resource "aws_iam_role" "app_role" { name = "app-role" assume_role_policy = data.aws_iam_policy_document.assume.json } resource "aws_iam_role_policy_attachment" "s3_access" { role = aws_iam_role.app_role.name policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" } resource "aws_instance" "app" { ami = "ami-0abc12345def67890" instance_type = "t3.micro" iam_instance_profile = aws_iam_instance_profile.app.name # インスタンスの起動前にS3ポリシーのアタッチが完了していることを保証する depends_on = [aws_iam_role_policy_attachment.s3_access] }
2. null_resourceがリソース完成を前提とする場合
null_resourceのprovisionerを使ってリモートでコマンドを実行する場合、対象リソースのIDや属性を参照していなければ暗黙依存が生成されません。resource "aws_db_instance" "main" { identifier = "app-db" engine = "mysql" # ... 設定省略 } # DBが起動してから初期データを投入するnull_resource resource "null_resource" "db_init" { provisioner "local-exec" { command = "mysql -h ${aws_db_instance.main.address} -u admin -p < init.sql" } depends_on = [aws_db_instance.main] }
【注意】Terraformの公式ドキュメントはprovisionerの使用を「最後の手段」と位置づけています。provisioner依存の設計は後述の「落とし穴」にもつながるため、代替手段(user_data / cloud-init)を先に検討することを推奨します。
3. モジュール間に参照式のない依存がある場合
モジュールを分割したときに、片方のモジュールが他方のモジュールの「副作用」(セキュリティグループの作成、Route53レコードの登録など)を前提とする場合にdepends_onが必要になることがあります。module "network" { source = "./modules/network" # VPC・サブネット・セキュリティグループを作成するモジュール } module "application" { source = "./modules/application" # network モジュールのoutputを使っていない場合でも # ネットワークリソースの完成を待つ必要があるなら depends_on を書く depends_on = [module.network] }
terraform graphで依存関係を可視化する
「自分のTerraformコードがどんな依存グラフを形成しているか」を視覚的に確認したいときはterraform graphが便利です。1. graphvizのインストール
terraform graphの出力はDOT言語形式なので、graphvizで画像に変換します。# Ubuntu/Debian の場合 $ sudo apt install -y graphviz # RHEL 9 / Rocky Linux 9 の場合 $ sudo dnf install -y graphviz # インストール確認 $ dot -V dot - graphviz version 2.43.0 (0)
2. terraform graphの実行と画像化
# 依存関係グラフをPNGに変換して保存する $ terraform graph | dot -Tpng -o graph.png # SVG形式で出力する場合(ブラウザで開ける) $ terraform graph | dot -Tsvg -o graph.svg # planの依存グラフ(より詳細) $ terraform graph -type=plan | dot -Tpng -o graph-plan.png
3. コマンドラインでグラフの内容を確認する
graphvizをインストールできない環境では、DOT言語の出力を直接読む方法もあります。# DOT言語出力をそのまま確認する(依存関係の向きを読み取れる) $ terraform graph digraph { compound = "true" newrank = "true" subgraph "root" { "[root] aws_instance.app (expand)" -> "[root] aws_iam_role_policy_attachment.s3_access (expand)" "[root] aws_subnet.public (expand)" -> "[root] aws_vpc.main (expand)" ... } }
depends_onの落とし穴|過剰使用によるメンテナンス性の低下
depends_onは便利に見えますが、安易に使うと深刻な問題を引き起こします。1. 計画精度の低下(「不透明な依存」問題)
Terraformはterraform planの実行時に、リソースの変更が必要かどうかを事前に判断します。この判断は参照式を通じた値の追跡によって行われます。しかし、depends_onで依存を追加すると、Terraformはその依存先のリソースを「不透明なブロック」として扱います。depends_on先のリソースに変更があると、依存元のリソースも自動的に「変更が必要」と判断されることがあります。これは不要な変更をplanに含ませる原因になります。
2. 暗黙依存で解決できるケースにdepends_onを書いてしまう
よくある間違いとして、参照式を書いているにもかかわらず、念のためdepends_onも追加するケースがあります。# 悪い例: 参照式があるのに depends_on も書いている resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id # この参照で暗黙依存が生成されている cidr_block = "10.0.1.0/24" depends_on = [aws_vpc.main] # 不要。削除すべき }
3. 本当にdepends_onが必要か確認するチェックリスト
depends_onを書く前に、以下を確認しましょう。・依存先リソースの属性値を参照式として使っていないか?(使っていれば暗黙依存で足りる)
・AWS上での「副作用」(権限適用・設定反映)を待つ必要があるか?(これだけがdepends_on必須のケース)
・モジュール間でoutputを介せばdepends_onなしで依存を表現できないか?
これらを確認した上で「やはりdepends_onが必要」と判断したときだけ使います。
実践的な依存関係設計パターン|モジュール間依存の制御
中規模以上のTerraformプロジェクトでは、機能ごとにモジュールを分割することが一般的です。このとき、モジュール間の依存関係をどう設計するかが重要です。1. outputを介した依存(推奨)
モジュール間の依存はoutput → 引数参照で表現するのが最もクリーンです。# modules/network/outputs.tf output "vpc_id" { value = aws_vpc.main.id description = "作成したVPCのID" } output "subnet_ids" { value = [aws_subnet.public.id, aws_subnet.private.id] description = "作成したサブネットのIDリスト" }
# main.tf(ルートモジュール) module "network" { source = "./modules/network" } module "application" { source = "./modules/application" # networkモジュールのoutputを引数として渡す # これにより application -> network の暗黙依存が自動生成される vpc_id = module.network.vpc_id subnet_ids = module.network.subnet_ids }
2. depends_onをモジュールレベルで使う場合
モジュールがoutputを介さない副作用依存を持つ場合のみ、モジュールレベルのdepends_onを使います。# セキュリティグループルールをモジュール内で設定する場合など module "security" { source = "./modules/security" } module "application" { source = "./modules/application" # applicationモジュールがsecurityモジュールの副作用(SGルール適用)を # 前提とするが、outputを介して値を受け取っていない場合 depends_on = [module.security] }
よくあるエラーと依存関係トラブルシューティング
1. 「Error: Reference to undeclared resource」が出る
Error: Reference to undeclared resource on main.tf line 12, in resource "aws_subnet" "public": 12: vpc_id = aws_vpc.main.id A managed resource "aws_vpc" "main" has not been declared in the root module.
・参照式のリソース名・リソースタイプのスペルを確認する
・別のmoduleやファイルに定義があるならoutputを介して渡す設計に変更する
2. 「Error creating Instance: InvalidGroup.NotFound」が出る
Error: creating EC2 Instance: InvalidGroup.NotFound: The security group 'sg-0abc123' does not exist
・EC2リソースのvpc_security_group_idsにSGのIDを参照式で書いているか確認する
・書いている場合でも順序がおかしければ、terraform state showでstateのIDが実際のAWS上リソースと一致しているか確認する
・stateとAWSの乖離(ドリフト)が原因の場合はterraform refreshで状態を同期する
3. dependsの順序は正しいのにタイミングエラーが起きる
AWSのAPIはリソースを作成しても、権限や設定が「伝播」するまで数秒かかることがあります(IAMの権限伝播、Security Group Ruleの適用など)。この問題には time_sleepリソース(hashicorp/timeプロバイダー)を使って待機を入れる方法があります。
resource "time_sleep" "wait_30_seconds" { depends_on = [aws_iam_role_policy_attachment.s3_access] create_duration = "30s" } resource "aws_instance" "app" { ami = "ami-0abc12345def67890" instance_type = "t3.micro" depends_on = [time_sleep.wait_30_seconds] }
本記事のまとめ
TerraformのリソースはHCL参照式から自動的に依存関係グラフが構築されます。depends_onは参照式では捕捉できない副作用依存にのみ使う「最後の手段」と位置づけることで、コードのメンテナンス性と計画精度を高く保てます。| やりたいこと | 手段 |
|---|---|
| リソースAの完成後にリソースBを作る(参照式あり) | 参照式を書くだけ(暗黙依存が自動生成される) |
| 副作用依存(IAMアタッチ後の権限適用など)を制御する | depends_on = [リソース] |
| 依存関係グラフを可視化する | terraform graph | dot -Tpng -o graph.png |
| モジュール間の依存を表現する(推奨) | output → 引数参照で暗黙依存を生成する |
| モジュール間の副作用依存を制御する | depends_on = [module.name] |
| depends_onが必要か判断する | 参照式で代替できないか先に確認する |
>> Terraform実践講座の詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:Terraformのmoved・removedブロックで安全にリファクタリングする方法|state mvに頼らない宣言型の構成変更
- この記事の属するカテゴリ:Terraformへ戻る

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