Terraform for_each The given key does not identify an element in this collection value

14.5k views Asked by At

I am working on a project where I am building a "stateful" qa environment that incorporates different Win versions that correspond to different sql versions. For example, for Server 2016 I will have 3 servers with each having a different version of sql. Same thing for 2019, and 2022. I have reached the point where it will read the values correctly but then it is giving me this error:

│ Error: Invalid index
│
│   on main.tf line 60, in resource "vsphere_virtual_machine" "vm":
│   60:   guest_id         = data.vsphere_virtual_machine.template[each.value.template].guest_id
│     ├────────────────
│     │ data.vsphere_virtual_machine.template is object with 2 attributes
│     │ each.value.template is "Templates/QA_2016"
│
│ The given key does not identify an element in this collection value.

Here is the code:

`

provider "vsphere" {
  vim_keep_alive = 30
  user           = var.vsphere_user
  password       = var.vsphere_password
  vsphere_server = var.vsphere_server

  # If you have a self-signed cert
  allow_unverified_ssl = true
}


#### data block see local vars

data "vsphere_datacenter" "dc" {
  name = local.dc
}

data "vsphere_compute_cluster" "compute_cluster" {
  name          = local.cluster
  datacenter_id = data.vsphere_datacenter.dc.id
}


data "vsphere_datastore" "datastore" {
  name          = local.datastore
  datacenter_id = data.vsphere_datacenter.dc.id
}


data "vsphere_network" "network" {
  for_each = var.vms
  name          = each.value.network
  datacenter_id = data.vsphere_datacenter.dc.id
 }

data "vsphere_virtual_machine" "template" {
  for_each = var.vms
  name          = each.value.template
  datacenter_id = data.vsphere_datacenter.dc.id
}

##### Resource Block


resource "vsphere_virtual_machine" "vm" {

  for_each = var.vms

  datastore_id     = data.vsphere_datastore.datastore.id
  guest_id         = data.vsphere_virtual_machine.template[each.value.template].guest_id
  resource_pool_id = data.vsphere_compute_cluster.compute_cluster.id
  
  
  
  # host_system_id = "${data.vsphere_datacenter.dc.id}"
  firmware                   = data.vsphere_virtual_machine.template[each.value.template].firmware
  num_cpus                   = local.cpu_count
  memory                     = local.memory
  scsi_type                  = data.vsphere_virtual_machine.template[each.value.template].scsi_type
  wait_for_guest_net_timeout = -1
  name                       = each.value.name

`

Here is the vars file:

`

locals {
    dc                  = "DC"
    cluster             = "The Cluster"
    datastore           = "Storage_thing"
    cpu_count           = "4"
    memory              = "16384"
    disk_label          = "disk0"
    disk_size           = "250"
    disk_thin           = "true"
    domain              = "my.domain"
    dns                 = ["xx.xx.xx.xx", "xx.xx.xx.xx"]
    password            = "NotMyPass"
    auto_logon          = true
    auto_logon_count    = 1
    firmware            = "efi"

  }


#### Name your vm's here - Terraform will provision what is provided here - Add or comment out VM's as needed
variable "vms" {
    type = map(any)
    default = {
      wqawin16sql14 = {
        name         = "wqawin16sql14"
        network      = "vm_network"
        template     = "Templates/QA_2016"
      },
      wqawin16sql17 = {
        name         = "wqawin16sql17"
        network      = "vm_network"
        template     = "Templates/QA_2016"
      },
    }
  }

`

1

There are 1 answers

0
Martin Atkins On BEST ANSWER

Terraform is reporting this error because your data "vsphere_virtual_machine" "template" block has for_each = var.vms and so the instance keys of that resource are the keys from your map value: "wqawin16sql14" and "wqawin16sql17".

That fails because you're trying to look up an instance using the value of the template attribute, which is "Templates/QA_2016" and therefore doesn't match any of the instance keys.

It seems like your goal here is to find one virtual machine for each distinct value of the template attributes in your input variable, and then use the guest_id of each of those VMs to populate the guest_id of the corresponding instance of resource "vsphere_virtual_machine" "vm".

If so, you'll need to make the for_each for your data resource be a collection where each element represents a template, rather than having each element represent a virtual machine to manage. One way to achieve that would be to calculate the set of all template values across all of your elements of var.vm, like this:

data "vsphere_virtual_machine" "template" {
  for_each = toset(var.vms[*].template)

  name          = each.value
  datacenter_id = data.vsphere_datacenter.dc.id
}

Notice that name is now set to just each.value because for_each is now just a set of template names, like toset(["Templates/QA_2016"]), so the values of this collection are just strings rather than objects with attributes.

With this change you should then have only one instance of this data resource whose address will be data.vmware_virtual_machine.template["Templates/QA_2016"]. This instance key now does match the template attribute in both of your VM objects, and so the dynamic lookup of the guest ID based on the template attribute of each VM object should succeed.