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
.
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 ?