Output list of Azure vm Ids Fail

I’m trying to output Azure vm ids from a module, which I need to reference in my main.tf file, but when I try to reference the output in my main.tf file it errors out with

Requesting any help on the best way to output such

Error: Missing resource instance key
│
│   on modules\vmWindows\output_win.tf line 2, in output "virtualMachineId":
│    2:     value = [azurerm_windows_virtual_machine.WindowsVM.id]
│
│ Because azurerm_windows_virtual_machine.WindowsVM has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│     azurerm_windows_virtual_machine.WindowsVM[each.key]

here’s a snippet of files
windows.tf file

resource "azurerm_windows_virtual_machine" "WindowsVM" {
  for_each = toset(var.vmName )
  name                = each.value 
  resource_group_name = var.vmRgName 
  location            = var.vmLocation 
  size                = var.vmSize
  admin_username      = var.adminUserName
  admin_password      = var.adminPassword 
  network_interface_ids = [
    var.vm_nic_id # reference NIC module
  ]
  identity {
    type = "SystemAssigned"
  }
  hotpatching_enabled = false
  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
  source_image_reference {
    offer     = "WindowsServer"
    publisher = "MicrosoftWindowsServer"
    sku       = "2022-datacenter-azure-edition" 
    version   = "latest"
  }
  additional_capabilities {
    ultra_ssd_enabled = false
  }
  vtpm_enabled               = false
  patch_mode                 = "Manual" 
  enable_automatic_updates   = false
  encryption_at_host_enabled = false
  provision_vm_agent         = true
}

windowsOutput.tf

output "virtualMachineId" {
    value = [azurerm_windows_virtual_machine.WindowsVM.id]
}

variable "vm_names" {
  type        = set(string) 
  default     = ["usvm1", "usvm2", "usvm3"]
  description = "(optional) describe your variable"
}

module "vms4CoreInfra" {
  source = "./modules/vmWindows"

  vmName   = var.vm_names 
  for_each = module.nic.internal_nic 
  vm_nic_id = module.nic.internal_nic[each.value]

  vmRgName         = azurerm_resource_group.sample.name
  adminPassword    = "somePassword"
  adminUserName    = "randomUser"
  enableVMShutdown = true
  vmLocation       = azurerm_resource_group.sample.location
  vmSize           = "Standard_B1s"
}

output "name" {
  value = module.vms4CoreInfra.virtualMachineId
}

variable def:

variable "vmName" {
    type = list(string)
    description = "(optional) describe your variable"
}

Thanks in advance for your assistance.

do you mind trying:

output “virtualMachineId” {
value = azurerm_windows_virtual_machine.WindowsVM[*].id
}

Thanks for the quick response @ausmartway.

Running terraform plan with suggested fails with below err:
Error: Unsupported attribute

│ on modules\vmWindows\output_win.tf line 2, in output “virtualMachineId”:
│ 2: value = azurerm_windows_virtual_machine.WindowsVM[*].id

│ This object does not have an attribute named “id”.

thanks

sorry I don’t have an environment to test this. you can output the entire object I think, just value = azurerm_windows_virtual_machine.WindowsVM[*] and see what happends from there.

@ausmartway still fails, somehow the output ref shows that there are three instances, of the VM created and not understanding why it outputs
This object does not have an attribute named “virtualMachineId”

##################################################################
Error: Unsupported attribute

│ on modules\vmWindows\output_win.tf line 2, in output “virtualMachineId”:
│ 2: value = azurerm_windows_virtual_machine.WindowsVM[].id

│ This object does not have an attribute named “id”.
│ on modules\vmWindows\output_win.tf line 2, in output “virtualMachineId”:
│ 2: value = azurerm_windows_virtual_machine.WindowsVM[
].id

│ This object does not have an attribute named “id”.

│ on main.tf line 58, in output “name”:
│ 58: value = module.vms4CoreInfra.virtualMachineId
│ ├────────────────
│ │ module.vms4CoreInfra is object with 3 attributes

│ This object does not have an attribute named “virtualMachineId”.

Hi @ausmartway,

This suggestion was a good idea but [*] is only appropriate to expand lists, and so it works for resources that use count but not resources that use for_each. for_each resources appear as a map of objects when used elsewhere in the module.

There are two different ways to adapt this suggestion for a for_each resource, depending on what exactly you want to provide to the calling module.

If the user of this module will need to be able to determine which single ID belongs to each name given in var.vmName then it would be best to return a map from VM name to ID, like this:

output "virtual_machine_ids" {
  value = tomap({
    for k, vm in azurerm_windows_virtual_machine.WindowsVM : k => vm.id
  })
}

If the caller of the module should just treat the whole set of VMs as a single unit and should not be able to tell which VM ID belongs to which VM name then you can alternatively return the result as a set of IDs, discarding the keys entirely:

output "virtual_machine_ids" {
  value = toset([
    for vm in azurerm_windows_virtual_machine.WindowsVM : vm.id
  ])
}

Both of these are based on for expressions, which are a more general alternative to the splat operator [*] which allow for more flexibility in exactly how to construct the result.


Some other general notes about the original snippets, not directly related to the question:

  • It’s conventional to name objects in Terraform using all lowercase and underscores, such as var.vm_name instead of var.vmName.
  • Any input variable or output value which uses a collection type (a list, a set, or a map) should typically have a plural name, such as var.vm_names instead of var.vm_name.
  • If your module is not making any use of the ordering of the VM names in var.vm_names – which seems to be true based on what you’ve shared – then it would be clearer to the user of the module to declare it as type = set(string) instead of type = list(string), and then you won’t need to use toset in the for_each expression because the value will already be a set.
1 Like

@apparentlymart thanks for the advice on naming conventions and types, will be applying these going forward :wink: .

Did try your output suggestion but unfortunately either of them still fails with

Error: Unsupported attribute

│ on main.tf line 58, in output “name”:
│ 58: value = module.vms4CoreInfra.virtual_machine_ids
│ ├────────────────
│ │ module.vms4CoreInfra is object with 3 attributes

│ ***

This object does not have an attribute named “virtual_machine_ids”.


Do you know why its complaining of the atrribute name?

Hi @bantaba,

I recall from your earlier examples that one of your module blocks included the for_each argument.

If that is true for this module "vms4CoreInfra" then the first attribute after module.vms4CoreInfra must be the instance key of a particular instance of the module, to specify which instance you are referring to.

The problem and solution here are essentially the same as what I described earlier for your resource that had for_each set. You will need to decide how you want to present these multiple module instances to the caller of your module and write a suitable expression – probably another for expression like before – to create that data structure.

Hello @apparentlymart,

snippet of the module I have

resource "azurerm_windows_virtual_machine" "WindowsVM" {
  for_each = toset(var.vmName )
  name                = each.value 
  resource_group_name = var.vmRgName 
  location            = var.vmLocation 
  size                = var.vmSize
  admin_username      = var.adminUserName
  admin_password      = var.adminPassword 
  network_interface_ids = [
    var.vm_nic_id # reference NIC module
  ]
... removed for brevity...

output for the vm creation module

output "virtualMachineId" {
    value = [azurerm_windows_virtual_machine.WindowsVM.id]
}

I referenced the module in my main.tf, passing var.vm_names below…

variable "vm_names" {
  type        = set(string) 
  default     = ["usvm1", "usvm2", "usvm3"]
  description = "(optional) describe your variable"
}

module "vms4CoreInfra" {
  source = "./modules/vmWindows"

When I reference the output. The error clearly shows that the set var.vm_name has the three attributes.

output "name" {
  value = module.vms4CoreInfra.virtualMachineId
}

I’m under the impression that the output instance name is just a reference name to the module, and the error is complaining about the virtual_machine_ids instance name as not an attribute, which is throwing me off? Or is it just a way Terraform generalizes such error messages?

Are you saying that I need to reference the module as module.vms4CoreInfra.virtual_machine_ids ?