terraformterraform-provider-vsphere

Terraform: iterate through variables


I got something like this:

global_tags = {
    "Tag_Name1" = "Tag_Value1"
    "Tag_Name2" = "Tag_Value2"
}
 
virtual_machine = {
    "my-host-1.foo.bar" {
        cores = 3
        memory = 2048
        host_tags = {
            "Tag_Name3" = "Tag_Value3"
            "Tag_Name4" = "Tag_Value4"
        }
    }
    "my-host-2.foo.bar" {
        cores = 5
        memory = 4096
        host_tags = {
            "Tag_Name5" = "Tag_Value5"
            "Tag_Name6" = "Tag_Value6"
        }
    }
}
 
variable "global_tags" {
    type = map(any)
    default = null
}
 
variable "virtual_machine" {
    type = map(object({
        cores = number
        memory = bool
        host_tags = map(any)
    }))
}
 
data "vsphere_tag_category" "tag_cat" {
  for_each = var.global_tags
  name = each.key
}
 
data "vsphere_tag" "global_tags" {
  for_each = var.global_tags
  name = each.value
  category_id = data.vsphere_tag_category.tag_cat[each.key].id
}
 
locals {
    global_tag_ids = [
        for tags in data.vsphere_tag.global_tags :
        tags.id
    ]
}
 
locals {
    all_host_tags = flatten([
        for hostname, outer_value in var.virtual_machines : [
            for tag_key, tag_value in outer_value.custom_tags : {
                hostname = hostname
                tag_key = tag_key
                tag_value = tag_value
            }
        ]
    ])
}
 
data "vsphere_tag_category" "host_tag_cats" {
    for_each = { for v in local.all_host_tags : "${v.hostname}.${v.tag_key}" => v }
    name = each.value.tag_key
}
 
data "vsphere_tag" "host_tags" {
    for_each = { for v in local.all_host_tags : "${v.hostname}.${v.tag_key}" => v }
    name = each.value.tag_value
    category_id = data.vsphere_tag_category.host_tag_cats[each.key].id
}
 
resource "vsphere_virtual_machine" "test" {
    for_each = var.virtual_machine
 
    name = each.key
    num_cpus            = each.value.cores
    cpu_hot_add_enabled = true
 
    memory_hot_add_enabled = true
    memory                 = each.value.memory
    ...
    tags = local.global_tag_ids
 
    clone {
        template_uuid = data.vsphere_virtual_machine.test.id
    }
}

I want to assign the tags to the host. with the "global_tags" this is no problem. this works fine. but now i want to merge the global_tags_ids with the ids from the host_tags. my problem here is, that i only want to assign the global_tags with the appropriate host_tags. so nothing get mixed up. has someone an idea to achieve that?

I tried to access these keys via the mapping keys, but i only get it right, when i set an explicit key.

I want to pass a merged array to "tags" in this ressource, so host1 gets the global tag + his host_tags and so on.


Solution

  • Sorry for the slow reply (I'm the guy who tried to answer on IRC then went silent once I saw the complexity).

    Here's how I would approach this problem, it might be possible to clean it up even more or do some shortcuts in the processing.

    Instead of having multiple data sources one for the global tags and one for the host tags, I have attempted to combine all of the category+tag variations into a single list that I then use as the input for a single datasource referencing all tags using category name + tag name as the key.

    Then when iterating over the tags to be used there's only one source with a uniform format to the key, for readability I pulled the lookup into some extra locals but this could also have been done inline.

    I'm not able to run it using your resources but I ended up with the following code that I think does what you want.

    locals {
      global_tag_details = [for category_name, tag_name in var.global_tags : { category_name : category_name, tag_name : tag_name }]
      host_tag_details   = flatten([for hostname, host_details in var.virtual_machine : [for category_name, tag_name in host_details.host_tags : { category_name : category_name, tag_name : tag_name }]])
      all_tag_details    = distinct(concat(local.global_tag_details, local.host_tag_details))
      all_category_names = distinct([for tag_details in local.all_tag_details : tag_details.category_name])
      all_tags_map       = { for tag_details in local.all_tag_details : "${tag_details.category_name}.${tag_details.tag_name}" => tag_details }
    }
    
    data "vsphere_tag_category" "this" {
      for_each = toset(local.all_category_names)
      name     = each.key
    }
    
    data "vsphere_tag" "this" {
      for_each    = local.all_tags_map
      name        = each.value.tag_name
      category_id = data.vsphere_tag_category.this[each.value.category_name].id
    }
    
    locals {
      global_tag_ids   = [for category_name, tag_name in var.global_tags : data.vsphere_tag.this["${category_name}.${tag_name}"].id]
      hostname_tag_ids = { for hostname, host_details in var.virtual_machine : hostname => [for category_name, tag_name in host_details.host_tags : data.vsphere_tag.this["${category_name}.${tag_name}"].id] }
    }
    
    resource "vsphere_virtual_machine" "this" {
      for_each = var.virtual_machine
    
      name = each.key
    
      tags = concat(local.hostname_tag_ids[each.key], local.global_tag_ids)
    }