The given key does not identify an element in this collection value for desired indexes

84 views Asked by At

I am trying to create ASG associations with the list of string Network interface card. Below is the code. The problem is I am not able to get the azurerm_network_interface index "${vm.vm_name}-${vm.nic_name}" to nic_asg_associations declaration

variable "vm_prefix" {
  type    = string
  default = "test"
}

variable "virtual_machines" {
  default = {
      app1_node1 = {
        "vm_name" = "app"
        "vm_num"  = "1"
        networks = {
          nic1 = {
            "nic_name" = "app-1"
            "subnet"   = "/subscriptions/**/resourceGroups/pluto/providers/Microsoft.Network/virtualNetworks/pluto-infra/subnets/app-subnet"
            "asg_ids"  = ["/subscriptions/**/resourceGroups/pluto/providers/Microsoft.Network/applicationSecurityGroups/app-asg", "/subscriptions/**/resourceGroups/pluto/providers/Microsoft.Network/applicationSecurityGroups/db-asg"]
          },
        }
      },
    }
  }


variable "resource_group" {
  type    = string
  default = "test"
}

data "azurerm_resource_group" "rg" {
  name = var.resource_group
}

locals {
  all_nics = {
    for vm in flatten([
      for vm_name, vm in var.virtual_machines: [
        for nic_name, nic in vm.networks : {
          vm_number      = vm.vm_num,
          vm_name        = vm_name,
          nic_name       = nic_name,
          subnet_value   = nic.subnet,
          nic_name_value = nic.nic_name
          asg_ids        = nic.asg_ids
        }
      ]
    ]) : "${vm.vm_name}-${vm.nic_name}" => vm
  }

  nic_asg_associations = flatten([
    for nic in local.all_nics : [
      for asg_id in nic.asg_ids : {
        nic_name = nic.nic_name,
        asg_id   = asg_id
      }
    ]
  ])
}

resource "azurerm_network_interface" "nic" {
  for_each            = local.all_nics
  name                = "${var.vm_prefix}-${each.value.nic_name}-nic"
  location            = "eastus"
  resource_group_name = var.resource_group

  ip_configuration {
    name                          = "${var.vm_prefix}-${each.value.nic_name}-ipconfig"
    subnet_id                     = each.value.subnet_value
    private_ip_address_allocation = "Dynamic"
  }
}


resource "azurerm_linux_virtual_machine" "vm" {

  depends_on                      = [azurerm_network_interface.nic]
  for_each                        = var.virtual_machines
  name                            = "${var.vm_prefix}-${each.value.vm_name}-${each.value.vm_num}"
  admin_username                  = "testadmin"
  admin_password                  = "test@1234522"
  disable_password_authentication = false
  location                        = "eastus"
  resource_group_name             = var.resource_group
  network_interface_ids           = [for nic_key, nic in azurerm_network_interface.nic : nic.id if startswith(nic_key, "${each.key}-")]
  size                            = "Standard_B2ms"

  os_disk {
    name                 = "${var.vm_prefix}-${each.value.vm_name}-${each.value.vm_num}-OSdisk"
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "RedHat"
    offer     = "RHEL"
    sku       = "82gen2"
    version   = "latest"
  }
}

resource "azurerm_network_interface_application_security_group_association" "nic_asg_association" {
  for_each = { for nic in local.all_nics : "${nic.vm_name}-${nic.nic_name}|${nic.asg_ids[0]}" => nic }

  network_interface_id          = azurerm_network_interface.nic[each.value.vm_name].id
  application_security_group_id = each.value.asg_ids[0]
}

Error :

│ Error: Invalid index
│
│   on main.tf line 118, in resource "azurerm_network_interface_application_security_group_association" "nic_asg_association":
│  118:   network_interface_id          = azurerm_network_interface.nic[each.key].id
│     ├────────────────
│     │ azurerm_network_interface.nic is object with 1 attribute "app1_node1-nic1"
│     │ each.key is "nic1|/subscriptions/a94f0758-94a8-426d-a3b6-aec6e83b51de/resourceGroups/pluto/providers/Microsoft.Network/applicationSecurityGroups/app-asg"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on main.tf line 118, in resource "azurerm_network_interface_application_security_group_association" "nic_asg_association":
│  118:   network_interface_id          = azurerm_network_interface.nic[each.key].id
│     ├────────────────
│     │ azurerm_network_interface.nic is object with 1 attribute "app1_node1-nic1"
│     │ each.key is "nic1|/subscriptions/a94f0758-94a8-426d-a3b6-aec6e83b51de/resourceGroups/pluto/providers/Microsoft.Network/applicationSecurityGroups/db-asg"
│
│ The given key does not identify an element in this collection value.
╵

could someone help me to write the correct nic_asg_associations and azurerm_network_interface_application_security_group_association ?

1

There are 1 answers

1
Vinay B On

I tried to create ASG associations with the list of string Network interfaces and I was able to achieve the requirement successfully.

You are trying to create virtual machines, network interfaces (NICs), and Application Security Groups (ASGs) in Azure using Terraform. The problem you are facing is caused by using the wrong index to refer to a network interface in the azurerm_network_interface_application_security_group_association resource.

You have an error because of the way you've set up your for_each for the azurerm_network_interface_application_security_group_association resource. The key for local.all_nics is different from the expected key format when you use azurerm_network_interface.nic inside the azurerm_network_interface_application_security_group_association. This happens because the for_each key in azurerm_network_interface_application_security_group_association is made from a combination of the VM name, NIC name, and the first ASG ID, which does not match any keys in azurerm_network_interface.nic.

The issue is about the node's key being used in your terraform.tfvars, which implies that Terraform is expecting a direct map of virtual machine configurations without this intermediary nodes layer. Terraform is expecting each virtual machine configuration directly under virtual_machines, but it found them nested under an additional nodes key, which doesn't match the expected structure defined in your variables.tf file

The tfvars format won't satisfy the requirement as Terraform does not recognize the structure as valid due to the specific way it's defined

My terraform configuration:

provider "azurerm" {
  features {}
}

variable "vm_prefix" {
  description = "Prefix for the virtual machine names"
  type        = string
  default     = "test"
}

variable "resource_group_name" {
  description = "The name of the resource group"
  type        = string
}

variable "virtual_machines" {
  description = "A map of virtual machines to create"
  type = map(object({
    vm_name   = string
    vm_num    = number
    networks = map(object({  // Note the change here from 'network' to 'networks'
      nic_name = string
      subnet   = string   // Note: Changed 'subnet_id' to 'subnet' to match module
      asg_ids  = list(string)
    }))
  }))
}


data "azurerm_resource_group" "example" {
  name = "testrg-vksb"
}

module "virtual_machine_setup" {
  source = "./modules/virtual_machine_setup"
  virtual_machines = var.virtual_machines
}

modules/virtual_machine_setup/main.tf

variable "virtual_machines" {
  description = "A map of virtual machines with their network interfaces and ASGs"
  type = map(object({
    vm_name  = string
    vm_num   = number
    networks = map(object({
      nic_name = string
      subnet   = string
      asg_ids  = list(string)
    }))
  }))
}




data "azurerm_resource_group" "example" {
  name = "testrg-vksb"
}

locals {
  // Flatten NICs for easy access
  all_nics = flatten([
    for vm_name, vm in var.virtual_machines : [
      for nic_key, nic in vm.networks : {
        vm_name  = vm_name,
        nic_name = nic.nic_name,
        subnet   = nic.subnet,
        asg_ids  = nic.asg_ids
      }
    ]
  ])

  // Prepare NIC-ASG associations
  nic_asg_associations = flatten([
    for nic in local.all_nics : [
      for asg_id in nic.asg_ids : {
        nic_name = nic.nic_name,
        asg_id   = asg_id
      }
    ]
  ])
}





resource "azurerm_network_interface" "nic" {
  for_each = { for nic in local.all_nics : nic.nic_name => nic }

  name                = each.key
  location            = "eastus2" # Ensure this matches your actual resource group location
  resource_group_name = data.azurerm_resource_group.example.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = each.value.subnet
    private_ip_address_allocation = "Dynamic"
  }
}


resource "azurerm_linux_virtual_machine" "vm" {
  for_each = var.virtual_machines

  name                = each.value.vm_name
  resource_group_name = data.azurerm_resource_group.example.name
  location            = "eastus2" # Adjust as necessary
  size                = "Standard_B2s" # Adjust as necessary

  network_interface_ids = [for nic in local.all_nics : azurerm_network_interface.nic[nic.nic_name].id if nic.vm_name == each.key]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-LTS"
    version   = "latest"
  }

  admin_username                  = "adminuser"
  admin_password                  = "Password1234!" # Ensure secure handling of credentials
  disable_password_authentication = false
}

resource "azurerm_network_interface_application_security_group_association" "nic_asg_association" {
  for_each = { for assoc in local.nic_asg_associations : "${assoc.nic_name}|${assoc.asg_id}" => assoc }

  network_interface_id          = azurerm_network_interface.nic[each.value.nic_name].id
  application_security_group_id = each.value.asg_id
}

tfvars:

vm_prefix = "test"

virtual_machines = {
  "app1_node1" = {
    vm_name = "app"
    vm_num  = 1
    networks = {  // Adjusted to be a map
      "nic1" = {  // Each NIC is now an entry in the map
        nic_name = "app-1"
        subnet = "/subscriptions/158b8345-c359-4d98-95c5-48f/resourceGroups/testrg-vksb/providers/Microsoft.Network/virtualNetworks/testvnetvk/subnets/testvksnet"
        asg_ids = [
          "/subscriptions/158b834-d048f/resourceGroups/testrg-vksb/providers/Microsoft.Network/applicationSecurityGroups/testsbasg1",
          "/subscriptions/158b8345-c35948f/resourceGroups/testrg-vksb/providers/Microsoft.Network/applicationSecurityGroups/testsbasg2"
        ]
      }
    }
  }
}

Deployment succeded:

enter image description here

enter image description here