Create resources iterating through the values of a map rather key

Hi All,

I couldn’t figure out how I can loop through the sum of values of the below map.

variable "images" {
  default = {
    "rhel-8-factory-os-ready" = {
       "availability_zone" = "eu-fra-1ah"
       "flavor" = 4
       "instance_count" = 2
       "image_name" = "rhel-8-factory-os-ready"
    },
    "rhel-7-factory-os-ready" = {
       "availability_zone" = "eu-fra-1ai"
       "instance_count" = 3
       "flavor" = 3
       "image_name" = "rhel-7-factory-os-ready"
    },
    "rhel-6-factory-os-ready" = {
       "availability_zone" = "eu-fra-1ah"
       "instance_count" = 3
       "flavor" = 3
       "image_name" = "rhel-6-factory-os-ready"
    }
  }
}

Here, I’ve to iterate through the sum of instance_count attribute of the all the keys & create instances based on the instance_count.

I could calculate the sum of instance_count, with below inbuilt functions.

locals {
  list_sum = length(flatten([for i in var.images: range(i["instance_count"])]))
}

How can I iterate through the list_sum variable & create the resources based on instance_count?

I created below lists to create resources ::

locals {
  list_images = tolist(keys(var.images))
  list_instance_count = [for i in var.images: i["instance_count"]]
  list_flavors = [for i in var.images: i["flavor"]]
  list_image_names = [for i in var.images: i["image_name"]]
  list_availability_zones = [for i in var.images: i["availability_zone"]]
}

My resource ::

resource "openstack_compute_instance_v2" "instance" {
  count = local.list_sum
  image_name = element(local.list_image_names, count.index +1 )
  flavor_id = element(local.list_flavors, (count.index + 1) )
  name = element(local.list_image_names, (count.index + 1) )
  security_groups = var.security_group
  availability_zone = element(local.list_availability_zones, (count.index + 1) )
  key_pair = "foptst"
  network {
    name = var.network_name
  }
}

By now, you may be knowing that my iteration is incorrect. My resource block has to create the number of resources based on instance_count var ie., 2 instances of rhel-8-factory-os-ready, 3 instances of rhel-7-factory-os-ready and 3 instances of rhel-6-factory-os-ready.

Because of incorrect looping, I couldn’t get it. It would be great if someone could help me how to iterate properly to create resources as expected.

Many Thanks in advance,
Harsha

1 Like

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

Awesome. This is what I was expecting. I’m no more using count. for_each is great.
Many Thanks :slight_smile:

Hi @apparentlymart
I would like to ask you if I can use the same logic for this:
sa_env_permissions = {
“1” : {
“environment” : [“dev”, “qaa”]
“permissions” : [“roles/resourcemanager.folderAdmin”, “roles/owner”]
},

thanks in advanced

Hi, This is a great example as I have used to deploy various instances types and flavors. However how with this example in mind could I overwrite the “instance_count” from 3 to 6 for example in .tfvars file ? Lets say I wanted to do this without having to provide non null values to . I tried with to map function , but could not make it work.

tfvars file
images = {
“vm1” = {
flavor = “10”
“instance_count” = 1
},
“vm2” = {
flavor = “20”
“instance_count” = 1
},
“vm3” = {
flavor = “30”
“instance_count” = 1
}

}

Hi @dominic.viau,

Welcome!

This is a very old topic. Please start a new topic to ask your new question, so that it will be more visible to those answering questions in this forum.

(You can link back to this topic in your new one if you think that would be useful context, but please still share all of the information you’d share if this topic didn’t already exist, since it’s often a mistake to assume that what you have is exactly the same as what someone else had in an earlier question.)