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

Terraform中foreach遍历嵌套数据及flatten函数用法求助

Terraform vSphere 动态配置network数据源解决方案

一、先搞懂flatten函数到底怎么用

flatten的作用就是把嵌套的列表/元组“拍扁”成一维结构。比如你有[[1,2], [3,4]],用flatten处理后就变成[1,2,3,4]。但要注意两个关键点:

  • 它只认列表/元组类型的嵌套,如果嵌套里是map,不会自动拆解map的键值对
  • 输入是tuple的话,输出也是tuple;输入是list则输出list。而for_each根本不认tuple——它只接受map类型,或者元素是字符串/数字的set,这就是你报错的核心原因。

二、针对你的场景的正确操作步骤

假设你的virtual_machines变量定义大概是这样(先对齐基础结构):

variable "virtual_machines" {
  type = list(object({
    name       = string
    interfaces = list(object({
      network_name = string
      # 其他接口属性比如vlan_id之类的...
    }))
  }))
}

1. 用flatten+for表达式生成一维接口列表,再转成map

先把每个虚拟机的interfaces拍平,再转成map(给for_each用的必须有唯一key):

locals {
  # 第一步:把所有虚拟机的接口拍平成一维列表,每个条目带虚拟机名称、接口索引、网络名
  flattened_interfaces = flatten([
    for vm in var.virtual_machines : [
      for idx, intf in vm.interfaces : {
        vm_name      = vm.name
        interface_idx = idx
        network_name = intf.network_name
      }
    ]
  ])

  # 第二步:转成map,用虚拟机名+接口索引作为唯一key,值是网络名
  vm_network_map = {
    for intf in local.flattened_interfaces : "${intf.vm_name}-${intf.interface_idx}" => intf.network_name
  }
}

2. 给network数据源配置for_each

现在local.vm_network_map是符合要求的map,直接用就行:

data "vsphere_network" "vm_networks" {
  for_each = local.vm_network_map

  name          = each.value
  datacenter_id = data.vsphere_datacenter.dc.id
}

三、优化:避免重复创建相同网络的数据源

如果多个虚拟机用同一个网络,上面的方法会生成多个重复的data资源,没必要。可以先提取所有唯一的网络名称:

locals {
  # 提取所有不重复的网络名称,先拍平再去重
  unique_network_names = distinct(flatten([
    for vm in var.virtual_machines : [for intf in vm.interfaces : intf.network_name]
  ]))

  # 转成map(用网络名本身当key,前提是你的网络在数据中心里名称唯一)
  unique_network_map = { for name in local.unique_network_names : name => name }
}

然后数据源用这个map,就能做到一个网络只创建一个data资源:

data "vsphere_network" "vm_networks" {
  for_each = local.unique_network_map

  name          = each.value
  datacenter_id = data.vsphere_datacenter.dc.id
}

四、为啥你之前直接用flatten会报错?

因为flatten返回的是tuple,而for_each不接受tuple类型。必须把tuple转成map或者set(distinct处理后的list会被Terraform当成set)。比如你也可以直接这么写(前提是网络名唯一):

data "vsphere_network" "vm_networks" {
  for_each = distinct(flatten([
    for vm in var.virtual_machines : [for intf in vm.interfaces : intf.network_name]
  ]))

  name          = each.value
  datacenter_id = data.vsphere_datacenter.dc.id
}

内容的提问来源于stack exchange,提问作者Roman Qwertoff

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:50:24