【ITニュース解説】Terraform Gotchas: Data Sources and depends_on
2025年09月28日に「Dev.to」が公開したITニュース「Terraform Gotchas: Data Sources and depends_on」について初心者にもわかりやすく解説しています。
ITニュース概要
Terraformで複数のモジュールを連携させる際、`depends_on`を安易に使うと、データソースの挙動により関係ないリソースまで予期せぬ変更対象となる場合がある。これを防ぐには、`depends_on`は最終手段とし、モジュール間で必要な値を出力・変数で渡すことで、より正確な依存関係を構築するべきだ。
ITニュース解説
Terraformは、クラウドやオンプレミスなどのインフラをコードとして管理する「Infrastructure as Code(IaC)」を実現するための非常に人気のあるツールである。インフラの構築や変更をコードで記述することで、手動操作によるミスを防ぎ、繰り返し実行可能で一貫性のある環境を簡単に作成できる利点がある。
このTerraformを使う上で「データソース」という機能は重要だ。データソースは、既に存在するクラウドプロバイダー(AWS、Azureなど)上のリソースや設定から情報を取得するために利用される。例えば、AWSのアカウントIDを取得したり、特定の条件に基づいてIAMポリシーのドキュメントを作成したり、さらにはローカルファイルをTerraformの設定で利用できるようにしたりと、非常に多様な用途に使える柔軟な機能である。
しかし、このデータソースと、Terraformの「モジュール」と呼ばれる機能、そしてモジュール間の依存関係を指定する「depends_on」という特殊な属性を組み合わせると、予期せぬ問題が発生することがある。具体的には、あるモジュールでの些細な変更が、別のモジュール内の全く関係ないリソースの再構築を引き起こしてしまうという現象だ。これはシステムを安定運用する上で避けたい事態である。
この問題を具体的に見てみよう。仮に、ルートとなるプロジェクトのディレクトリがあり、その中に二つの子モジュール「Module A」と「Module B」があると想定する。Module Aでは、アプリケーションのログを保存するためのCloudWatchロググループと、設定値を保存するためのSSMパラメータ(例えばアプリケーションID「1234567890」)を作成している。一方、Module Bでは、アプリケーションを実行するためのECSタスク定義を作成している。このタスク定義は、ログをModule Aで作成したCloudWatchロググループに書き込むように設定されている。
そして、ルートのmain.tfファイルでこれら二つのモジュールを呼び出す際に、Module BがModule Aに依存するように「depends_on = [module.moduleA]」という設定を追加する。このdepends_onは、Module Bの全てのリソースが作成される前に、Module Aの全てのリソースが確実に準備されていることを保証するための簡単な方法である。この設定により、例えば、Module Bのタスク定義が作られる前にModule Aのロググループが確実に存在することが保証される。
この状態でTerraformの初期化、計画、適用を行うと、CloudWatchロググループ、SSMパラメータ、ECSタスク定義の三つのリソースが新規作成される。もしここでModule A内のSSMパラメータの値だけを「1234567890」から別の値に変更して再度計画を実行すると、TerraformはSSMパラメータのみが更新されることを示す。この時点では、期待通りの予測可能な動作である。
問題は、ここにデータソースが絡んでくると発生する。例えば、Module Bのmain.tfに、AWSアカウントの情報を取得するデータソース「aws_caller_identity」と、それを参照して名前を決定するIAMロール「aws_iam_role」を追加するとどうなるか。IAMロールの名前は「app_role_」に続けてaws_caller_identityで取得したアカウントIDを使用する設定だ。
この変更を加えた後、再びModule AのSSMパラメータの値を変更し、Terraformの計画を実行してみよう。すると、SSMパラメータの変更に加えて、Module Bで追加したIAMロールも「置き換え」(再作成)の対象として表示される。IAMロールもデータソースも、Module Aの変更とは直接関係がないにもかかわらず、なぜこのような予期せぬ再作成が計画されてしまうのだろうか。
その理由は、Module Bに設定した「depends_on = [module.moduleA]」という広範な依存関係にある。この設定は、Terraformに対して「Module Aのどんな変更もModule B全体に影響を与える可能性がある」と伝えていると解釈できる。データソースは、実行時に情報を取得する性質があるため、Terraformはdepends_onによってModule Aに何らかの変更があった場合、Module B内のデータソースやそれを参照するリソースが影響を受ける可能性があると保守的に判断するのだ。結果として、直接変更されていないIAMロールまでもが再評価され、計画上は再作成されることになってしまう。特定のクラウドプロバイダーの環境によっては、depends_on = [module.moduleA.aws_cloudwatch_log_group]のように特定のModule Aのリソースに限定しても解決しない場合がある。
このような予期せぬ変更の連鎖を防ぎ、より予測可能なインフラ管理を実現するための解決策は、depends_on属性を極力使用しないことである。Terraformは、リソースが別のリソースの属性値を参照している場合、自動的に正確な依存関係を構築する能力が非常に高い。
具体的な修正方法としては、以下の手順を取る。まず、Module Aで作成したCloudWatchロググループの名前を、Module Aの「出力」(output)として公開する。これにより、他のモジュールがこの値を利用できるようになる。次に、Module Bで、このロググループ名を受け取るための「入力変数」(variable)を定義する。そして、Module BのECSタスク定義内のロググループ名を、ハードコードされた文字列から、この入力変数に置き換える。最後に、ルートのmain.tfでModule Bを呼び出す際に、以前設定していた「depends_on」属性を削除し、代わりにModule Aの出力として公開したロググループ名を、Module Bの入力変数に直接渡すように変更する。
このように設定を変更し、再度SSMパラメータの値を変更して計画を実行すると、今度はSSMパラメータのみが変更対象として表示され、IAMロールは無関係な変更の影響を受けなくなる。これは、TerraformがModule Aのロググループ名とModule Bのタスク定義の間にのみ具体的な依存関係を認識し、それ以外のリソースには不要な影響を与えないためである。
結論として、depends_onメタ属性は、特定のリソースを参照できない場合や、一時的な急場しのぎの解決策として有用な場面もある。しかし、安定したインフラ運用を目指すのであれば、その使用は最後の手段と考えるべきだ。可能な限り、リソースの出力と入力変数を介して、リソース間の具体的な参照に基づいた依存関係を構築することが推奨される。これにより、Terraformが自動的に正確な実行順序を判断し、予期せぬ変更や再構築といったトラブルを回避できるようになる。インフラはビジネス目的を達成するための基盤であり、その安定性と予測可能性は非常に重要であるため、こうした依存関係の管理には特に注意を払うことが望ましい。