Terraform for loop question - match two loops based on a value in the loop

Hi

I am using the bpg/proxmox provider to provision VM-s. I have create a list of VM-s that I wish to be present.

These VM-s need to be deployed from a image and the images are local to only to a specific proxmox node. I have three nodes, so I chose to loop over them and download the image in each node.

Example:

variable "proxmox_nodes" {
  description = "List of proxmox nodes in a cluster. Required for operations where 'stuff' needs to be present on all nodes, but they are all local storage."
  type = list
}
#############
# https://registry.terraform.io/providers/bpg/proxmox/latest/docs/resources/virtual_environment_download_file
# Download the latest Debian12 qcow2 image. NB! For proxmox the endfix needs to be .img
#############
resource "proxmox_virtual_environment_download_file" "latest_debian_12_bookworm_qcow2_img" {
  # Loop over list of proxmox_nodes and download file to each one.
  for_each = toset(var.proxmox_nodes)
  content_type = "iso"
  datastore_id = "images"
  file_name    = "debian-12-generic-amd64.qcow2.img"
  node_name = each.value
  url       = "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2"
}

This will download the IMG on all 3 nodes.

Now in my VM creation I have specified the IMG location.

      file_id      = disk.value.init_os == "Debian12" ? proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img[each.key].id : null

But this will give me an error:

│ Error: Invalid index
│ 
│   on modules/proxmox-vm/main.tf line 135, in resource "proxmox_virtual_environment_vm" "instances":
│  135:       file_id      = disk.value.init_os == "Debian12" ? proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img[each.key].id : null
│     ├────────────────
│     │ each.key is "test-vm-02"
│     │ proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img is object with 3 attributes
│ 
│ The given key does not identify an element in this collection value.

How can I match that it will choose the variable where the VM will be created will match the correct node?

For example the VM list datatype also has a variable node, which will tell which proxmox node to deploy the VM to.

variable "proxmox_instances" {
  # https://spacelift.io/blog/terraform-map-variable
  description = "Instances to be deployed in Proxmox."
  type = map(object({
    name             = string
    description      = optional(string, "Managed by Terraform.")
    node_name        = string
    os               = string
    machine          = optional(string, "q35")
    bios             = optional(string, "ovmf")
    agent_enabled    = optional(bool, true)
    cpu_cores        = number
    cpu_type         = optional(string, "x86-64-v2-AES")
    memory_dedicated = number
    memory_floating  = number
    disks = optional(list(object({
      datastore_id = optional(string, "volumes")
      interface    = optional(string, "virtio0")
      size         = number
      init_os      = string
    })),[])
    efi_disk_datastore_id   = optional(string, "volumes")
    network_bridge          = optional(string, "ovsbr0")
    vlan_id                 = number
    cloud_init_datastore_id = optional(string, "volumes")
    ipv4_address            = string
    ipv4_gateway            = string
  }))
  default = {}
}

EDIT: Could something like this work? If the node_name and the proxmox_nodes values are identicial then I can reference them like so

file_id = disk.value.init_os == "Debian12" ? proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img[each.value.node_name].id : null

Based on the error, unless I’m missing something obvious, it seems like proxmox_virtual_environment_download_file.latest_debian_12_bookworm_qcow2_img is still not indexed on the list? Maybe try making an output of it and / or testing accessing the items independently to double-check.

It might be also worth considering making a module that packages the various resources, and doing for_each on that (either with a set / list of strings for the nodename, or with a list of objects / maps that have some additional parameters).