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源查询结果:
- 直接管理VPC的默认路由表,替代
data.aws_route_tables查询 - 若需要额外路由表,通过
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参数分两步执行:
- 先创建VPC并查询路由表:
terraform apply -target=aws_vpc.main -target=data.aws_route_tables.rts
- 再执行完整的apply:
terraform apply
这种方式无需修改代码,但需要手动分阶段操作,适合临时场景。
内容的提问来源于stack exchange,提问作者Ivan Petrov
相关产品推荐
相关产品推荐

