"Invalid for_each argument" with resource attributes

│ Error: Invalid for_each argument
│ 
│   on .terraform/modules/linux_virtual_machine/vm.tf line 157, in resource "azurerm_virtual_machine_extension" "linux_wa_agent":
│  157:   for_each = (
│  158:     local.linux_custom_data.enabled && local.linux_custom_data.provider == "waagent" && local.linux_custom_data != "" && lower(var.vm_type) == "linux" ?
│  159:     try(nonsensitive(azurerm_linux_virtual_machine.this), azurerm_linux_virtual_machine.this) : {}
│  160:   )
│     ├────────────────
│     │ azurerm_linux_virtual_machine.this is object with 3 attributes
│     │ local.linux_custom_data is object with 3 attributes
│     │ local.linux_custom_data.enabled is "true"
│     │ local.linux_custom_data.provider is "waagent"
│     │ var.vm_type is "linux"
│ 
│ The "for_each" value depends on resource attributes that cannot be
│ determined until apply, so Terraform cannot predict how many instances will
│ be created. To work around this, use the -target argument to first apply
│ only the resources that the for_each depends on

Hi @apparentlymart, I’m also facing similar kind of issue to run custom script for multiple linux VM via extension. Can you able to find something to advice in this case.

i have passed below input from my resource creation module

=======================================================

linux_custom_data = {
  enabled     = true,
  provider    = "waagent",
  script_path = "file path",
}

to parent module variable available like, var.linux_custom_data

Hi @SaravananGuru,

I’m not sure exactly what’s going on here but there are a few odd things about the configuration shown in the error message. Fixing these probably won’t make this error go away, because they seem unrelated to it, but it should at least give a simpler expression to debug:

  • local.linux_custom_data != "" will always return true here, because the linux_custom_data value you showed has an object type and thus it can never be equal to a string.
  • A resource can never be sensitive as a whole, so nonsensitive(azurerm_linux_virtual_machine.this) can never succeed. You should be able to just write azurerm_linux_virtual_machine.this alone, because the set of instance keys for that resource is never sensitive.

Stylistically, I’d also prefer to write your conditional expression here as a for expression with an if clause, but this should not change the behavior in any significant way:

for_each = {
  for k, v in azurerm_linux_virtual_machine.this : k => v
  if local.linux_custom_data.enabled
    && local.linux_custom_data.provider == "waagent"
    && lower(var.vm_type) == "linux"
}

I don’t yet see what part of your expression would be introducing an unknown value, but simplifying it in these ways will hopefully make it easier to debug what’s left. If you try these simplifications and see a different result (either a different error or a success) then please let me know what that new result is.

1 Like

Hi @apparentlymart ,

thank you for the update. Now, the terraform plan executed successfully. however, the plan I could see it going to replace the existing VM resources also rather than adding vm extension resource alone.

module.linux_virtual_machine.azurerm_linux_virtual_machine.this[“vm-1”] must be replaced

-/+ resource “azurerm_linux_virtual_machine” “this” {
- availability_set_id = “” → null
~ computer_name = “xxx-xxx-prod-002” → (known after apply)
+ custom_data = (sensitive value) # forces replacement
- dedicated_host_id = “” → null
- encryption_at_host_enabled = false → null
- eviction_policy = “” → null
~ id = “/subscriptions/xxxxxx(subscription-id)/resourceGroups/xxx-xx-data-xxx-prd-xxx-rg/providers/Microsoft.Compute/virtualMachines/xxx-xxx-prod-002” → (known after apply)
- license_type = “” → null
name = “xxx-xxx-prod-002”
~ private_ip_address = “xxxxxx” → (known after apply)
~ private_ip_addresses = [
- “XXXXXX”,
] → (known after apply)
- proximity_placement_group_id = “” → null
~ public_ip_address = “” → (known after apply)
~ public_ip_addresses = → (known after apply)

please find the below changes done on root module vm extension block.

resource “azurerm_virtual_machine_extension” “linux_wa_agent” {

for_each = {

for k, v in azurerm_linux_virtual_machine.this : k => v

if local.linux_custom_data.enabled

&& local.linux_custom_data.provider == "waagent"

&& lower(var.vm_type) == "linux"

}

name = “LinuxCustomScriptExecution”

virtual_machine_id = each.value.id

publisher = “Microsoft.Azure.Extensions”

type = “CustomScript”

type_handler_version = “2.1”

protected_settings = jsonencode(

{

  script          = filebase64(local.linux_custom_data.script_path),

  managedIdentity = {}

}

)

If it’s a case can i do hard code for separate block to initiate vm extension provision or terraform will not create a machine if it’s already exist (means I no need to worry in that case Since, i’m not using any new labeling it will not destroy the current vm configuration?)