Terraform vSphere提供程序迭代网卡嵌套映射配置方法
如何遍历嵌套映射为Terraform数据资源块分配字符串类型值
场景说明
基于Terraform vSphere 2.0提供程序通过OVA模板批量部署多台虚拟机时,不同环境的虚拟机网络接口配置存在差异,OVA模板仅预置所有环境通用的全局网络接口,需要通过vsphere_network数据源查询各虚拟机网卡对应的分布式虚拟端口组(DVPG)ID。
目标实现逻辑:通过单个vsphere_network数据块迭代获取所有DVPG ID,再通过单个虚拟机资源块结合dynamic网络接口块完成所有虚拟机部署。
现有基础配置
变量定义
variable "vmconfig" { description = "Map of VM name => Configs " type = map(object({ name = string cpus = number memory = number folder = string remote_ovf = string netint = map(string) })) default = {} }
.tfvars配置示例
vmconfig = { "vm1" = { name = "vm1" cpus = 4 memory = 16384 folder = "foo/bary" remote_ovf = "foo.bar.ova" netint = { nic1 = "segment1", nic2 = "segment2", nic3 = "segment3", nic4 = "segment4" } }, "vm2" = {...} }
本地值转换配置
locals { vm_values = { for name, config in var.vmconfig : name => { vm_name = config.name num_cpus = config.cpus memory = config.memory folder = config.folder remote_ovf_url = config.remote_ovf netint = config.netint } } }
初始错误实现
最初尝试直接遍历VM配置给数据源传值,同时虚拟机资源未做VM维度遍历:
data "vsphere_network" "nicint" { for_each = local.vm_values name = each.value.netint datacenter_id = data.vsphere_datacenter.dc.id } resource "vsphere_virtual_machine" "vm" { # 省略其他VM配置 dynamic "network_interface" { for_each = data.vsphere_network.nicint content { network_id = network_interface.value.id } } }
该写法核心问题:each.value.netint是map(string)类型,直接传给要求字符串入参的name字段会触发类型不匹配报错,且虚拟机资源未按VM维度遍历,所有网卡会被错误挂载到单台VM上。
已尝试方案及问题
- 方案1:直接映射netint为for_each值
触发报错:属性data "vsphere_network" "nicint" { for_each = {for k,v in local.vm_values : k => v.netint} name = each.value datacenter_id = data.vsphere_datacenter.dc.id }name值不合法,需要字符串类型,实际传入each.value为包含4个元素的字符串map,类型不匹配。 - 方案2:merge拼接带VM前缀的网卡映射
该写法可临时运行,但资源键值设计不合理,后续调整网卡配置时Terraform无法正确识别存量资源关联关系,会触发资源销毁重建,无法完成原地更新。locals { netint_map = merge([ for vmtype, values in var.vmconfig: { for netint in values.netint: "${vmtype}-${netint}" => {vmtype = vmtype, netint = netint} } ]...) } data "vsphere_network" "nicint" { for_each = local.netint_map name = each.value datacenter_id = data.vsphere_datacenter.dc.id }
正确实现方案
核心逻辑分三步:扁平化嵌套网卡配置、去重重复的网络段减少冗余查询、虚拟机资源按VM维度遍历后动态生成对应网卡配置。
第一步:补全本地值逻辑
locals { vm_values = { for name, config in var.vmconfig : name => { vm_name = config.name num_cpus = config.cpus memory = config.memory folder = config.folder remote_ovf_url = config.remote_ovf netint = config.netint } } # 提取所有不重复的DVPG段名,避免重复查询同一个端口组 unique_dvpg_segments = toset(flatten([ for vm_name, vm_conf in local.vm_values : values(vm_conf.netint) ])) }
第二步:配置vsphere_network数据源
同个DVPG仅生成一个数据源实例,减少API调用,键为稳定的段名,不会触发无意义重建:
data "vsphere_network" "nicint" { for_each = local.unique_dvpg_segments name = each.value datacenter_id = data.vsphere_datacenter.dc.id }
第三步:修正虚拟机资源配置
虚拟机资源本身用for_each遍历所有VM配置,内部dynamic块仅遍历当前VM对应的网卡,直接通过段名索引已查询的DVPG ID:
resource "vsphere_virtual_machine" "vm" { for_each = local.vm_values name = each.value.vm_name num_cpus = each.value.num_cpus memory = each.value.memory folder = each.value.folder # 其余OVA部署配置(remote_ovf_url、磁盘配置等)按原有逻辑补全 # ... dynamic "network_interface" { for_each = each.value.netint content { network_id = data.vsphere_network.nicint[network_interface.value].id } } }
该方案优势:
- 类型完全匹配:
name参数始终接收字符串类型的段名,无类型报错 - 资源状态稳定:所有资源的for_each键与配置强绑定,调整单台VM的单个网卡时仅变更对应资源,不会触发全量销毁重建
- 执行效率高:同个DVPG仅查询一次,减少不必要的vSphere API调用
- 配置对齐:网卡顺序与tfvars中定义的nic1、nic2顺序完全一致,不会出现网卡错位问题
内容的提问来源于stack exchange,提问作者yudodisterra
相关产品推荐
相关产品推荐

