Search code examples
azureterraformforeach-loop-container

Referencing value for each.key for each loop from another terraform blocks


I was trying to deploy 2 VMs in Azure using for each loop with each.key and each value config. However, I am getting an error message below in network_interface_ids argument where the value of my each.key definition for the nics are pointing to the name of VMs instead of the nic name ids. Is there anything I missed here from the config? Appreciate your kind inputs.

my sample code:

locals {
  virtual_machines = {
    (var.vm_win_name) = (var.location)
    (var.vm_win_name-test) = (var.location) 
  }
}

locals {
  vm_nic_interfaces = {
    (var.vm_win_nic_name) = (var.location)
    (var.vm_win_nic_name-test) = (var.location) 
  }
}

variable "vm_win_name" {
  description = "The name of VM"
  type = string
  default = "Testvm01"
}

variable "vm_win_name-test" {      
  description = "The name of VM"
  type = string
  default = "Testvm02"
}

variable "vm_win_nic_name" {
  type = string
  default = "Testvm01-nic"
}

variable "vm_win_nic_name-test" { 
  type = string
  default = "Testvm02-nic"
}

resource "azurerm_network_interface" "nic_win_jumpbox" {
  for_each = local.vm_nic_interfaces

  name = each.key
  location = each.value
  resource_group_name = azurerm_resource_group.resource_groups[var.mgmt_resource_group_name].name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = data.azurerm_subnet.subnet_ids[2].id
    private_ip_address_allocation = "Dynamic"
  }
}

resource "azurerm_windows_virtual_machine" "win_jumpbox" {
  for_each = local.virtual_machines

  name = each.key
  location = each.value
  resource_group_name = azurerm_resource_group.resource_groups[var.mgmt_resource_group_name].name
  network_interface_ids = [azurerm_network_interface.nic_win_jumpbox[each.key].id]

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2022-datacenter-azure-edition-hotpatch"
    version = "latest"
  }

  size = var.vm_size
  admin_username = "azureadmin"
  admin_password = "Testvm1234&
  enable_automatic_updates = false
  patch_mode = "AutomaticByPlatform"

  tags = local.resource_groups_tags


  os_disk {
    name = each.key
    caching = "ReadWrite"
    storage_account_type = "StandardSSD_LRS"

}

Error Message:

│     │ azurerm_network_interface.nic_win_jumpbox is object with 2 attributes
│     │ each.key is "Testvm01"
│ 
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│ 
│   on winvm.tf line 44, in resource "azurerm_windows_virtual_machine" "win_jumpbox":
│   44:   network_interface_ids = [azurerm_network_interface.nic_win_jumpbox[each.key].id]
│     ├────────────────
│     │ azurerm_network_interface.nic_win_jumpbox is object with 2 attributes
│     │ each.key is "Testvm02"
│ 
│ The given key does not identify an element in this collection value.

I have tried to modify the possible value in the network_interface_ids arguments but error still persist.


Solution

  • The design of your code can be improved, I hope you don't mind some suggestions:

    1. Use maps instead of declaring multiple variables for each VM or NIC.
    2. There is a 1:1 relationship between VMs and NICs, so these can be grouped together

    Example usage:

    providers.tf
    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~>3.101.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
    variables.tf

    var.virtual_machines is set as a map of objects - this allow us to add, change or remove VMs/NICs without touching the remaining code.

    variable "mgmt_resource_group_name" {
      description = "The name of the resource group"
      type        = string
      default     = "myResourceGroup"
    }
    
    variable "mgmt_resource_group_location" {
      description = "The location of the resource group"
      type        = string
      default     = "East US"
    }
    
    variable "virtual_machines" {
      description = "Virtual machines configuration."
      type = map(object({
        name     = string,
        vm_size  = optional(string, "Standard_DS1_v2"),
        nic_name = string
      }))
      default = {
        "01" = {
          name     = "Testvm01",
          nic_name = "Testvm01-nic"
        },
        "02" = {
          name     = "Testvm02",
          nic_name = "Testvm02-nic"
        }
      }
    }
    
    variable "resource_groups_tags" {
      description = "Tags"
      type        = map(string)
      default = {
        foo = "bar"
      }
    }
    
    main.tf
    resource "azurerm_resource_group" "resource_group" {
      name     = var.mgmt_resource_group_name
      location = var.mgmt_resource_group_location
    
      tags = var.resource_groups_tags
    }
    
    resource "azurerm_network_interface" "nic_win_jumpbox" {
      for_each = var.virtual_machines
    
      name                = each.value.nic_name
      location            = azurerm_resource_group.resource_group.location
      resource_group_name = azurerm_resource_group.resource_group.name
    
      ip_configuration {
        name = "internal"
      # subnet_id                     = data.azurerm_subnet.subnet_ids[2].id
        private_ip_address_allocation = "Dynamic"
      }
    }
    
    resource "azurerm_windows_virtual_machine" "win_jumpbox" {
      for_each = var.virtual_machines
    
      name                = each.value.name
      location            = azurerm_resource_group.resource_group.location
      resource_group_name = azurerm_resource_group.resource_group.name
    
      network_interface_ids = [
        azurerm_network_interface.nic_win_jumpbox[each.key].id
      ]
    
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2022-datacenter-azure-edition-hotpatch"
        version   = "latest"
      }
    
      size                     = each.value.vm_size
      admin_username           = "azureadmin"
      admin_password           = "Testvm1234&"
      enable_automatic_updates = false
      patch_mode               = "AutomaticByPlatform"
    
      tags = var.resource_groups_tags
    
      os_disk {
        name                 = each.value.name
        caching              = "ReadWrite"
        storage_account_type = "StandardSSD_LRS"
      }
    }
    

    Running terraform plan:

    Terraform used the selected providers to generate the following execution
    plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # azurerm_network_interface.nic_win_jumpbox["01"] will be created
      + resource "azurerm_network_interface" "nic_win_jumpbox" {
          + applied_dns_servers           = (known after apply)
          + dns_servers                   = (known after apply)
          + enable_accelerated_networking = false
          + enable_ip_forwarding          = false
          + id                            = (known after apply)
          + internal_dns_name_label       = (known after apply)
          + internal_domain_name_suffix   = (known after apply)
          + location                      = "eastus"
          + mac_address                   = (known after apply)
          + name                          = "Testvm01-nic"
          + private_ip_address            = (known after apply)
          + private_ip_addresses          = (known after apply)
          + resource_group_name           = "myResourceGroup"
          + virtual_machine_id            = (known after apply)
    
          + ip_configuration {
              + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
              + name                                               = "internal"
              + primary                                            = (known after apply)
              + private_ip_address                                 = (known after apply)
              + private_ip_address_allocation                      = "Dynamic"
              + private_ip_address_version                         = "IPv4"
            }
        }
    
      # azurerm_network_interface.nic_win_jumpbox["02"] will be created
      + resource "azurerm_network_interface" "nic_win_jumpbox" {
          + applied_dns_servers           = (known after apply)
          + dns_servers                   = (known after apply)
          + enable_accelerated_networking = false
          + enable_ip_forwarding          = false
          + id                            = (known after apply)
          + internal_dns_name_label       = (known after apply)
          + internal_domain_name_suffix   = (known after apply)
          + location                      = "eastus"
          + mac_address                   = (known after apply)
          + name                          = "Testvm02-nic"
          + private_ip_address            = (known after apply)
          + private_ip_addresses          = (known after apply)
          + resource_group_name           = "myResourceGroup"
          + virtual_machine_id            = (known after apply)
    
          + ip_configuration {
              + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
              + name                                               = "internal"
              + primary                                            = (known after apply)
              + private_ip_address                                 = (known after apply)
              + private_ip_address_allocation                      = "Dynamic"
              + private_ip_address_version                         = "IPv4"
            }
        }
    
      # azurerm_resource_group.resource_group will be created
      + resource "azurerm_resource_group" "resource_group" {
          + id       = (known after apply)
          + location = "eastus"
          + name     = "myResourceGroup"
          + tags     = {
              + "foo" = "bar"
            }
        }
    
      # azurerm_windows_virtual_machine.win_jumpbox["01"] will be created
      + resource "azurerm_windows_virtual_machine" "win_jumpbox" {
          + admin_password                                         = (sensitive value)
          + admin_username                                         = "azureadmin"
          + allow_extension_operations                             = true
          + bypass_platform_safety_checks_on_user_schedule_enabled = false
          + computer_name                                          = (known after apply)
          + disk_controller_type                                   = (known after apply)
          + enable_automatic_updates                               = false
          + extensions_time_budget                                 = "PT1H30M"
          + hotpatching_enabled                                    = false
          + id                                                     = (known after apply)
          + location                                               = "eastus"
          + max_bid_price                                          = -1
          + name                                                   = "Testvm01"
          + network_interface_ids                                  = (known after apply)
          + patch_assessment_mode                                  = "ImageDefault"
          + patch_mode                                             = "AutomaticByPlatform"
          + platform_fault_domain                                  = -1
          + priority                                               = "Regular"
          + private_ip_address                                     = (known after apply)
          + private_ip_addresses                                   = (known after apply)
          + provision_vm_agent                                     = true
          + public_ip_address                                      = (known after apply)
          + public_ip_addresses                                    = (known after apply)
          + resource_group_name                                    = "myResourceGroup"
          + size                                                   = "Standard_DS1_v2"
          + tags                                                   = {
              + "foo" = "bar"
            }
          + virtual_machine_id                                     = (known after apply)
          + vm_agent_platform_updates_enabled                      = false
    
          + os_disk {
              + caching                   = "ReadWrite"
              + disk_size_gb              = (known after apply)
              + name                      = "Testvm01"
              + storage_account_type      = "StandardSSD_LRS"
              + write_accelerator_enabled = false
            }
    
          + source_image_reference {
              + offer     = "WindowsServer"
              + publisher = "MicrosoftWindowsServer"
              + sku       = "2022-datacenter-azure-edition-hotpatch"
              + version   = "latest"
            }
        }
    
      # azurerm_windows_virtual_machine.win_jumpbox["02"] will be created
      + resource "azurerm_windows_virtual_machine" "win_jumpbox" {
          + admin_password                                         = (sensitive value)
          + admin_username                                         = "azureadmin"
          + allow_extension_operations                             = true
          + bypass_platform_safety_checks_on_user_schedule_enabled = false
          + computer_name                                          = (known after apply)
          + disk_controller_type                                   = (known after apply)
          + enable_automatic_updates                               = false
          + extensions_time_budget                                 = "PT1H30M"
          + hotpatching_enabled                                    = false
          + id                                                     = (known after apply)
          + location                                               = "eastus"
          + max_bid_price                                          = -1
          + name                                                   = "Testvm02"
          + network_interface_ids                                  = (known after apply)
          + patch_assessment_mode                                  = "ImageDefault"
          + patch_mode                                             = "AutomaticByPlatform"
          + platform_fault_domain                                  = -1
          + priority                                               = "Regular"
          + private_ip_address                                     = (known after apply)
          + private_ip_addresses                                   = (known after apply)
          + provision_vm_agent                                     = true
          + public_ip_address                                      = (known after apply)
          + public_ip_addresses                                    = (known after apply)
          + resource_group_name                                    = "myResourceGroup"
          + size                                                   = "Standard_DS1_v2"
          + tags                                                   = {
              + "foo" = "bar"
            }
          + virtual_machine_id                                     = (known after apply)
          + vm_agent_platform_updates_enabled                      = false
    
          + os_disk {
              + caching                   = "ReadWrite"
              + disk_size_gb              = (known after apply)
              + name                      = "Testvm02"
              + storage_account_type      = "StandardSSD_LRS"
              + write_accelerator_enabled = false
            }
    
          + source_image_reference {
              + offer     = "WindowsServer"
              + publisher = "MicrosoftWindowsServer"
              + sku       = "2022-datacenter-azure-edition-hotpatch"
              + version   = "latest"
            }
        }
    
    Plan: 5 to add, 0 to change, 0 to destroy.