Home > database >  List & String Conversion Issue for Data sources
List & String Conversion Issue for Data sources

Time:01-15

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]
  • Related