yudodisterra
yudodisterra

Reputation: 13

Iterating network interfaces in vsphere provider with Terraform

Question: How can I iterate through a nested map to assign string values for a data resource block?

Context: Working on a requirement to deploy multiple VMs via OVA template using the vsphere provider 2.0 on terraform.

As the network interfaces will vary according to the environment, the OVA template will only include the "global" network interface common to all VMs in any environment.

I am using the vsphere_network data resource to retrieve the distributed virtual port group ID for each network interface being assigned to the VMs.

Currently stuck on a variable interpolation to iterate through this info to assign to each vm resource in terraform.

1 vsphere network data block to iterate all DVPG ids, and 1 vm resource block to deploy all vms with those DVPGs using dynamic network interface block

VM Configuration Variable:

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" = {...}, etc.

Calling the variable above into a local var:

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
    }
  }
}

Trying to iterate through each value for netint inside the data resource block using for_each instead of count(listed as best practices for the vm type being deployed):

data "vsphere_network" "nicint" {
   for_each         = local.vm_values
   name             = each.value.netint
   datacenter_id    = data.vsphere_datacenter.dc.id

}

This data resource block is then called inside the VM resource block using dynamic:

resource "vsphere_virtual_machine" "vm" {
.
.
.

    dynamic "network_interface" {
        for_each = data.vsphere_network.nicint
        content {
          network_id = network_interface.value.id
        }
      }
}

The issue I'm having is iterating through each value inside netint, I get the inkling that I might be missing something trivial here, would appreciate your support in defining that for_each iteration accurately so that multiple vsphere_network data sources are available programmatically using that one data block.

I have tried the following variations for iterating in the data block:

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

}

Error I get is: Inappropriate value for attribute "name": string required each.value is a map of string with 4 elements

I tried using merge, it works! BUT it ended up creating duplicates for each VM and wouldn't modify an existing resource, but destroy and create another.

Another local variable created to map the network interface segments:

  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

}

Dear Hivemind, please guide me to optimize this effectively - thank you!!

Upvotes: 1

Views: 1924

Answers (1)

Marcin
Marcin

Reputation: 238081

Your merge is correct. So I just post it here for reference:

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

}

I think the issue is with your dynamic block. namely, instead of for_each = data.vsphere_network.nicint you should iterate over nicint from your variable, not data source.

resource "vsphere_virtual_machine" "vm" {

    for_each = var.vmconfig
    
    #...

    dynamic "network_interface" {
        for_each     = toset(each.value.netint)
        content {
          network_id = data.vsphere_network.nicint["${each.key}-${network_interface.key}"].id
        }
      }
}

Upvotes: 1

Related Questions