I am caught in a bit of a loop on this one. Need to provide the azure_windows_virtual_machine with a list of network interface IDs. The network interfaces are created using a separate resource block. In my variable definition for the windows vm, I provide an argument for the name[s] of said network interfaces so that we can correctly associate the nics that we want with each virtual machine. If we have 100 nics and 90 VMs, some of the VMs could get two NICs, so we want to be sure we provide some link between NIC name and VM name.
The network interface names are therefore a list(string).
I have been trying to use the values function to get the list of NIC IDs (given the names), but running into a failure: "The each object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set."
If I use a data source in the resource block, which seemed most logical, it fails too because I have a list(string) specified for the network_interface_names argument, but the data source cannot take that. It of course needs a single string. But it's never going to be a single string, it's always going to be a list (since we can have more than one NIC per VM).
I think the correct answer is to maybe create the list of IDs beforehand - trick is that it would need to almost be dynamic for each defined VM - because each VM will have a different list of network_interface_names. We therefore need to generate the new list on the fly for each VM.
Variables
variable "resource_groups" {
description = "Resource groups"
type = map(object({
location = string
}))
}
variable "virtual_networks" {
description = "virtual networks and properties"
type = map(object({
resource_group_name = string
address_space = list(string)
}))
}
variable "subnets" {
description = "subnet and their properties"
type = map(object({
resource_group_name = string
virtual_network_name = string
address_prefixes = list(string)
}))
}
variable "nic" {
description = "network interfaces"
type = map(object({
subnet_name = string
resource_group_name = string
}))
}
variable "admin_password" {
type = string
sensitive = true
}
variable "admin_user" {
type = string
sensitive = true
}
variable "windows_vm" {
description = "Windows virtual machine"
type = map(object({
network_interface_names = list(string)
resource_group_name = string
size = string
timezone = string
}))
}
INPUTS
resource_groups = {
rg-eastus-dev1 = {
location = "eastus"
}
}
virtual_networks = {
vnet-dev1 = {
resource_group_name = "rg-eastus-dev1"
address_space = ["10.0.0.0/16"]
}
}
subnets = {
snet-01 = {
resource_group_name = "rg-eastus-dev1"
virtual_network_name = "vnet-dev1"
address_prefixes = ["10.0.1.0/24"]
}
}
nic = {
nic1 = {
subnet_name = "snet-01"
resource_group_name = "rg-eastus-dev1"
}
}
admin_password = "s}8cpH96qa.1BQ"
admin_user = "padmin"
windows_vm = {
winvm1 = {
network_interface_names = ["nic1"]
resource_group_name = "rg-eastus-dev1"
size = "Standard_B2s"
timezone = "Eastern Standard Time"
}
}
MAIN
resource "azurerm_resource_group" "rgs" {
for_each = var.resource_groups
name = each.key
location = each.value["location"]
}
data "azurerm_resource_group" "rgs" {
for_each = var.resource_groups
name = each.key
depends_on = [
azurerm_resource_group.rgs
]
}
resource "azurerm_virtual_network" "vnet" {
for_each = var.virtual_networks
name = each.key
resource_group_name = each.value["resource_group_name"]
address_space = each.value["address_space"]
location = data.azurerm_resource_group.rgs[each.value["resource_group_name"]].location
}
resource "azurerm_subnet" "subnet" {
for_each = var.subnets
name = each.key
resource_group_name = each.value["resource_group_name"]
virtual_network_name = each.value["virtual_network_name"]
address_prefixes = each.value["address_prefixes"]
depends_on = [
azurerm_virtual_network.vnet
]
}
data "azurerm_subnet" "subnet" {
for_each = var.subnets
name = each.key
virtual_network_name = each.value["virtual_network_name"]
resource_group_name = each.value["resource_group_name"]
depends_on = [
azurerm_resource_group.rgs
]
}
resource "azurerm_network_interface" "nics" {
for_each = var.nic
ip_configuration {
name = each.key
subnet_id = data.azurerm_subnet.subnet[each.value["subnet_name"]].id
private_ip_address_allocation = "Dynamic"
}
location = data.azurerm_resource_group.rgs[each.value["resource_group_name"]].location
name = each.key
resource_group_name = each.value["resource_group_name"]
depends_on = [
azurerm_resource_group.rgs,
azurerm_subnet.subnet
]
}
data "azurerm_network_interface" "nics" {
for_each = var.nic
name = each.key
resource_group_name = each.value["resource_group_name"]
depends_on = [
azurerm_resource_group.rgs
]
}
resource "azurerm_windows_virtual_machine" "windows_vm" {
for_each = var.windows_vm
admin_password = var.admin_password
admin_username = var.admin_user
location = data.azurerm_resource_group.rgs[each.value["resource_group_name"]].location
name = each.key
network_interface_ids = values(data.azurerm_network_interface.nics[each.value["network_interface_names"]].id)
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
resource_group_name = each.value["resource_group_name"]
size = each.value["size"]
timezone = each.value["timezone"]
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2019-Datacenter"
version = "latest"
}
}
Current Error on Plan
╷
│ Error: Invalid index
│
│ on main.tf line 75, in resource "azurerm_windows_virtual_machine" "windows_vm":
│ 75: network_interface_ids = values(data.azurerm_network_interface.nics[each.value["network_interface_names"]].id)
│ ├────────────────
│ │ data.azurerm_network_interface.nics is object with 1 attribute "nic1"
│ │ each.value["network_interface_names"] is list of string with 1 element
│
│ The given key does not identify an element in this collection value: string required.
Possible Solution - But Not working Provide a map, keyed off the VM name, of NIC IDs. Then, in the windows_vm resource, take that map and try to get the list of NIC ID values.
locals {
nic_ids {
[for k, v in var.windows_vm : k => v {data.azurerm_network_interface.nics[v.network_interface_names]}.id]
}
}
resource "azurerm_windows_virtual_machine" "windows_vm" {
for_each = var.windows_vm
admin_password = var.admin_password
admin_username = var.admin_user
location = data.azurerm_resource_group.rgs[each.value["resource_group_name"]].location
name = each.key
network_interface_ids = values(local.nic_ids[each.key])
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
resource_group_name = each.value["resource_group_name"]
size = each.value["size"]
timezone = each.value["timezone"]
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2019-Datacenter"
version = "latest"
}
}
CodePudding user response:
First of all, you do not need to call data
after creation of each resource
. The resource
itself will contain all the information that you need. So you should eliminate all data
sources in your code and use resource
directly.
But returning to the error you provided. One way to generate the list dynamically, would be:
network_interface_ids = [for ni_name in each.value["network_interface_names"]: azurerm_network_interface.nics[ni_name].id]