Terraformのdepends_onと依存関係設計・暗黙依存とterraform graphでリソース作成順序を制御する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformのdepends_onと依存関係設計・暗黙依存とterraform graphでリソース作成順序を制御する方法
TITLE: Terraformのdepends_onと依存関係設計|暗黙依存とterraform graphでリソース作成順序を制御する方法 CATEGORY: Terraform TAGS: terraform,depends_on,依存関係,terraform graph,IaC,HCL DESCRIPTION: TerraformのリソースはHCL参照式で暗黙的に依存関係を形成しますが、暗黙依存が効かないケースではdepends_onが必要です。terraform graphの可視化手法、depends_onの正しい使い所と落とし穴、モジュール間依存の設計パターンを実コード例で解説します。 STATUS: 新規 「terraform planは通ったのに、applyしたらエラーになった。リソースの作成順序がおかしいのか?」「depends_onを書けばいいのはわかっているが、どこに書けば正しいのかわからない」
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の過剰使用は計画精度を下げるため最小限にとどめる


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

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

aws_subnet.publicのvpc_idにaws_vpc.main.idを参照しています。Terraformはこの参照式を検知して「VPCが先に完成しないとサブネットは作れない」という依存関係を自動的にグラフに追加します。depends_onは一行も書いていませんが、順序は保証されます。

暗黙依存の重要なポイントをまとめます。

・参照式(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.

vpc_idが「(known after apply)」と表示されているのがポイントです。これは「VPCが作成されてIDが確定した後に設定される」ことを示しており、Terraformが暗黙依存を正しく認識している証拠です。

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

aws_instanceはaws_iam_role_policy_attachmentの属性を参照していないため、暗黙依存は生成されません。しかし、インスタンスが起動してアプリケーションがS3にアクセスする前にポリシーアタッチが完了していないと権限エラーになります。このケースでdepends_onが必要です。

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

この例ではnull_resourceのcommand内でaws_db_instance.main.addressを文字列補間しているため、実は暗黙依存が生成されます。しかし、provisionerのcommandがリソースのIDや属性を参照しない場合は暗黙依存が生成されないため、depends_onが必要になります。

【注意】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] }

ただし、モジュール間でoutputを介して値を渡している場合はdepends_onは不要です。参照式で暗黙依存が生成されるからです。

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

出力されたPNG画像を開くと、リソース間の矢印で依存関係が可視化されます。depends_onで追加した依存も、暗黙依存と同様にグラフに現れます。

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)" ... } }

矢印の向き(A -> B)は「AはBに依存する」を意味します。depends_onで追加した依存も同じ矢印で表現されます。

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] # 不要。削除すべき }

参照式で暗黙依存が確立している場合、depends_onは冗長であり、計画精度を下げるだけです。

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 }

module.network.vpc_idという参照式があるため、Terraformは自動的にnetworkモジュールを先に完成させます。depends_onは不要です。

2. depends_onをモジュールレベルで使う場合

モジュールがoutputを介さない副作用依存を持つ場合のみ、モジュールレベルのdepends_onを使います。

# セキュリティグループルールをモジュール内で設定する場合など module "security" { source = "./modules/security" } module "application" { source = "./modules/application" # applicationモジュールがsecurityモジュールの副作用(SGルール適用)を # 前提とするが、outputを介して値を受け取っていない場合 depends_on = [module.security] }

ただし、このケースが本当に必要なのかを再度検討することをお勧めします。outputを介してSGのIDを渡せれば、depends_onなしで解決できます。

よくあるエラーと依存関係トラブルシューティング

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.

このエラーは参照先のリソースが存在しないことを示しています。依存関係の問題ではなく、参照先のリソース定義が欠落しているか、typoが原因です。

・参照式のリソース名・リソースタイプのスペルを確認する
・別のmoduleやファイルに定義があるならoutputを介して渡す設計に変更する

2. 「Error creating Instance: InvalidGroup.NotFound」が出る

Error: creating EC2 Instance: InvalidGroup.NotFound: The security group 'sg-0abc123' does not exist

指定したセキュリティグループIDがAWS上にまだ存在しない状態でEC2が作成されようとしています。依存関係が正しく設定されていないか、手動で削除されたIDを参照しています。

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

ただし、time_sleepは根本解決ではありません。設計上できる限り避け、IAMのポリシー伝播待ちが必要な場合は設計を見直すことが推奨されます。

本記事のまとめ

TerraformのリソースはHCL参照式から自動的に依存関係グラフが構築されます。depends_onは参照式では捕捉できない副作用依存にのみ使う「最後の手段」と位置づけることで、コードのメンテナンス性と計画精度を高く保てます。

やりたいこと 手段
リソースAの完成後にリソースBを作る(参照式あり) 参照式を書くだけ(暗黙依存が自動生成される)
副作用依存(IAMアタッチ後の権限適用など)を制御する depends_on = [リソース]
依存関係グラフを可視化する terraform graph | dot -Tpng -o graph.png
モジュール間の依存を表現する(推奨) output → 引数参照で暗黙依存を生成する
モジュール間の副作用依存を制御する depends_on = [module.name]
depends_onが必要か判断する 参照式で代替できないか先に確認する
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Terraformのdepends_on設計から実践的なIaC運用まで、ハンズオン形式で体系的に学べるセミナーを開催しています。
>> Terraform実践講座の詳細はこちら

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

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

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

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

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

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

登録10秒/合わなければ解除3秒 / 詳細はこちら

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

この記事を書いた人

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

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

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