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