depends_on — コードから見えない依存関係を明示するメタ引数

1. 概要

  • depends_onが必要な場面(暗黙的な依存が検出できないケース)
  • depends_onの基本構文
  • dataブロックへのdepends_on
  • モジュールへのdepends_on
  • 使いすぎによる弊害

Terraformは通常、リソース間の暗黙的な依存関係を自動検出します。しかし、コードからは依存が見えない場合(例: IAMロールのポリシー反映待ち、S3バケットポリシーの反映)は、depends_onで明示的に依存関係を設定する必要があります。


2. 暗黙的な依存 vs 明示的な依存

暗黙的な依存(depends_onは不要)

# aws_iam_role.lambda.arnを参照しているので、TerraformはLambdaがIAMロールの
# 作成後に作られることを自動的に理解する
resource "aws_iam_role" "lambda" {
  name               = "lambda-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_lambda_function" "main" {
  role = aws_iam_role.lambda.arn  # ← この参照が依存関係を示す
  # ...
}

明示的な依存が必要なケース

# IAMポリシーをアタッチしても、コード上はLambdaが参照していないため
# Terraformが依存関係を検出できない
resource "aws_iam_role_policy_attachment" "lambda" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

resource "aws_lambda_function" "main" {
  # iam_role_policy_attachmentを参照していないため、
  # ポリシーアタッチ前にLambdaが作成される可能性がある
  role = aws_iam_role.lambda.arn
  
  # ✅ depends_onで明示する
  depends_on = [aws_iam_role_policy_attachment.lambda]
}

3. 基本構文

resource "<TYPE>" "<NAME>" {
  # ...

  depends_on = [
    <resource_or_module_reference>,
    <resource_or_module_reference>,
  ]
}

4. 実用パターン

IAMロール + ポリシーアタッチメント + EC2

terraform {
  required_version = ">= 1.9"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

data "aws_iam_policy_document" "ec2_assume_role" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "app" {
  name               = "app-ec2-role"
  assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json

  tags = {
    Name      = "app-ec2-role"
    ManagedBy = "terraform"
  }
}

resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.app.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "app" {
  name = "app-instance-profile"
  role = aws_iam_role.app.name
}

data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_instance" "app" {
  ami                  = data.aws_ami.amazon_linux_2023.id
  instance_type        = "t3.micro"
  iam_instance_profile = aws_iam_instance_profile.app.name

  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }

  # ポリシーアタッチが完了してからインスタンスを作成する
  depends_on = [aws_iam_role_policy_attachment.ssm]

  tags = {
    Name        = "app"
    Environment = "dev"
    ManagedBy   = "terraform"
  }
}

S3バケットポリシー + Lambda

resource "aws_s3_bucket" "data" {
  bucket = "myapp-data-bucket"

  tags = {
    Name      = "myapp-data-bucket"
    ManagedBy = "terraform"
  }
}

resource "aws_s3_bucket_policy" "data" {
  bucket = aws_s3_bucket.data.id
  policy = data.aws_iam_policy_document.s3_policy.json
}

resource "aws_lambda_function" "processor" {
  filename      = "lambda.zip"
  function_name = "data-processor"
  role          = aws_iam_role.lambda.arn
  handler       = "index.handler"
  runtime       = "nodejs20.x"

  environment {
    variables = {
      BUCKET_NAME = aws_s3_bucket.data.bucket
    }
  }

  # バケットポリシーが反映されてからLambdaを作成する
  depends_on = [aws_s3_bucket_policy.data]
}

5. dataブロックへのdepends_on

dataブロックが同じapplyで作成するリソースに依存する場合は、depends_onが必要です。

resource "aws_iam_role" "lambda" {
  name               = "lambda-role"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

# ❌ このdataブロックはplanフェーズで評価されるが、
# lambda-roleはplanフェーズでまだ存在しない
data "aws_iam_role" "lambda" {
  name = "lambda-role"
}

# ✅ depends_onでリソース作成後に評価させる
data "aws_iam_role" "lambda" {
  name       = "lambda-role"
  depends_on = [aws_iam_role.lambda]
}

💡 そもそもdataが不要: 同じapplyで作成したリソースは、dataブロックを使わずに直接aws_iam_role.lambda.arnで参照するのがベストです。dataブロック + depends_onよりも、直接参照の方がシンプルです。


6. モジュールへのdepends_on

モジュール全体に依存する場合は、モジュールブロックにdepends_onを設定します。

module "networking" {
  source      = "./modules/networking"
  environment = var.environment
}

module "compute" {
  source      = "./modules/compute"
  environment = var.environment
  subnet_ids  = module.networking.private_subnet_ids

  # ネットワークモジュール全体の完了を待つ
  depends_on = [module.networking]
}

7. depends_onの使いすぎに注意

depends_onを多用すると、Terraformが並列実行できるリソースが減り、apply時間が長くなります。

# ❌ 不要なdepends_onを付けすぎると遅くなる
resource "aws_instance" "web" {
  depends_on = [
    aws_security_group.web,    # すでにsecurity_group_idで参照している
    aws_subnet.main,           # すでにsubnet_idで参照している
    aws_vpc.main,              # 間接参照で自動検出される
  ]
}

# ✅ 明示的な参照があればdepends_onは不要
resource "aws_instance" "web" {
  subnet_id              = aws_subnet.main.id              # 依存を自動検出
  vpc_security_group_ids = [aws_security_group.web.id]    # 依存を自動検出
}

8. 関連記事


9. まとめ

  • Terraformは参照(resource.type.name.attr)から暗黙的な依存を自動検出する
  • コードから見えない依存がある場合(IAMポリシー反映、バケットポリシー)はdepends_onを使う
  • dataブロックが同じapplyで作成するリソースに依存する場合はdepends_onが必要(または直接参照に変更する)
  • depends_onの多用は並列実行を阻害するため、本当に必要な場合だけ使う

動作確認バージョン: Terraform >= 1.9 / AWS Provider ~> 5.0 公式ドキュメント: https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on