Home > Software engineering >  Terraform Azure for each VM / NIC
Terraform Azure for each VM / NIC

Time:11-24

I'm trying to create multiplane vms using for each function in terraform.

Resource Group

resource "azurerm_resource_group" "rg" {
  name     = "${var.prefix}-rg"
  location = "east us 2"
  tags = var.tags
}

VNET

resource "azurerm_virtual_network" "vnet" {
  name                = "${var.prefix}-network-1"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  tags = var.tags
}

Subnet

resource "azurerm_subnet" "subnet" {
  name                 = "${var.prefix}-network-subnet-1"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.2.0/24"]
}

Variables for NICS

variable "nics" {
  type = map
  default = {
    
  nic3 = {
        name = "ubuntu-test-3"
  }

  nic4 = {
        name = "ubuntu-test-4"
  }
 }
}

NICS

resource "azurerm_network_interface" "nics" {
  for_each            = var.nics
  name                = each.value.name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "${each.value.name}-conf-1"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
  }

  tags = var.tags
}

Variables for VMS

variable "vms" {
  description = "Virtual Machines"
  type = map
  default = {
  vm3 = {
        name            = "ubuntu-test-3"
        size            = "Standard_DS1_v2"
    }
  vm4 = {
        name            = "ubuntu-test-4"
        size            = "Standard_DS1_v2"
  }
 }

}

and the block for the VM ( not completed - i wrote only the section that i have issue with )

resource "azurerm_virtual_machine" "vms" {
  for_each            = var.vms
  name                = each.value.name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  vm_size                = each.value.size
  tags = var.tags
  network_interface_ids = [
    azurerm_network_interface.nics[each.value].id,
  ]

The issue is with this section

network_interface_ids = [
        azurerm_network_interface.nics[each.value].id,
      ]

I'm getting ERROR

│ Error: Invalid index
│
│   on main.tf line 247, in resource "azurerm_virtual_machine" "vms":
│  247:     azurerm_network_interface.nics[each.value].id,
│     ├────────────────
│     │ azurerm_network_interface.nics is object with 2 attributes
│     │ each.value is object with 2 attributes
│
│ The given key does not identify an element in this collection value: string required.

Also tried with

network_interface_ids = [
        azurerm_network_interface.nics[each.key].id,
      ]

and got ERROR

│ Error: Invalid index
│
│   on main.tf line 249, in resource "azurerm_virtual_machine" "vms":
│  249:     azurerm_network_interface.nics[each.key].id,
│     ├────────────────
│     │ azurerm_network_interface.nics is object with 2 attributes
│     │ each.key is "vm3"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on main.tf line 249, in resource "azurerm_virtual_machine" "vms":
│  249:     azurerm_network_interface.nics[each.key].id,
│     ├────────────────
│     │ azurerm_network_interface.nics is object with 2 attributes
│     │ each.key is "vm4"
│
│ The given key does not identify an element in this collection value

What I'm doing wrong ?

CodePudding user response:

In order for this to work, you would need to modify the variable for VMs slightly:

variable "vms" {
  description = "Virtual Machines"
  type = map
  default = {
  vm3 = {
        name            = "ubuntu-test-3"
        size            = "Standard_DS1_v2"
        nic             = "nic3"
    }
  vm4 = {
        name            = "ubuntu-test-4"
        size            = "Standard_DS1_v2"
        nic             = "nic4"
  }
 }
}

Then, in the VM resource block:

resource "azurerm_virtual_machine" "vms" {
  for_each            = var.vms
  name                = each.value.name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  vm_size             = each.value.size
  tags = var.tags
  network_interface_ids = [
    azurerm_network_interface.nics[each.value.nic].id,
  ]
}

Alternatively, you could try with resource chaining with for_each [1], but then you would have to refactor the resource block a bit:

resource "azurerm_virtual_machine" "vms" {
  for_each            = azurerm_network_interface.nics
  name                = each.value.name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  vm_size             = var.vm_size # or set it to be equal to "Standard_DS1_v2"
  tags = var.tags
  network_interface_ids = [
    each.value.id,
  ]
}

Then, you would also have to define a new variable called vm_size:

variable "vm_size" {
  type        = string
  description = "VM size."

  default = "Standard_DS1_v2"
}

In the second case, you could remove the variable vms completely.


[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each#chaining-for_each-between-resources

CodePudding user response:

Replicated the same scenario and able to create resources.

Made couple of changes for the existing code base provided

  1. Added **nic = "nic" value at vms block
  2. Updated network_interface_ids = [azurerm_network_interface.nics[each.value.nic].id,]

Here is the code snippet. Step1: Main tf code as below

        provider "azurerm" {
 features {}
 }
variable "prefix" {
  default = "rg_swarna"
}

resource "azurerm_resource_group" "rg" {
  name     = "${var.prefix}-rg"
  location = "West US"
 // tags = var.tags
}
resource "azurerm_virtual_network" "vnet" {
  name                = "${var.prefix}-network-1"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
 // tags = var.tags
}
resource "azurerm_subnet" "subnet" {
  name                 = "${var.prefix}-network-subnet-1"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "nics" {
  for_each            = var.nics
  name                = each.value.name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "${each.value.name}-conf-1"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
  }

  //tags = var.tags
}
resource "azurerm_virtual_machine" "vms" {
  for_each            = var.vms
  name                = each.value.name
  vm_size                = "Standard_DS1_v2"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  network_interface_ids =  [azurerm_network_interface.nics[each.value.nic].id,]
   storage_os_disk {
    name              = "myosdisk${each.value.name}"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }
  storage_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04-LTS"
    version   = "latest"
  }
  os_profile {
    computer_name  = "TestDemo"
    admin_username = "azureuser"
    admin_password = "Azureuser@123"
  }
   os_profile_linux_config {
    disable_password_authentication = false
  }
}

Step2: variable tf file as fallows

variable "allowed_subnet_ids" {
  type        = list(string)
  description = "List of subnet IDs to be allowed to access the ACR"
}
variable "nics" {
  type = map
  default = {
    
  nic3 = {
        name = "ubuntutest3"
  }

  nic4 = {
        name = "ubuntutest4"
  }
 }
}
variable "vms" {
  description = "Virtual Machines"
  type = map
  default = {
  vm3 = {
        name            = "ubuntutest3"
        size            = "Standard_DS1_v2"
         nic             = "nic3"
    }
  vm4 = {
        name            = "ubuntutest4"
        size            = "Standard_DS1_v2"
        nic             = "nic4"
   }
 }
}
variable "allowed_ips" {
  type        = list(string)
  description = "White list IP addresses"
}

variable "sku" {
  type        = string
  description = "SKU"
}
variable "resource_group_name" {
  type        = string
  description = "resource_group_name"
}
variable "location" {
  type        = string
  description = "location"
}    

Step3:

terraform plan
terraform apply -auto-approve

Here are the reference screenshots enter image description here

Here is the output from above code

enter image description here

  • Related