依存関係管理のベスト プラクティス

このドキュメントでは、Terraform 構成でリソース間の依存関係を表す際の推奨事項について説明します。

明示的な依存関係よりも暗黙的な依存関係を優先する

リソースの依存関係は、あるリソースが他のリソースの存在に依存する場合に発生します。リソースが正しい順序で作成されるように、Terraform はこれらの依存関係を認識している必要があります。たとえば、リソース A がリソース B に依存する場合、リソース B はリソース A より前に作成されます。

Terraform 構成の依存関係は、暗黙的および明示的な依存関係宣言によって記述します。暗黙的な依存関係は式参照で宣言し、明示的な依存関係は depends_on メタ引数で指定します。depends_on 引数は、リソースまたはモジュールが依存するオブジェクトに対する処理をすべて完了した後に、依存関係のあるオブジェクトを処理することを Terraform に指示します。

どちらのアプローチでも正しい順序で処理が行われますが、リソースの更新と置換を計画する段階では、多くの場合、暗黙的な依存関係のほうが効率的です。Terraform は、暗黙的な依存関係に関連する特定のフィールドをインテリジェントに追跡し、これらのフィールドが依存関係内で変更されない場合は、依存関係にあるリソースの変更を回避します。

明示的な依存関係は、暗黙的な依存関係ほど具体的な情報を伝達しません。Terraform が依存関係を構成する特定の属性を認識していない場合、リソースの作成、更新、置換に対してより慎重なプランしか策定されません。これは、Terraform がリソースを作成する順序だけでなく、リソースの更新や置換が必要かどうかを Terraform が判断する方法にも影響します。

2 つのリソース間の依存関係が明確でなく、暗黙的な依存関係では表すことが場合に限り、depends_on メタ引数で明示的な依存関係を指定することをおすすめします。

次の例では、BigQuery データセットを作成する前に、必要なプロジェクト サービスが有効になっている必要があります。この依存関係は明示的に宣言されています。

非推奨:

module "project_services" {
  source  = "terraform-google-modules/project-factory/google//modules/project_services"
  version = "~> 14.4"

  project_id = var.project_id
  activate_apis = [
    "bigquery.googleapis.com",
    "bigquerystorage.googleapis.com",
  ]
}

module "bigquery" {
  source       = "terraform-google-modules/bigquery/google"
  version      = "~> 5.4"

  dataset_id   = "demo_dataset"
  dataset_name = "demo_dataset"
  project_id   = var.project_id
  depends_on = [module.project_services] # <- explicit dependency
}

次の例では、project_id 引数を project_services リソースの project_id 出力属性として参照することで、明示的な依存関係を暗黙的な依存関係に置き換えています。

推奨:

module "bigquery" {
  source       = "terraform-google-modules/bigquery/google"
  version      = "~> 5.4"

  dataset_id   = "demo_dataset"
  dataset_name = "demo_dataset"
  project_id   = module.project_services.project_id # <- implicit dependency
}

暗黙的な依存関係を使用すると依存関係を正確に宣言できます。たとえば、アップストリーム オブジェクトから収集する必要のある情報を正確に指定できます。また、変更が必要な個所が少なくなるため、エラーが発生するリスクも低減します。

依存関係にあるリソースからの出力属性を参照する

アップストリーム リソースからの値を参照して暗黙的な依存関係を作成する場合は、出力属性(特にまだ不明な値)のみを参照するようにしてください。これにより、Terraform はアップストリーム リソースが作成されるまで、現在のリソースをプロビジョニングしません。

次の例では、google_storage_bucket_object リソースが google_storage_bucket リソースの名前引数を参照しています。これらの引数は、Terraform の計画フェーズで既知の値になります。つまり、既知の引数(バケット名)を参照しても google_storage_bucket_objectgoogle_storage_bucket の間に暗黙的な依存関係が作成されないため、google_storage_bucket_object リソースを作成するときに Terraform は google_storage_bucket リソースの作成を待ちません。このため、2 つのリソース間の暗黙的な依存関係を宣言する意味がなくなります。

非推奨:

# Cloud Storage bucket
resource "google_storage_bucket" "bucket" {
  name = "demo-bucket"
  location = "US"
}

resource "google_storage_bucket_object" "bucket_object" {
  name   = "demo-object"
  source = "./test.txt"
  bucket = google_storage_bucket.bucket.name # name is an input argument
}

代わりに、google_storage_bucket_object リソースは google_storage_bucket_object リソースの id 出力属性を参照する必要があります。id フィールドは出力属性であるため、その値はリソースの作成が実行された後にのみ設定されます。Terraform は、google_storage_bucket_object リソースの作成が完了してから google_storage_bucket_object リソースの作成を開始します。

推奨:

resource "google_storage_bucket_object" "bucket_object" {
  name   = "demo-object"
  source = "./test.txt"
  bucket = google_storage_bucket.bucket.id # id is an output attribute
}

参照する出力属性が明確でない場合があります。たとえば、次の例では、module_a は生成されたファイルの名前を入力として受け取ります。module_a 内では、ファイル名を使用してファイルを読み取ります。このコードをそのまま実行すると、no such file or directory 例外が発生します。この状況は、Terraform が計画フェーズでファイルを読み取ろうとすると発生します(つまり、その時点ではまだファイルが作成されていません)。この場合、local_file リソースの出力属性を調べると、ファイル名入力引数に代わって使用可能な明確なフィールドがないことがわかります。

非推奨:

resource "local_file" "generated_file" {
 filename = "./generated_file.text"
 content = templatefile("./template.tftpl", {
   project_id = var.project_id
 })
}

module "module_a" {
 source = "./modules/module-a"
 root_config_file_path = local_file.generated_file.filename
}

明示的な依存関係を導入することで、この問題を解決できます。明示的な依存関係が必要な理由をコメントとして必ず追加することをおすすめします。

推奨:

module "module_a" {
 source = "./modules/module-a"
 root_config_file_path = local_file.generated_file.filename
 depends_on = [local_file.generated_file] # waiting for generated_file to be created
}

次のステップ