1

I have been wanting to create a "azurerm_virtual_network" by passing location and name of the resource group attribute using data source: "azurerm_resource_group". My data source itself is created using "for_each". The idea here is, if I want to provision more VNETs in different groups, I just add more resource group names into the variable list, "var.network_rg". However, I am not able to figure out how to pass this list of values as an input to the attributes of vnet resource which is also created using for_each. The loop seems to be taking the value from resource config "for_each" instead of data source. Can anyone please help identify what is wrong here ? Is this even the correct way to achieve what I'm trying to do ? Below is my code:

child main.tf

data "azurerm_resource_group" "network_rg" {
  for_each = toset(var.network_rg)
  name     = "${var.owner}_${var.env}_${each.key}_${var.location_short}"
}

resource "azurerm_virtual_network" "vnet" {
  for_each            = var.vnet
  name                = "${var.owner}_${var.env}_${each.value["name"]}_${var.location_short}}"
  location            = data.azurerm_resource_group.network_rg[each.key].location
  resource_group_name = data.azurerm_resource_group.network_rg[each.key].name
  address_space       = each.value["address_space"]
  lifecycle {
    ignore_changes = [
      tags["Created"]
    ]
  }
  tags = {
    "Purpose" = var.purpose_tag #"Test"
    }
}

child variable.tf

variable "owner" {
  type        = string
  description = "Owner of the resource"
}
variable "network_rg" {
  type        = list(string)
  description = "RG in which VNET is to be created"
}
variable "location" {
  type        = string
  description = "Location in which resource group needs to be created"
}
variable "env" {
  type        = string
  description = "Environment to be deployed in"
}
variable "location_short" {
  type        = string
  description = "Short name for location"
}
variable "vnet" {
  type = map
  description = "Map of VNET attributes"
}
variable "purpose_tag" {
  type        = string
  description = "Purpose Tag"
}

.tfvars

owner = "rrb"
location = "australiaeast"
env = "dev"
location_short = "aue"
purpose_tag = "Test"
####################### Resource Group Module ####################################
rg_name = ["platform"] #add rg name here to create more rg

###################### Network Module ############################################

network_rg = ["platform"]
vnet = {
    hub_vnet = {
        name = "hub"
        address_space = ["10.200.0.0/16"]
    }
    spoke_vnet = {
        name = "spoke"
        address_space = ["10.201.0.0/16"]
    }
}

Root main.tf

module "rg" {
    source = "./modules/resourceGroup"
    owner = var.owner
    env  = var.env
    rg_name = var.rg_name
    location = var.location
    location_short = var.location_short
    purpose_tag = var.purpose_tag
}

module "network" {
    source = "./modules/network"
    network_rg = var.network_rg
    vnet = var.vnet
    owner = var.owner
    env  = var.env
    location = var.location
    location_short = var.location_short
    purpose_tag = var.purpose_tag
}

Error Message

│ Error: Invalid index
│ 
│   on modules/network/main.tf line 10, in resource "azurerm_virtual_network" "vnet":
│   10:   location            = data.azurerm_resource_group.network_rg[each.key].location
│     ├────────────────
│     │ data.azurerm_resource_group.network_rg is object with 1 attribute "platform"
│     │ each.key is "spoke_vnet"
│ 
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│ 
│   on modules/network/main.tf line 10, in resource "azurerm_virtual_network" "vnet":
│   10:   location            = data.azurerm_resource_group.network_rg[each.key].location
│     ├────────────────
│     │ data.azurerm_resource_group.network_rg is object with 1 attribute "platform"
│     │ each.key is "hub_vnet"
│ 
│ The given key does not identify an element in this collection value.

Output of for_each loop as per below suggestion

> {for idx, val in setproduct(keys(var.vnet), var.network_rg): idx => val}
{
  "0" = [
    "hub_vnet",
    "platform",
  ]
  "1" = [
    "spoke_vnet",
    "platform",
  ]
}
rb16
  • 129
  • 4
  • 11
  • In the first `for_each` (in the data source) you are using one variable and in the `azurerm_virtual_network` you are using another one. Those two do not have the same key values, hence why you are getting an error. In other words, the data source will have a key `platform` and the resource will use keys from the `vnet` variable, so it will have keys of `hub_vnet` and `spoke_vnet`. – Marko E Dec 11 '22 at 09:56
  • Yes, that's correct @MarkoE. How do I pass the key of data source in the resource config in my case so that it picks the correct value ? Why is it picking the key from `vnet` variable inspite of referring data source ? – rb16 Dec 11 '22 at 17:48
  • Because you told it so. :) The `for_each` in the data source has one key, the `for_each` for the resource has a second one. – Marko E Dec 11 '22 at 18:55

1 Answers1

1

You have to iterate over vents and network_rgs. Normally this would be done using double for loop, but in you case you could use setproduct as well.

resource "azurerm_virtual_network" "vnet" {
  for_each            = {for idx, val in setproduct(keys(var.vnet), var.network_rg): idx => val}
  name                = "${var.owner}_${var.env}_${var.vnet[each.value[0]].name}_${var.location_short}}"

  location            = data.azurerm_resource_group.network_rg[each.value[1]].location

  resource_group_name = data.azurerm_resource_group.network_rg[each.value[1]].name

  address_space       = var.vnet[each.value[0]].address_space
  lifecycle {
    ignore_changes = [
      tags["Created"]
    ]
  }
  tags = {
    "Purpose" = var.purpose_tag #"Test"
    }
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • Thanks for the above snippet @Marcin! Could you please explain how the `for_each` is actually translating the `for` loop you have used along with `setproduct`. I did try my best in understanding the code you have written, but finding it hard to decode it. Please do bare with me as I'm an amateur in meta-arguements, functions and expressions. – rb16 Dec 11 '22 at 17:52
  • 3
    @rb16 https://developer.hashicorp.com/terraform/language/expressions/for. Also: Marcin 1, chatbot 0. – Matthew Schuchard Dec 11 '22 at 18:38
  • Hello @Marcin, I tried decoding it further and I've added the output of the `for_each` in the above section for better clarity. Wouldn't the `var.vnet[each.value[0]]` result in `hub_vnet`, and `platform` where as I need `spoke_vnet` and `hub_vnet` ? Also, wouldn't the behavior be similar when using data source as well, wherein `network_rg[each.value[1]]` would result in `spoke_vnet` and `platform` but I just need `platform`. – rb16 Dec 12 '22 at 13:55
  • @rb16 For new issues, please make new question with relevant details. – Marcin Dec 12 '22 at 22:33