Loop over objects within objects using terraform - terraform

I have a route_table variable containing route table objects which each have several route objects.
variable "route_tables" {
description = "a map of route tables"
type = map(any)
default = {
"route_table01" = {
name = "route_table01"
route = {
name = "route1"
address_prefix = "17.65.255.0/24"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.18.128.6"
}
route = {
name = "route2"
address_prefix = "204.61.3.87/32"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.18.128.6"
}
route = {
name = "route3"
address_prefix = "13.61.37.248/29"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.18.128.6"
}
}
}
}
I'm now trying to loop over them in my module:
resource "azurerm_route_table" "route_tables" {
for_each = var.route_tables
name = each.value.name
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
tags = local.tags
dynamic "route" {
for_each = each.value.route
content {
name = each.value.route.name
address_prefix = each.value.route.address_prefix
next_hop_type = each.value.route.next_hop_type
next_hop_in_ip_address = each.value.route.next_hop_in_ip_address
}
}
}
The tf plan completes successfully, but only 1 route is being added to the route table:
How can I loop over these routes so that all of them are added to the route table?

I believe one change you'll want to make is that you currently have nothing allowing you to iterate through each nested block. To use for_each, your dynamic block needs to use a different collection as the basis for how it iterates. Instead:
resource "azurerm_route_table" "route_tables" {
for_each = {
for routes in var.route_tables : routes.name => {
name = routes.name
location = routes.location
resource_group_name = routes.resource_group.name
route = routes.route
}
}
name = each.value.name
location = each.value.location
resource_group_name = each.value.resource_group_name
dynamic "route" {
for_each = each.value.route
content {
name = route.value.name
address_prefix = route.value.address_prefix
next_hop_type = route.value.next_hop_type
next_hop_in_ip_address = route.value.next_hop_in_ip_address
}
}
}
Where your variable might be
variable "route_tables" {
description = "List of route tables to create."
type = any
default = []
}
And your input might be
route_tables = [
{
name = ""
location = ""
resource_group_name = ""
num_loops = 2
route = [
{
name = ""
address_prefix = ""
next_hop_type = ""
next_hop_in_ip_address = ""
},
{
name = ""
address_prefix = ""
next_hop_type = ""
next_hop_in_ip_address = ""
}
}
]
}
Good luck and I hope this helps.
Cheers.

Related

azure terraform: how to attach multiple data disks to multiple VMs

I'm following Neal Shah's instructions for deploying multiple VMs with multiple managed disks (https://www.nealshah.dev/posts/2020/05/terraform-for-azure-deploying-multiple-vms-with-multiple-managed-disks/#deploying-multiple-vms-with-multiple-datadisks)
everything works fine except for the azurerm_virtual_machine_data_disk_attachment resource which fails with the following error
│ Error: Invalid index
│
│ on main.tf line 103, in resource "azurerm_virtual_machine_data_disk_attachment" "managed_disk_attach":
│ 103: virtual_machine_id = azurerm_linux_virtual_machine.vms[element(split("_", each.key), 1)].id
│ ├────────────────
│ │ azurerm_linux_virtual_machine.vms is tuple with 3 elements
│ │ each.key is "datadisk_dca0-apache-cassandra-node0_disk00"
│
│ The given key does not identify an element in this collection value: a number is required.
my code is below:
locals {
vm_datadiskdisk_count_map = { for k in toset(var.nodes) : k => var.data_disk_count }
luns = { for k in local.datadisk_lun_map : k.datadisk_name => k.lun }
datadisk_lun_map = flatten([
for vm_name, count in local.vm_datadiskdisk_count_map : [
for i in range(count) : {
datadisk_name = format("datadisk_%s_disk%02d", vm_name, i)
lun = i
}
]
])
}
# create resource group
resource "azurerm_resource_group" "resource_group" {
name = format("%s-%s", var.dca, var.name)
location = var.location
}
# create availability set
resource "azurerm_availability_set" "vm_availability_set" {
name = format("%s-%s-availability-set", var.dca, var.name)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
}
# create Security Group to access linux
resource "azurerm_network_security_group" "linux_vm_nsg" {
name = format("%s-%s-linux-vm-nsg", var.dca, var.name)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
security_rule {
name = "AllowSSH"
description = "Allow SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# associate the linux NSG with the subnet
resource "azurerm_subnet_network_security_group_association" "linux_vm_nsg_association" {
subnet_id = "${data.azurerm_subnet.subnet.id}"
network_security_group_id = azurerm_network_security_group.linux_vm_nsg.id
}
# create NICs for apache cassandra hosts
resource "azurerm_network_interface" "vm_nics" {
depends_on = [azurerm_subnet_network_security_group_association.linux_vm_nsg_association]
count = length(var.nodes)
name = format("%s-%s-nic${count.index}", var.dca, var.name)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
ip_configuration {
name = format("%s-%s-apache-cassandra-ip", var.dca, var.name)
subnet_id = "${data.azurerm_subnet.subnet.id}"
private_ip_address_allocation = "Dynamic"
}
}
# create apache cassandra VMs
resource "azurerm_linux_virtual_machine" "vms" {
count = length(var.nodes)
name = element(var.nodes, count.index)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
network_interface_ids = [element(azurerm_network_interface.vm_nics.*.id, count.index)]
availability_set_id = azurerm_availability_set.vm_availability_set.id
size = var.vm_size
admin_username = var.admin_username
disable_password_authentication = true
admin_ssh_key {
username = var.admin_username
public_key = var.ssh_pub_key
}
source_image_id = var.source_image_id
os_disk {
caching = "ReadWrite"
storage_account_type = var.storage_account_type
disk_size_gb = var.os_disk_size_gb
}
}
# create data disk(s) for VMs
resource "azurerm_managed_disk" "managed_disk" {
for_each= toset([for j in local.datadisk_lun_map : j.datadisk_name])
name= each.key
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
storage_account_type = var.storage_account_type
create_option = "Empty"
disk_size_gb = var.disk_size_gb
}
resource "azurerm_virtual_machine_data_disk_attachment" "managed_disk_attach" {
for_each = toset([for j in local.datadisk_lun_map : j.datadisk_name])
managed_disk_id = azurerm_managed_disk.managed_disk[each.key].id
virtual_machine_id = azurerm_linux_virtual_machine.vms[element(split("_", each.key), 1)].id
lun = lookup(local.luns, each.key)
caching = "ReadWrite"
}
anyone know how to accomplish this? thanks!
I've tried several different approaches to this but have been unsuccessful so far, I was expecting it to work as described in Neal's post
I was able to get this working. However, I have not tested adding/removing nodes/disks yet. But this working to create multiple VMs with multiple data disks attached to each VM.
I use a variable file that I source to substitute the variables in the *.tf files.
variables.tf
variable "azure_subscription_id" {
type = string
description = "Azure Subscription ID"
default = ""
}
variable "dca" {
type = string
description = "datacenter [dca0|dca2|dca4|dca6]."
default = ""
}
variable "location" {
type = string
description = "Location of the resource group."
default = ""
}
variable "resource_group" {
type = string
description = "resource group name."
default = ""
}
variable "subnet_name" {
type = string
description = "subnet name"
default = ""
}
variable "vnet_name" {
type = string
description = "vnet name"
default = ""
}
variable "vnet_rg" {
type = string
description = "vnet resource group"
default = ""
}
variable "vm_size" {
type = string
description = "vm size"
default = ""
}
variable "os_disk_size_gb" {
type = string
description = "vm os disk size gb"
default = ""
}
variable "data_disk_size_gb" {
type = string
description = "vm data disk size gb"
default = ""
}
variable "admin_username" {
type = string
description = "admin user name"
default = ""
}
variable "ssh_pub_key" {
type = string
description = "public key for admin user"
default = ""
}
variable "source_image_id" {
type = string
description = "image id"
default = ""
}
variable "os_disk_storage_account_type" {
type = string
description = ""
default = ""
}
variable "data_disk_storage_account_type" {
type = string
description = ""
default = ""
}
variable "vm_list" {
type = map(object({
hostname = string
}))
default = {
vm0 ={
hostname = "${dca}-${name}-node-0"
},
vm1 = {
hostname = "${dca}-${name}-node-1"
}
vm2 = {
hostname = "${dca}-${name}-node-2"
}
}
}
variable "disks_per_instance" {
type = string
description = ""
default = ""
}
terraform.tfvars
# subscription
azure_subscription_id = "${azure_subscription_id}"
# name and location
resource_group = "${dca}-${name}"
location = "${location}"
dca = "${dca}"
# Network
subnet_name = "${subnet_name}"
vnet_name = "${dca}vnet"
vnet_rg = "th-${dca}-vnet"
# VM
vm_size = "${vm_size}"
os_disk_size_gb = "${os_disk_size_gb}"
os_disk_storage_account_type = "${os_disk_storage_account_type}"
source_image_id = "${source_image_id}"
# User/key info
admin_username = "${admin_username}"
ssh_pub_key = ${ssh_pub_key}
# data disk info
data_disk_storage_account_type = "${data_disk_storage_account_type}"
data_disk_size_gb = "${data_disk_size_gb}"
disks_per_instance= "${disks_per_instance}"
main.tf
# set locals for multi data disks
locals {
vm_datadiskdisk_count_map = { for k, query in var.vm_list : k => var.disks_per_instance }
luns = { for k in local.datadisk_lun_map : k.datadisk_name => k.lun }
datadisk_lun_map = flatten([
for vm_name, count in local.vm_datadiskdisk_count_map : [
for i in range(count) : {
datadisk_name = format("datadisk_%s_disk%02d", vm_name, i)
lun = i
}
]
])
}
# create resource group
resource "azurerm_resource_group" "resource_group" {
name = format("%s", var.resource_group)
location = var.location
}
# create data disk(s)
resource "azurerm_managed_disk" "managed_disk" {
for_each = toset([for j in local.datadisk_lun_map : j.datadisk_name])
name = each.key
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
storage_account_type = var.data_disk_storage_account_type
create_option = "Empty"
disk_size_gb = var.data_disk_size_gb
}
# create availability set
resource "azurerm_availability_set" "vm_availability_set" {
name = format("%s-availability-set", var.resource_group)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
}
# create Security Group to access linux
resource "azurerm_network_security_group" "linux_vm_nsg" {
name = format("%s-linux-vm-nsg", var.resource_group)
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
security_rule {
name = "AllowSSH"
description = "Allow SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
# associate the linux NSG with the subnet
resource "azurerm_subnet_network_security_group_association" "linux_vm_nsg_association" {
subnet_id = "${data.azurerm_subnet.subnet.id}"
network_security_group_id = azurerm_network_security_group.linux_vm_nsg.id
}
# create NICs for vms
resource "azurerm_network_interface" "nics" {
depends_on = [azurerm_subnet_network_security_group_association.linux_vm_nsg_association]
for_each = var.vm_list
name = "${each.value.hostname}-nic"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
ip_configuration {
name = format("%s-proxy-ip", var.resource_group)
subnet_id = "${data.azurerm_subnet.subnet.id}"
private_ip_address_allocation = "Dynamic"
}
}
# create VMs
resource "azurerm_linux_virtual_machine" "vms" {
for_each = var.vm_list
name = each.value.hostname
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
network_interface_ids = [azurerm_network_interface.nics[each.key].id]
availability_set_id = azurerm_availability_set.vm_availability_set.id
size = var.vm_size
source_image_id = var.source_image_id
custom_data = filebase64("cloud-init.sh")
admin_username = var.admin_username
disable_password_authentication = true
admin_ssh_key {
username = var.admin_username
public_key = var.ssh_pub_key
}
os_disk {
caching = "ReadWrite"
storage_account_type = var.os_disk_storage_account_type
disk_size_gb = var.os_disk_size_gb
}
}
# attache data disks vms
resource "azurerm_virtual_machine_data_disk_attachment" "managed_disk_attach" {
for_each = toset([for j in local.datadisk_lun_map : j.datadisk_name])
managed_disk_id = azurerm_managed_disk.managed_disk[each.key].id
virtual_machine_id = azurerm_linux_virtual_machine.vms[element(split("_", each.key), 1)].id
lun = lookup(local.luns, each.key)
caching = "ReadWrite"
}

terraform add multiple VM and change the default name of the resoruces

I need a bit of help as I have a terraform script and I want to add multiple VM and change the name of the network card like node_name-NIC and do the same thing even for the other resources but is failing and i cant fine the proper way to do it.
below is the terraform script
terraform {
required_providers {
azurerm = {
// source = "hashicorp/azurerm"
version = "=1.44"
}
}
}
locals {
rsname = "testing-new-terraform-modules"
node_name = ["server1","server2"]
clustersize = 2
node_size = "Standard_B4ms"
av_set_name = "Windows-AV-Set"
vnet_name = "VNET_1"
vnet_rg = "RG_VNET_D"
gw_subnet = "SUB_GW_INT"
vm_subnet = "SUB_WIN"
image_rg = "RG__TEMPLATE"
common_tags = {
lbuildingblock = "GENERAL"
customer = "IND"
}
}
module "resource_group" {
source = "../modules/resources/azure/data-resource-group"
rsname = local.rsname
}
data "azurerm_virtual_network" "virtual_network" {
name = local.vnet_name
resource_group_name = local.vnet_rg
}
# GatewayZone subnet, for the Load Balancer frontend IP address
module "gw_subnet" {
source = "../modules/resources/azure/data-subnet"
subnet-name = local.gw_subnet
vnet-name = data.azurerm_virtual_network.virtual_network.name
rs-name = data.azurerm_virtual_network.virtual_network.resource_group_name
}
module "windows_subnet" {
source = "../modules/resources/azure/data-subnet"
// We will use the SUB_LHIND_P_APP subnet, no need to create a new subnet just for two servers
subnet-name = local.vm_subnet
rs-name = local.vnet_rg
vnet-name = local.vnet_name
}
//data "azurerm_network_security_group" "app_nsg" {
//
// name = "SUB_LHIND_D_APP_NSG"
// resource_group_name = data.azurerm_virtual_network.virtual_network.resource_group_name
//}
module "nic" {
source = "../modules/resources/azure/network-interface"
location = module.resource_group.rs_group_location
name = "${local.node_name[0]}-NIC"
nic_count = local.clustersize
resource_group = module.resource_group.rs_group_name
subnet_id = module.windows_subnet.subnet_id
tags = local.common_tags
}
module "av_set" {
source = "../modules/resources/azure/availability-set"
av_name = local.av_set_name
resource_group = module.resource_group.rs_group_name
location = module.resource_group.rs_group_location
}
module "template_image" {
source = "../modules/resources/azure/data-templates"
template_name = "WindowsServer2019"
resource_group = local.image_rg
}
module "windows" {
source = "../modules/resources/azure/windows-server"
location = module.resource_group.rs_group_location
network_interface_ids = module.nic.nic_id
node_count = local.clustersize
node_name = local.node_name
node_size = local.node_size
av_set_id = module.av_set.availability_set_id
resource_group = module.resource_group.rs_group_name
template_id = module.template_image.template_id
username = var.username
password = var.password
domain_user = var.domain_user
domain_pass = var.domain_pass
}
is failing with the below error
Error: Invalid index
on ../modules/resources/azure/network-interface/main.tf line 10, in resource "azurerm_network_interface" "nic":
10: name = var.name[count.index]
|----------------
| count.index is 0
| var.name is "SW-AZLHIND-580-NIC"
This value does not have any indices.
and the resource Network-Interface is like below
resource "azurerm_network_interface" "nic" {
count = var.nic_count
location = var.location
name = var.name[count.index]
resource_group_name = var.resource_group
tags = var.tags
// network_security_group_id = var.network_security_group_id
ip_configuration {
name = var.name[count.index]
private_ip_address_allocation = "dynamic"
subnet_id = var.subnet_id
}
}
You can use the following:
name = "{var.name}-${count.index}"

Dynamic deployment of Private endpoint

I am trying to develop a module that if the variable DeployPrivateEndpoint == true will deploy the private endpoint and if false it will not be deployed.
I currently have the following code:
resource "azurerm_container_registry" "ACR" {
count = length(var.ACR_Name)
name = var.ACR_Name[count.index]
resource_group_name = var.resourcegroup_name
location = var.location
sku = var.ACR_Sku
admin_enabled = var.ACR_AdminEnabled
georeplication_locations = var.ACR_GeoRepLocation
}
resource "azurerm_private_dns_zone" "PDZ" {
count = var.DeployPrivateEndpoint == true ? 1 : 0
name = "privatelink.azurecr.io"
resource_group_name = var.resourcegroup_name
}
resource "azurerm_private_endpoint" "PEP" {
count = var.DeployPrivateEndpoint == true ? length(var.PEP_Name) : 0
name = var.PEP_Name[count.index]
location = var.location
resource_group_name = var.resourcegroup_name
subnet_id = element(concat(var.subnet_id[*], [""]), count.index)
private_dns_zone_group {
name = "private-dns-zone-group"
private_dns_zone_ids = element(concat(azurerm_private_dns_zone.PDZ.*.id, [""]), count.index)
}
private_service_connection {
name = var.PEP_Name[count.index]
private_connection_resource_id = element(concat(azurerm_container_registry.ACR.*.id, [""]), count.index)
subresource_names = [ "registry" ]
is_manual_connection = false
}
}
The code only crashes on the part at private_dns_zone_group at this point if the value of the variable is false. Terraform expects that a private_dns_zone_ids will be given, but it is not created because the variable is set to false. I get the following error:
Error: Invalid index
on .terraform\modules\containerRegistry\outputs.tf line 10, in
output "ACR_PDZID": 10: value =
azurerm_private_dns_zone.PDZ.0.id
|----------------
| azurerm_private_dns_zone.PDZ is empty tuple
any help is appreciated!
Edit:
The module is called trough a main that looks like this:
terraform {
required_version = ">= 0.13"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.47.0"
}
}
}
provider "azurerm" {
subscription_id = "****"
client_id = "****"
client_secret = "*****"
tenant_id = "*****"
features {}
}
module "ResourceGroups" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/ResourceGroup"
location = var.location
RG_Name = var.RG_Name
}
module "VirtualNetwork" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/VirtualNetwork"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
VNET_Name = var.vnet_name
VNET_Cidr = var.vnet_cidr
}
module "Subnet" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/Subnet"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
VNET_name = module.VirtualNetwork.VNET_Name[0]
SNET_cidr = var.subnet_cidr
SNET_name = var.subnet_names
}
module "containerRegistry" {
source = "git::https://***#dev.azure.com/***/AzureTerraformModules/_git/ContainerRegistry"
resourcegroup_name = module.ResourceGroups.RG_Name[0]
location = var.location
subnet_id = module.Subnet.SNET_ID
PEP_Name = ["****", "*****"]
ACR_Name = ["****", "*****" ]
ACR_Sku = "Premium"
DeployPrivateEndpoint = false
}
The output.tf file form the module looks lik this:
output "ACR_ID" {
value = azurerm_container_registry.ACR.*.id
}
output "ACR_LoginServer" {
value = azurerm_container_registry.ACR.*.login_server
}
output "ACR_PDZID" {
value = azurerm_private_dns_zone.PDZ.0.id
}
output "ACR_PEPID" {
value = azurerm_private_endpoint.PEP.*.id
}
You should tune a bit your ACR_PDZID output, change 0 to *
output "ACR_PDZID" should look like this:
output "ACR_PDZID" {
value = azurerm_private_dns_zone.PDZ.*.id
}

Terraform for_each if value exists in object

I would like to dynamically create some subnets and route tables from a .tfvars file, and then link each subnet to the associated route table if specified.
Here is my .tfvars file:
vnet_spoke_object = {
specialsubnets = {
Subnet_1 = {
name = "test1"
cidr = ["10.0.0.0/28"]
route = "route1"
}
Subnet_2 = {
name = "test2"
cidr = ["10.0.0.16/28"]
route = "route2"
}
Subnet_3 = {
name = "test3"
cidr = ["10.0.0.32/28"]
}
}
}
route_table = {
route1 = {
name = "route1"
disable_bgp_route_propagation = true
route_entries = {
re1 = {
name = "rt-rfc-10-28"
prefix = "10.0.0.0/28"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.0.10"
}
}
}
route2 = {
name = "route2"
disable_bgp_route_propagation = true
route_entries = {
re1 = {
name = "rt-rfc-10-28"
prefix = "10.0.0.16/28"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = "10.0.0.10"
}
}
}
}
...and here is my build script:
provider "azurerm" {
version = "2.18.0"
features{}
}
variable "ARM_LOCATION" {
default = "uksouth"
}
variable "ARM_SUBSCRIPTION_ID" {
default = "asdf-b31e023c78b8"
}
variable "vnet_spoke_object" {}
variable "route_table" {}
module "names" {
source = "./nbs-azure-naming-standard"
env = "dev"
location = var.ARM_LOCATION
subId = var.ARM_SUBSCRIPTION_ID
}
resource "azurerm_resource_group" "test" {
name = "${module.names.standard["resource-group"]}-vnet"
location = var.ARM_LOCATION
}
resource "azurerm_virtual_network" "test" {
name = "${module.names.standard["virtual-network"]}-test"
location = var.ARM_LOCATION
resource_group_name = azurerm_resource_group.test.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "test" {
for_each = var.vnet_spoke_object.specialsubnets
name = "${module.names.standard["subnet"]}-${each.value.name}"
resource_group_name = azurerm_resource_group.test.name
virtual_network_name = azurerm_virtual_network.test.name
address_prefixes = each.value.cidr
}
resource "azurerm_route_table" "test" {
for_each = var.route_table
name = "${module.names.standard["route-table"]}-${each.value.name}"
location = var.ARM_LOCATION
resource_group_name = azurerm_resource_group.test.name
disable_bgp_route_propagation = each.value.disable_bgp_route_propagation
dynamic "route" {
for_each = each.value.route_entries
content {
name = route.value.name
address_prefix = route.value.prefix
next_hop_type = route.value.next_hop_type
next_hop_in_ip_address = contains(keys(route.value), "next_hop_in_ip_address") ? route.value.next_hop_in_ip_address: null
}
}
}
That part works fine in creating the vnet/subnet/route resources, but the problem I face is to dynamically link each subnet to the route table listed in the .tfvars. Not all the subnets will have a route table associated with it, thus it will need to only run IF the key/value route is listed.
resource "azurerm_subnet_route_table_association" "test" {
for_each = {
for key, value in var.vnet_spoke_object.specialsubnets:
key => value
if value.route != null
}
lifecycle {
ignore_changes = [
subnet_id
]
}
subnet_id = azurerm_subnet.test[each.key].id
route_table_id = azurerm_route_table.test[each.key].id
}
The error I face with the above code is:
Error: Unsupported attribute
on main.tf line 65, in resource "azurerm_subnet_route_table_association" "test":
65: if value.route != null
This object does not have an attribute named "route".
I have tried various ways with no success, and I'm at a loss here and would appreciate any guidance posisble.
Based on your scenario, I'm guessing vnet_spoke_object in input looks like this:
vnet_spoke_object = {
specialsubnets = {
subnetA = {
cidr = "..."
}
subnetB = {
cidr = "..."
route = "..."
}
}
}
The problem with that is that a missing route entry doesn't resolve to null, it causes a panic or crash. You'd need to write your input like this (with explicit nulls):
vnet_spoke_object = {
specialsubnets = {
subnetA = {
cidr = "..."
route = null
}
subnetB = {
cidr = "..."
route = "..."
}
}
}
Or lookup route by name and provide a null default in your for map generator expression like this:
for_each = {
for key, value in var.vnet_spoke_object.specialsubnets:
key => value
if lookup(value, "route", null) != null
}

Terraform create multiple network interfaces with `for_each` and reference `azurerm_public_ip`

I have the following code:
resource "azurerm_public_ip" "imaged_pub_ip" {
for_each = var.create_vm_images ? var.vms_to_image : {}
name = "${var.team_name}_${var.release}_imaged_public_ip_${each.value}"
location = var.loc
resource_group_name = "${var.team_name}_${var.release}_${var.owner}_${var.intention}"
allocation_method = "Static"
tags = {
team = var.team_name
environment = var.env_name
useby = var.useby
release = var.release
devops_work_item_id = var.devops_work_item_id
owner = var.owner
intention = var.intention
}
}
resource "azurerm_network_interface" "imaged_net_int" {
for_each = var.create_vm_images ? var.vms_to_image : {}
name = "${var.team_name}_${var.release}_imaged_net_int_${each.value}"
location = var.loc
resource_group_name = "${var.team_name}_${var.release}_${var.owner}_${var.intention}"
ip_configuration {
name = "eth0"
private_ip_address_allocation = "Dynamic"
subnet_id = data.azurerm_subnet.subnet.id
public_ip_address_id = values(azurerm_public_ip.imaged_pub_ip).*.id
}
I am unable to reference the azurerm_public_ip.id in public_ip_address_id. It has an error: Inappropriate value for attribute "public_ip_address_id": string required.
I use a value map to say how many instances are required:
variable "vms_to_image" {
type = map
}
create_vm_images = "true"
vms_to_image = {
vm_id1 = "1"
vm_id2 = "0"
}
How do I reference the azurerm_public_ip.id for public_ip_address_id?
This worked public_ip_address_id = azurerm_public_ip.imaged_pub_ip[each.key].id

Resources