You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform中for_each遇‘known only after apply’问题求解

解决Terraform for_each依赖未创建资源导致的"仅在apply后可知"错误

问题场景

新建VPC后尝试自动为其所有路由表添加指向VPC对等连接的路由条目,但Terraform在plan阶段无法确定新建VPC的路由表ID集合,导致local.bar的值仅能在apply后获取,触发for_each参数无效的错误。

原代码

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

# Find the corresponding routing table
data "aws_route_tables" "rts" {
  vpc_id = aws_vpc.main.id

  depends_on = [ aws_vpc.main ]
}

resource "aws_route" "rs" {
  for_each                  = { for index, entry in local.bar : "${entry.route_table_id}.${entry.zone_cidr}" => entry }
  route_table_id            = each.value.route_table_id
  destination_cidr_block    = each.value.zone_cidr
  vpc_peering_connection_id = data.aws_vpc_peering_connection.accepter.id
}

locals {
  bar = flatten([
    for route_table_id in toset(data.aws_route_tables.rts.ids) : [
      for zone_info in foo.main.zone_info : {
        route_table_id = route_table_id
        zone_cidr      = zone_info.cidr
      }
    ]
  ])
}

错误信息

Error: Invalid for_each argument

on .terraform/modules/abcde/main.tf line 88, in resource "aws_route":
80: for_each = { for index, entry in local.bar : "${entry.route_table_id}.${entry.zone_cidr}" => entry }

| local.bar will be known only after apply

The "for_each" map includes keys derived from resource attributes that cannot
be determined until apply, and so Terraform cannot determine the full set of
keys that will identify the instances of this resource.

When working with unknown values in for_each, it's better to define the map
keys statically in your configuration and place apply-time results only in
the map values.

Alternatively, you could use the -target planning option to first apply only
the resources that the for_each value depends on, and then apply a second
time to fully converge.

解决方法

方案1:显式管理路由表(推荐)

核心思路是让所有路由表ID在plan阶段即可确定,避免依赖apply后才能获取的data源查询结果:

  1. 直接管理VPC的默认路由表,替代data.aws_route_tables查询
  2. 若需要额外路由表,通过count或for_each显式创建,确保数量和ID在plan阶段可知

修改后的代码示例:

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

# 显式接管默认路由表,保留必要的默认路由
resource "aws_default_route_table" "main" {
  default_route_table_id = aws_vpc.main.default_route_table_id

  # 示例:保留本地路由(根据实际需求调整)
  route {
    cidr_block = "10.0.0.0/16"
    gateway_id = "local"
  }
}

# (可选)显式创建额外路由表,数量由静态变量控制
resource "aws_route_table" "additional" {
  count  = 2 # 静态数量,plan阶段可推断
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "10.0.0.0/16"
    gateway_id = "local"
  }
}

# 合并所有已知的路由表ID
locals {
  all_route_table_ids = concat(
    [aws_default_route_table.main.id],
    aws_route_table.additional[*].id
  )

  bar = flatten([
    for route_table_id in local.all_route_table_ids : [
      for zone_info in foo.main.zone_info : {
        route_table_id = route_table_id
        zone_cidr      = zone_info.cidr
      }
    ]
  ])
}

resource "aws_route" "rs" {
  for_each                  = { for entry in local.bar : "${entry.route_table_id}.${entry.zone_cidr}" => entry }
  route_table_id            = each.value.route_table_id
  destination_cidr_block    = each.value.zone_cidr
  vpc_peering_connection_id = data.aws_vpc_peering_connection.accepter.id
}

这种方式下,所有路由表ID都来自显式创建的资源或VPC的已知属性,local.bar的结构在plan阶段完全可推断,for_each能正常工作。

方案2:分阶段apply(临时应急)

如果无法修改路由表的创建方式,可以通过-target参数分两步执行:

  1. 先创建VPC并查询路由表:
terraform apply -target=aws_vpc.main -target=data.aws_route_tables.rts
  1. 再执行完整的apply:
terraform apply

这种方式无需修改代码,但需要手动分阶段操作,适合临时场景。

内容的提问来源于stack exchange,提问作者Ivan Petrov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:45:31