Create resources iterating through the values of a map rather key

Hi @harshavmb,

The goal when working with for_each or count is always to transform your input value into a collection that has one element per instance you want to create, which commonly involves the flatten function. So in this case, we need one element per instance_count per image, I think.

locals {
  # A list of objects with one object per instance.
  instances = flatten([
    for image_key, image in var.images : [
      for index in range(image.instance_count) : {
        availability_zone = image.availability_zone
        flavor            = image.flavor
        instance_index    = index
        image_key         = image_key
        image_name        = image.image_name
      }
    ]
  ])
}

The above works by initially constructing a list of lists. The first level of lists represents the images, and the second level represents the instances for each image, created using range. The above is a combination of the example in the flatten docs and the example in the range docs.

We can then use local.instances as part of the for_each argument in the resource block:

resource "openstack_compute_instance_v2" "instance" {
  for_each = {
    # Generate a unique string identifier for each instance
    for inst in local.instances : format("%s-%02d", inst.image_key, inst.instance_index + 1) => inst
  }

  image_name        = each.value.image_name
  flavor_id         = each.value.flavor
  name              = each.key
  security_groups   = var.security_groups
  availability_zone = each.value.availability_zones
  key_pair          = "foptst"
  network {
    name = var.network_name
  }
}

This uses for_each instead of count, because count is for creating a number of equivalent instances, where all are redundantly performing the same function. In this case, the instances are differentiated by the image they are created from and so using for_each allows us to customize the tracking keys to include the image keys, giving instance addresses like this:

  • openstack_compute_instance_v2.instance["rhel-8-factory-os-ready-01"]
  • openstack_compute_instance_v2.instance["rhel-8-factory-os-ready-02"]
  • openstack_compute_instance_v2.instance["rhel-7-factory-os-ready-01"]
  • openstack_compute_instance_v2.instance["rhel-7-factory-os-ready-02"]
  • openstack_compute_instance_v2.instance["rhel-7-factory-os-ready-03"]
  • openstack_compute_instance_v2.instance["rhel-6-factory-os-ready-01"]
  • openstack_compute_instance_v2.instance["rhel-6-factory-os-ready-02"]
  • openstack_compute_instance_v2.instance["rhel-6-factory-os-ready-03"]

If you were to decrease instance_count for rhel-7-factory-os-ready on a subsequent run, Terraform would plan to destroy openstack_compute_instance_v2.instance["rhel-7-factory-os-ready-03"] and leave all of the other instances untouched, because it can see that there is no longer an element with key "rhel-7-factory-os-ready-03" in the for_each map.

4 Likes