Range number to concat with template string

Can u guys help with this question ?

I have the following code:

locals {
  names_linux   = var.vm_os == "linux" ? toset(var.vms_name) : toset([])
  names_windows = var.vm_os == "windows" ? toset(var.vms_name) : toset([])
}

resource "azurerm_windows_virtual_machine" "vmwin" {
  for_each                   = local.names_windows
  name                       = "vmazu${var.tags.env}wn${each.value}"
   .....
  ......
--

This code create vm linux/windows conditionally.

now, i need to concat a number (range number) sequence with each vm (var.vms_name) created. Like: “vmazu${var.tags.env}wn${each.value}001”,“vmazu${var.tags.env}wn${each.value}002” .
Using the range function inside locals, I was not successful.

tks!

Hi @heltonengc,

I’m not sure I understand your goal correctly.

Do you want to declare more than one virtual machine for each element of var.vms_name? If so, what is the rule for deciding how many instances of each name should exist?

Yes.

i will be insert into vms_name on tf vars, how many vm i need.

Ex:
----
variable "vms_name" {
  type        = list(string)
  description = "Name of vms will you want"
}

variable "vm_os" {
  type        = string
 description = " os than you want"
}
-----
tfvars:

vms_name = ["vmlx", "vmly"] 
vm_os = "windows"
----

locals {
  names_linux   = var.vm_os == "linux" ? toset(var.vms_name) : toset([])
  names_windows = var.vm_os == "windows" ? toset(var.vms_name) : toset([])

}

resource "azurerm_windows_virtual_machine" "vmwin" {

  for_each                   = local.names_windows
  name                       = "vmazu${var.tags.env}wn${each.value}" <<< Will be create two vms. 

I need a numeric sequence to be added along with the “name” parameter for each vm added. Ex: "vmazu${var.tags.env}wnvmlx>001< …002… It’s possible?

I try to use range function, but this error returns : Cannot include the given value in a string template: string required.

Sorry if I’m still not clear.

Hi @heltonengc,

In the code you’ve shared I don’t understand what the rule is for deciding the number of each VM. How do you decide whether vmlx is 001 or 002? Are you intending to use the order of the original list to decide that?

Yes. Use the order of the original list to decide.

Okay! Then the main thing to consider here is that converting the list to a set discards the original order – since a set is an unordered collection of values – so if you want to make use of the order you’ll need to retain the element index, or something derived from it, in the data structure passed to for_each.

Here’s one way to do that:

locals {
  vms = tomap({
    for i, name in var.vms_name : name => {
      index = i
    }
  })
  vms_linux = var.vm_os == "linux" ? local.vms : tomap({})
  vms_windows = var.vm_os == "windows" ? local.vms : tomap({})
}

This uses a map instead of a set because we need to retain some additional information about each element – the index – and so we need somewhere to save that.

for_each will also accept a map, so you don’t need to change the for_each expression itself but you will need to change the expression used to construct the name:

resource "azurerm_windows_virtual_machine" "vmwin" {
  for_each = local.names_windows

  name = "vmazu${var.tags.env}wn${each.key}${format("%03d", each.value.index + 1)}"
  # ...
}

Notice:

  • The name is now only in each.key, because each.value is now the value of each map element, which is the object containing the index attribute.
  • The index therefore comes from each.value.index.
  • I used the format function to format the number to be at least three digits long with zero padding, since that’s what you showed in your examples.

The above should achieve what you asked for, but it has a significant downside: if you add or remove items from anywhere other than the end of var.vms_name in future you will effectively renumber all of the subsequent items in the list, causing them to have their names changed. If Azure does not allow changing the name of a virtual machine after it’s running then that would mean replacing all of those VMs to renumber them.

The typical reason to use for_each is to avoid being order-specific, and introducing numbering derived from the original order defeats that goal.

1 Like