「for_each map includes keys derived from resource attributes」エラーの解決方法

1. 概要

  • 「The ‘for_each’ map includes keys derived from resource attributes that cannot be determined until apply」エラー
  • countエラーとの違い
  • 原因とよくある発生パターン
  • 解決方法(toset()・変数化・段階的apply)

for_eachではcountと同様に、planフェーズで確定できない値をキーに使うとエラーになります。ただしfor_eachの場合、キー(key)が未知であることが問題であり、値(value)が未知の場合とは異なります。


2. エラーメッセージ

Error: Invalid for_each argument

  on main.tf line X, in resource "aws_route53_record" "records":
   X:   for_each = aws_instance.web

The "for_each" map includes keys derived from resource attributes that cannot
be determined until apply, so Terraform cannot predict how many instances will
be created. To work around this, use the -target argument to first apply only
the resources that for_each depends on.

3. エラーの再現例

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

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

variable "environment" {
  description = "環境名"
  type        = string
  default     = "dev"
}

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

resource "aws_instance" "web" {
  count         = 3
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"

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

  tags = {
    Name        = "${var.environment}-web-${count.index + 1}"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

# ❌ エラー: aws_instance.web の id はapplyまで確定しない
resource "aws_eip_association" "web" {
  for_each    = { for i, inst in aws_instance.web : inst.id => inst }
  #                                                   ^^^^^^ applyまで不明
  instance_id = each.value.id
  allocation_id = "eipalloc-xxxxx"
}

4. 解決方法1: 既知の値をキーに使う(推奨)

# ✅ countのインデックスをキーに使う(planフェーズで確定できる)
resource "aws_eip_association" "web" {
  for_each = { for i in range(3) : i => aws_instance.web[i] }

  instance_id   = each.value.id
  allocation_id = "eipalloc-xxxxx"
}
# ✅ さらに良い方法: 変数のsetをfor_eachに使い、インスタンスもfor_eachで作る
variable "web_servers" {
  description = "Webサーバーの定義"
  type        = set(string)
  default     = ["web-1", "web-2", "web-3"]
}

resource "aws_instance" "web" {
  for_each      = var.web_servers
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"

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

  tags = {
    Name        = "${var.environment}-${each.key}"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

# for_eachのキーが var.web_servers(planで確定済み)なのでエラーにならない
resource "aws_eip" "web" {
  for_each = var.web_servers

  tags = {
    Name        = "${var.environment}-${each.key}-eip"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

5. 解決方法2: -targetで段階的にapply(応急処置)

# ステップ1: インスタンスだけ先にapply
terraform apply -target=aws_instance.web

# ステップ2: インスタンスのIDが確定した状態で全体apply
terraform apply

6. countとfor_eachエラーの違い

比較countエラーfor_eachエラー
エラーになる場所countの数が未知for_eachのキーが未知
値が未知でもエラー?はいキーが既知なら値が未知でもOK
よくある原因length(resource.attr){ for x in resource : x.id => x }
応急処置-target-target

7. 関連記事


8. まとめ

  • for_eachのキーがapplyまで確定しない属性(新規リソースのIDなど)に依存するとエラー
  • 根本解決はfor_eachのキーに変数やリテラルなど「planで確定できる値」を使う
  • インスタンスをcountで作る場合はrange(n)でキーを生成する
  • インスタンスもfor_eachで作るなら変数のsetを共通のキーとして使う
  • 応急処置は-targetで依存リソースを先にapply

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