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
相关产品推荐
相关产品推荐

