loopsterraformvsphere

terraform: iterate over key-value pairs of map


since I'm very new in terraform :

I'm trying to deploy a K8s cluster on vsphere

.... terraform.tfvars

vms = {
  master_vm1 = { name = "master01", vm_disk_size = 50, vm_vcpu = 2, vm_memory = 2048, vm_ip = "192.168.50.66"},
  master_vm2= { name = "master02", vm_disk_size = 50, vm_vcpu = 4, vm_memory = 4096, vm_ip = "192.168.50.77"},
  master_vm3 = { name = "master03", vm_disk_size = 50, vm_vcpu = 4, vm_memory = 4096, vm_ip = "192.168.50.88"},
  worker_vm1 = { name = "worker101", vm_disk_size = 80, vm_vcpu = 2, vm_memory = 2048, vm_ip = "192.168.50.166"},
  worker_vm2= { name = "worker102", vm_disk_size = 80, vm_vcpu = 4, vm_memory = 4096, vm_ip = "192.168.50.177"},
  worker_vm3 = { name = "worker103", vm_disk_size = 80, vm_vcpu = 4, vm_memory = 4096, vm_ip = "192.168.50.188"},

main.tf

resource "vsphere_virtual_machine" "vm" {
  for_each              = var.vms
..
..
  name                  = format("${each.value.name}.${var.vm_domain}")
  num_cpus              = var.vm_vcpu == false ? var.vm_vcpu : each.value.vm_vcpu
  memory                = var.vm_memory == false ? var.vm_memory : each.value.vm_memory
  firmware              = var.vm_firmware
  folder                = var.vm_folder
  cpu_hot_add_enabled      = "true"
  memory_hot_add_enabled   = "true"
  disk {
    label               = var.vm_disk_label
    size                = each.value.vm_disk_size
    thin_provisioned    = var.vm_disk_thin
  }

Using this approach the deployment works flawless

However, if I try to simply the terraform.tfvars...

nodes = {
   master_nodes ={
    master_vm1 ={ name = "master101", vm_ip = "192.168.50.66"}
    master_vm2 ={ name = "master102", vm_ip = "192.168.50.77"}
    master_vm3 ={ name = "master103", vm_ip = "192.168.50.88"}
  }
   worker_nodes ={
    worker_vm1 ={ name = "worker101", vm_ip = "192.168.50.166"}
    worker_vm2 ={ name = "worker102", vm_ip = "192.168.50.177"}
    worker_vm3 ={ name = "worker103", vm_ip = "192.168.50.188"}
  }
}

I failed to understand how to iterate over nodes ....


Solution

  • You have to flatten your nodes variable, e.g.:

    locals {
    
      nodes_flat = merge(flatten([
        for node_key, node_values in var.nodes: {
          for sub_node_key, node_details in node_values: 
                "${node_key}-${sub_node_key}" => {
                     "node_key" = node_key
                     "sub_node_key" = sub_node_key
                     "name" = node_details.name
                     "vm_ip" = node_details.vm_ip
                 }
       } 
      ])...) # do NOT remove the dots
    
    }
    

    then

    resource "vsphere_virtual_machine" "vm" {
      for_each              = local.nodes_flat
    ..
    ..
      name                  = format("${each.value.name}.${var.vm_domain}")
      #num_cpus              = var.vm_vcpu == false ? var.vm_vcpu : each.value.vm_vcpu # # your `nodes` does not contain vm_vcpu, so don't know what you want to do here.
      #memory                = var.vm_memory == false ? var.vm_memory : each.value.vm_memory # your `nodes` does not contain vm_memory, so don't know what you want to do here.
      firmware              = var.vm_firmware
      folder                = var.vm_folder
      cpu_hot_add_enabled      = "true"
      memory_hot_add_enabled   = "true"
      disk {
        label               = var.vm_disk_label
        #size                = each.value.vm_disk_size # your `nodes` does not contain vm_disk_size, so don't know what you want to do here.
        thin_provisioned    = var.vm_disk_thin
      }
    }
    

    That's should give you idea. You are not defining vm_disk_size in nodes nor even use vm_ip that you actually have (same for vm_vcpu and vm_memory). But regardless, flattening is the way to solve your problem.