Terraform on Azure - I am trying to call the results of a for_each block in one resource in another resource.
I have gone through the following & cant seem to be able to wrap my head around the logic.
https://stackoverflow.com/questions/71646136/terraform-reference-a-for-each-resource-from-another-for-each-resource
https://stackoverflow.com/questions/68571073/for-each-loop-in-resource-block-terraform
I have 2 resource blocks [I have added the locals file ]
# Local Variables
#
locals {
# Create a flattened list for resource groups in multiple regions
multi_region_rg = flatten([
for rg_key, rg in var.multiple_resource_groups : [
for region_key, region in var.regions : {
rg_name = var.environment != "" ? format("%s-rg-%s-%s-%s", var.tenant, rg_key, var.environment, region.code) : format("%s-rg-%s-%s", var.tenant, rg_key, region.code)
region_name = region.name
}
]
])
}
# Local Variables
#
locals {
# Create a flattened list for resource groups in multiple regions
multi_region_vnet = flatten([
for vnet_key, vnet in var.multiple_vnets : [
for region_key, region in var.regions : {
vnet_name = var.environment != "" ? format("%s-rg-%s-%s-%s", var.tenant, vnet_key, var.environment, region.code) : format("%s-rg-%s-%s", var.tenant, vnet_key, region.code)
region_name = region.name
virtual_network_address_space = vnet.address_space
}
]
])
}
TFVARS file
# Global tags
global_tags = { "ManagedBy" = "Terraform" }
# Map of regions for deployment of resource groups
regions = {
region1 = { name = "australiaeast", code = "ae" },
region2 = { name = "australiasoutheast", code = "ase" }
}
# Tenant
tenant = "cubem_"
# Map of resource groups
# NOTE: Use short names or aliases for resource groups as the code will generate the resource group name as per the following format:
# <tenant>-rg-<resource_group_alias>-<environment>-<region_code>
multiple_resource_groups = {
connectivity = {
tags = {
CostCenter = "NETWORKS",
Department = "Network Services"
}
}
}
###################################### Vnet Values #######################
# Map of resource groups
# NOTE: Use short names or aliases for resource groups as the code will generate the resource group name as per the following format:
# <tenant>-rg-<resource_group_alias>-<environment>-<region_code>
multiple_vnets = {
vnetx = { address_space = ["10.1.0.0/24"] },
}
# Create 2 resource groups in 2 regions [1 in each region]
#
resource "azurerm_resource_group" "rg" {
for_each = {
for k, v in local.multi_region_rg : v.rg_name => v
}
name = each.key
location = each.value.region_name
}
# Create 2 vnets in 2 regions [1 in each region]
#
resource "azurerm_virtual_network" "vnet" {
for_each = {
for k, v in local.multi_region_vnet : v.vnet_name => v
}
name = each.key
location = each.value.region_name
address_space = each.value.virtual_network_address_space
resource_group_name = azurerm_resource_group.rg[each.key] #Im trying to call the results of the resource group block above
}
These blocks work well when they are run independently but I want to have the ability to run them concurrently so that the vnets gets deployed into the resource groups that are created.
If I use this line resource_group_name = azurerm_resource_group.rg[each.key]
its throwing this error
PS C:\PycharmProjects\terraform-projects\my_azure_connectivity_ref_repo\hub-and-spoke\modules\vnet> terraform plan
╷
│ Error: Invalid index
│
│ on main.tf line 69, in resource "azurerm_virtual_network" "vnet":
│ 69: resource_group_name = azurerm_resource_group.rg[each.key]
│ ├────────────────
│ │ azurerm_resource_group.rg is object with 2 attributes
│ │ each.key is "rg-vnetx-ae"
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│ on main.tf line 69, in resource "azurerm_virtual_network" "vnet":
│ 69: resource_group_name = azurerm_resource_group.rg[each.key]
│ ├────────────────
│ │ azurerm_resource_group.rg is object with 2 attributes
│ │ each.key is "rg-vnetx-ase"
│
│ The given key does not identify an element in this collection value.
╵
PS C:\PycharmProjects\terraform-projects\my_azure_connectivity_ref_repo\hub-and-spoke\modules\vnet>
There are two issues in your resource "azurerm_virtual_network" "vnet"
definition in the resource_group_name
attribute.
[each.key]
in azurerm_resource_group.rg[each.key]
expression does not exists. here [each.key]
is the vnet name actually coming from for_each = { for k, v in local.multi_region_vnet : v.vnet_name => v}
which is not available in the resource azurerm_resource_group.rg
> { for k, v in local.multi_region_vnet : v.vnet_name => v }
{
"test-rg-vnet_key-dev-ae" = {
"region_name" = "australiaeast"
"virtual_network_address_space" = [
"10.1.0.0/24",
]
"vnet_name" = "test-rg-vnet_key-dev-ae"
}
"test-rg-vnet_key-dev-ase" = {
"region_name" = "australiasoutheast"
"virtual_network_address_space" = [
"10.1.0.0/24",
]
"vnet_name" = "test-rg-vnet_key-dev-ase"
}
}
resource_group_name
attribute of vnet resource with the reference. in spite of azurerm_resource_group.rg[$(expression)]
should be azurerm_resource_group.rg[$(expression)].name
Hence, You have to adjust either of your resources with a standard key which is common for both resource_group and vnet. I have done it in the resource group with location/region as it is required in both of the resources.
Hence the correct code addressing both the above issues would be.
## key is now region_name in azurerm_resource_group ##
resource "azurerm_resource_group" "rg" {
for_each = {
for k, v in local.multi_region_rg : v.region_name => v
}
name = each.value.rg_name
location = each.key
}
resource "azurerm_virtual_network" "vnet" {
for_each = {
for k, v in local.multi_region_vnet : v.vnet_name => v
}
name = each.key
location = each.value.region_name
address_space = each.value.virtual_network_address_space
resource_group_name = azurerm_resource_group.rg[(each.value.region_name)].name
}
➜ random_local_tests git:(main) ✗ terraform apply -auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.rg["australiaeast"] will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "australiaeast"
+ name = "test-rg-vnet_key-dev-ae"
}
# azurerm_resource_group.rg["australiasoutheast"] will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "australiasoutheast"
+ name = "test-rg-vnet_key-dev-ase"
}
# azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ae"] will be created
+ resource "azurerm_virtual_network" "vnet" {
+ address_space = [
+ "10.1.0.0/24",
]
+ dns_servers = (known after apply)
+ guid = (known after apply)
+ id = (known after apply)
+ location = "australiaeast"
+ name = "test-rg-vnet_key-dev-ae"
+ resource_group_name = "test-rg-vnet_key-dev-ae"
+ subnet = (known after apply)
}
# azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ase"] will be created
+ resource "azurerm_virtual_network" "vnet" {
+ address_space = [
+ "10.1.0.0/24",
]
+ dns_servers = (known after apply)
+ guid = (known after apply)
+ id = (known after apply)
+ location = "australiasoutheast"
+ name = "test-rg-vnet_key-dev-ase"
+ resource_group_name = "test-rg-vnet_key-dev-ase"
+ subnet = (known after apply)
}
Plan: 4 to add, 0 to change, 0 to destroy.
azurerm_resource_group.rg["australiaeast"]: Creating...
azurerm_resource_group.rg["australiasoutheast"]: Creating...
azurerm_resource_group.rg["australiaeast"]: Creation complete after 5s [id=/subscriptions/xxxxxxxxxxxx/resourceGroups/test-rg-vnet_key-dev-ae]
azurerm_resource_group.rg["australiasoutheast"]: Creation complete after 6s [id=/subscriptions/xxxxxxxxxxxx/resourceGroups/test-rg-vnet_key-dev-ase]
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ae"]: Creating...
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ase"]: Creating...
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ase"]: Still creating... [10s elapsed]
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ae"]: Still creating... [10s elapsed]
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ae"]: Creation complete after 18s [id=/subscriptions/xxxxxxxxxxxx/resourceGroups/test-rg-vnet_key-dev-ae/providers/Microsoft.Network/virtualNetworks/test-rg-vnet_key-dev-ae]
azurerm_virtual_network.vnet["test-rg-vnet_key-dev-ase"]: Creation complete after 19s [id=/subscriptions/xxxxxxxxxxxx/resourceGroups/test-rg-vnet_key-dev-ase/providers/Microsoft.Network/virtualNetworks/test-rg-vnet_key-dev-ase]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Hope it helps.
And additional info which is not relevant here too much I have used static values instead of some variables.
rg_name = var.environment != "" ? format("%s-rg-%s-%s-%s", "test", "vnet_key", var.environment, region.code) : format("%s-rg-%s-%s", "test", "vnet_key", region.code)
vnet_name = var.environment != "" ? format("%s-rg-%s-%s-%s", "test", "vnet_key", var.environment, region.code) : format("%s-rg-%s-%s", "test", "vnet_key", region.code)