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

Terraform访问嵌套对象报错:for_each需为映射或字符串集合

问题:Terraform vSphere Provider 多NIC虚拟机模块报错(for_each类型不匹配)

我使用hashicorp/vsphere Provider搭建Terraform流水线,尝试通过tfvars文件创建带2个NIC的虚拟机,并将vsphere_virtual_machine资源封装为模块,但执行时持续报错。

相关代码与配置

tfvars文件结构(Terraform\VMWare\NewBuildCluster\vmware_server_build.tfvars)

tfvars = {
  # Core vSphere Vars
  vsphere_server            = "vcentre.example.com" # Address of vCentre
  vsphere_datacenter        = "vsphere_datacenter" # Location to build new servers
  vsphere_datastore         = "datastore0001" # Data store to save VM files
  vsphere_cluster           = "NewBuildCluster" # Cluster to build on

  #VM Related Vars
  vm = {
    TestVM01 = {
      vsphere_vm_template             = "WindowsTemplate2022" # Name of template
      vsphere_vm_name                 = "TestVM01" # Name of VM
      vsphere_vm_cpu_cores            = 4 # Total cores
      vsphere_vm_num_cores_per_socket = 2 # Cores per socket
      vsphere_vm_memory               = 8 # RAM assignment (Math conducted in module for MB conversion)

      #VM Customisation Variables
      vm_nics = {
        eth0 = {
          vsphere_vm_network        = "VLAN10" # Name of Virtual Network (defined in vSphere)
          vsphere_vm_domain         = "ad.example.com" # Domain to join server to once built
          vsphere_vm_ip             = "10.200.10.240" # Static assigned IP address
          vsphere_vm_ip_gateway     = "10.200.10.1" # Gateway IP Address
          vsphere_vm_ip_dnslist     = ["10.200.10.10", "10.200.10.30"]  # List of DNS addresses
        },
        eth1 = {
          vsphere_vm_network        = "VLAN10" # Name of Virtual Network (defined in vSphere)
          vsphere_vm_domain         = "ad.example.com" # Domain to join server to once built
          vsphere_vm_ip             = "10.200.10.241" # Static assigned IP address
          vsphere_vm_ip_gateway     = "10.200.10.1" # Gateway IP Address
          vsphere_vm_ip_dnslist     = ["10.200.10.10", "10.200.10.30"]  # List of DNS addresses
        }
      }

      data_disk = {
        disk1 = {
          size_gb = 50 # Data disk size
        },
        disk2 = {
          size_gb = 10 # Data disk size
        }
      }
    }
  }
}

网络Data块代码(Pipeline\VMWare\main.tf)

data "vsphere_virtual_machine" "network" {
  for_each = flatten([
    for vm_key, vm_value in var.tfvars.vm : [
      for nic_key, nic_value in vm_value.vm_nics : {
        name         = nic_value.vsphere_vm_network
        datacenter_id = data.vsphere_datacenter.datacenter.id
      }
    ]
  ])

  name          = each.value.name
  datacenter_id = each.value.datacenter_id
}

报错信息

│ The given "for_each" argument value is unsuitable: the "for_each" argument
│ must be a map, or set of strings, and you have provided a value of type
│ tuple.

解决方法

错误原因

flatten()函数返回的是tuple(元组)类型,但Terraform要求for_each的输入必须是map(映射)或字符串集合(set of strings),因此类型不匹配导致报错。同时,原代码中误用了data "vsphere_virtual_machine",实际应该使用data "vsphere_network"来获取vSphere网络资源。

方案1:生成唯一键的Map供for_each使用

修改Data块代码,为每个网络条目生成唯一键,将tuple转换为map:

data "vsphere_network" "network" {
  for_each = tomap({
    for nic_entry in flatten([
      for vm_key, vm_value in var.tfvars.vm : [
        for nic_key, nic_value in vm_value.vm_nics : {
          key           = "${vm_key}-${nic_key}-${nic_value.vsphere_vm_network}"
          name          = nic_value.vsphere_vm_network
          datacenter_id = data.vsphere_datacenter.datacenter.id
        }
      ]
    ]) : nic_entry.key => nic_entry
  })

  name          = each.value.name
  datacenter_id = each.value.datacenter_id
}
  • 内部循环中通过vm_key(VM名称)、nic_key(NIC名称)和vsphere_vm_network(网络名称)组合生成唯一键,避免重复
  • 使用for表达式将flatten后的tuple转换为以唯一键为索引的map
  • 外层用tomap()确保类型符合for_each要求

方案2:去重获取网络资源(更高效)

如果多个NIC使用同一个网络,可以先提取所有唯一的网络名称,再创建Data块,避免重复获取同一网络资源:

data "vsphere_network" "network" {
  for_each = toset(flatten([
    for vm_value in var.tfvars.vm : [
      for nic_value in vm_value.vm_nics : nic_value.vsphere_vm_network
    ]
  ]))

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

后续在模块中引用网络ID时,直接使用data.vsphere_network.network[nic_value.vsphere_vm_network].id即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:24:55