Multiple subnet_ids / output / compute module

I have created multiple subnet ids using map(string) in vnet module. Also, exported the output of subnet_ids in output.tf file.

How I input the subnet-ids into network interface of compute module?

The code is below.

vent.tf

resource "azurerm_virtual_network" "ex-re-2" {
  name  = var.vnet_name
  location = var.location
  resource_group_name = var.rg_name
  address_space = [var.address_space]
  }

  resource "azurerm_subnet" "ex-re-3" {
    for_each = var.subnets
    resource_group_name = var.rg_name
    virtual_network_name = azurerm_virtual_network.ex-re-2.name
    name = each.key
    address_prefixes = [each.value]
  }

variables.tf

variable "rg_name" {}

variable "location" {}

variable "vnet_name" {}

variable "address_space" {}

variable "subnets" {
    type = map(string)
}

output.tf

output "network_name" {
  value = azurerm_virtual_network.ex-re-2
  description = "Name of the virtual network"
}

output "subnet_ids" {
  value = azurerm_subnet.ex-re-3["*"].id
}

Hi @hajee_78,

I think you are asking about how to make your subnet_ids output value be a map from subnet key (the same keys as var.subnets) to subnet ID.

The following syntax would describe that:

output "subnet_ids" {
  value = tomap({
    for name, subnet in azurerm_subnet.ex-re-3 : name => subnet.id
  })
}

Thanks @apparentlymart for your help. I did output subnet_ids as you suggested and try to input the same into compute module under resource NIC… But end up with some error. Not sure how to proceed. I am very new to terraform and your help is highly appreciated… I will paste the terraform code later…

Hi @hajee_78,

Indeed it would be useful to share the code you tried, and also please share the full error that Terraform produced, exactly as Terraform presented it, so we can see what exactly went wrong.

Thanks!

Hi @apparentlymart thanks for asking and your help. Below is code I am trying to achieve.

main.tf

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "3.30.0"
    }
  }
}

provider "azurerm" {
 features {
   
 }
}

module "resourcegroup" {
  source = "./modules/resourcegroup"
  rg_name = var.rg_name
  location = var.location
}

module "networking" {
  source = "./modules/networking"
  rg_name = module.resourcegroup.resource_group_name
  location = module.resourcegroup.location_id
  vnet_name = var.vnet_name
  address_space = var.address_space
  subnets = var.subnets
  network_security_group_names = var.network_security_group_names
  network_sucurity_group_rules = var.network_sucurity_group_rules
}

module "compute" {
  source = "./modules/compute"
  rg_name = module.resourcegroup.resource_group_name
  location = module.resourcegroup.location_id
  network_interface_name = tomap({
    for k, subnet_id in module.networking.subnet_ids : k => {
    name = k
    subnet_id = subnet_id }
  })
}

variables.tf

variable "rg_name" {}
variable "location" {}
variable "vnet_name" {}
variable "address_space" {}
variable "subnets" {
    type = map(string)
}
variable "network_interface_name" {
  type = map (
    object ({
      name = string
      subnet_id = string
    })
  )
}
variable "network_sucurity_group_rules" {
  type=list
}

variable "network_security_group_names" {
  type=map(string)
}

terraform.tfvars

rg_name = "Az3Tier"
location = "SouthEast Asia"
vnet_name = "Az3tier-vnet"
address_space = "10.0.0.0/16"
subnets = {
    "web-subnet" = "10.0.1.0/24"
    "app-subnet" = "10.0.2.0/24"
    "db-subnet" = "10.0.3.0/24"
    }
network_security_group_names = {
    "web-nsg" = "web-subnet"
    "app-nsg" = "app-subnet"
    "db-nsg"  = "db-subnet"
}

network_sucurity_group_rules = [
    {
    id=1,
    priority = "200",
    direction = "Inbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "3389"
    source_address_prefix = "*"
    destination_address_prefix = "*"
    network_security_group_names = "web-nsg"
},
 {
    id=2,
    priority = "300",
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "80"
    source_address_prefix = "*"
    destination_address_prefix = "*"
    network_security_group_names = "web-nsg"
},
{
    id=3,
    priority = "200",
    direction = "Inbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "3389"
    source_address_prefix = "10.0.1.0/24"
    destination_address_prefix = "10.0.2.0/24"
    network_security_group_names = "app-nsg"
},
{
    id=4,
    priority = "200",
    direction = "Inbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "1433"
    source_address_prefix = "10.0.2.0/24"
    destination_address_prefix = "10.0.3.0/24"
    network_security_group_names = "db-nsg"
},
{
    id=5,
    priority = "4094",
    direction = "Inbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
    network_security_group_names = "db-nsg"
},
{
    id=6,
    priority = "4096",
    direction = "Outbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
    network_security_group_names = "db-nsg"
}
]

network_interface_name = [ {
    name = "web-nic" 
    subnet_id = "web-subnet"
},
{
    name = "app-nic" 
    subnet_id = "app-subnet"
},
{
    name = "db-nic" 
    subnet_id = "db-subnet"
}
 ]

module / resourcegroup

rg.tf

resource "azurerm_resource_group" "ex-re-1" {
  name = var.rg_name
  location = var.location
}

variables.tf

variable "rg_name" {}

variable "location" {}

output.tf

output "resource_group_name" {
  value = azurerm_resource_group.ex-re-1.name
  description = "Name of the resource group"
}

output "location_id" {
    value = azurerm_resource_group.ex-re-1.location
    description = "Location id of the resource group"
}

modules / networking

vnet.tf

resource "azurerm_virtual_network" "ex-re-2" {
  name  = var.vnet_name
  location = var.location
  resource_group_name = var.rg_name
  address_space = [var.address_space]
  }

  resource "azurerm_subnet" "ex-re-3" {
    for_each = var.subnets
    resource_group_name = var.rg_name
    virtual_network_name = azurerm_virtual_network.ex-re-2.name
    name = each.key
    address_prefixes = [each.value]
  }

  resource "azurerm_network_security_group" "ex-re-4" {
  for_each = var.network_security_group_names
  name                = each.key
  location            = var.location
  resource_group_name = var.rg_name
  }

  resource "azurerm_subnet_network_security_group_association" "ex-re-5" {
    for_each = var.network_security_group_names
    subnet_id = azurerm_subnet.ex-re-3[each.value].id
    network_security_group_id = azurerm_network_security_group.ex-re-4[each.key].id
  }

  resource "azurerm_network_security_rule" "ex-re-6" {
  for_each = {for rule in var.network_sucurity_group_rules:rule.id => rule}
  name                        = "${each.value.access}-${each.value.destination_port_range}"
  priority                    = each.value.priority
  direction                   = each.value.direction
  access                      = each.value.access
  protocol                    = each.value.protocol
  source_port_range           = each.value.source_port_range
  destination_port_range      = each.value.destination_port_range
  source_address_prefix       = each.value.source_address_prefix
  destination_address_prefix  = each.value.destination_address_prefix
  resource_group_name         = var.rg_name
  network_security_group_name = azurerm_network_security_group.ex-re-4[each.value.network_security_group_names].name
}

variables.tf

variable "rg_name" {}

variable "location" {}

variable "vnet_name" {}

variable "address_space" {}

variable "subnets" {
    type = map(string)
}

variable "network_security_group_names" {
  type=map(string)
}

variable "network_sucurity_group_rules" {
  type=list
}

output.tf

output "network_name" {
  value = azurerm_virtual_network.ex-re-2
  description = "Name of the virtual network"
}

output "subnet_ids" {
 value = tomap ({for k, s in azurerm_subnet.ex-re-3:k => s.id})
}

modules/computing

main.tf

resource "azurerm_network_interface" "ex-re-7" {
    for_each = var.network_interface_name
    name = each.value.name
    resource_group_name = var.rg_name
    location = var.location

    ip_configuration{
        name = "Internal"
        subnet_id = each.value.subnet_id
        private_ip_address_allocation = "Dynamic"
    }
   }
    

variables.tf

variable "rg_name" {}

variable "location" {}

variable "network_interface_name" {
  type = map (
    object ({
      name = string
      subnet_id = string
    })
  )
}

Sorry for the long coding since I split the resources into different groups as modules.

And the output error is…

PS C:\Terraform\5-11-2022> terraform init
Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Using previously-installed hashicorp/azurerm v3.30.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
PS C:\Terraform\5-11-2022> terraform validate
Success! The configuration is valid.

PS C:\Terraform\5-11-2022> terraform plan -out main.tfplan
module.resourcegroup.azurerm_resource_group.ex-re-1: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier]
module.networking.azurerm_network_security_group.ex-re-4["app-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/app-nsg]
module.networking.azurerm_virtual_network.ex-re-2: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet]
module.networking.azurerm_network_security_group.ex-re-4["web-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/web-nsg]
module.networking.azurerm_network_security_group.ex-re-4["db-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/db-nsg]
module.networking.azurerm_subnet.ex-re-3["web-subnet"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/web-subnet]
module.networking.azurerm_subnet.ex-re-3["app-subnet"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/app-subnet]
module.networking.azurerm_subnet.ex-re-3["db-subnet"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/db-subnet]
module.networking.azurerm_network_security_rule.ex-re-6["1"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/web-nsg/securityRules/Allow-3389]
module.networking.azurerm_network_security_rule.ex-re-6["2"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/web-nsg/securityRules/Allow-80]
module.networking.azurerm_network_security_rule.ex-re-6["6"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/db-nsg/securityRules/Deny-*]
module.networking.azurerm_network_security_rule.ex-re-6["5"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/db-nsg/securityRules/Deny-*]
module.networking.azurerm_network_security_rule.ex-re-6["4"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/db-nsg/securityRules/Allow-1433]
module.networking.azurerm_network_security_rule.ex-re-6["3"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/networkSecurityGroups/app-nsg/securityRules/Allow-3389]
module.networking.azurerm_subnet_network_security_group_association.ex-re-5["web-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/web-subnet]
module.networking.azurerm_subnet_network_security_group_association.ex-re-5["db-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/db-subnet]
module.networking.azurerm_subnet_network_security_group_association.ex-re-5["app-nsg"]: Refreshing state... [id=/subscriptions/8e4dbcff-3b28-4654-837f-458c790f4e5f/resourceGroups/Az3Tier/providers/Microsoft.Network/virtualNetworks/Az3tier-vnet/subnets/app-subnet]
╷
│ Error: Invalid value for input variable
│
│   on terraform.tfvars line 91:
│   91: network_interface_name = [ {
│   92:     name = "web-nic" 
│   93:     subnet_id = "web-subnet"
│   94: },
│   95: {
│   96:     name = "app-nic"
│   97:     subnet_id = "app-subnet"
│   98: },
│   99: {
│  100:     name = "db-nic"
│  101:     subnet_id = "db-subnet"
│  102: }
│  103:  ]
│
│ The given value is not valid for variable "network_interface_name": map of object required.
╵
PS C:\Terraform\5-11-2022> 

I understand that the map of object is required as input. But not sure how I bring into map of object terraform.tfvars. Sorry for the very long code and your help is highly appreciated.

Regards,
Hajee_78

Hi @hajee_78,

Your network_interface_name value is given as a list of objects rather than a map of objects, and so Terraform returns a type error.

To write a map instead of a list you’ll need to decide a unique key to use for each element. If you intend to use the name values as the keys then you could write the variable value like this:

network_interface_name = {
  web-nic = {
    subnet_id = "web-subnet"
  }
  app-nic = {
    subnet_id = "app-subnet"
  }
  db-nic = {
    subnet_id = "db-subnet"
  }
]

With the map keys representing the names you don’t need the name attribute in the objects anymore:

variable "network_interface_name" {
  type = map(
    object({
      subnet_id = string
    })
  )
}

When you use for_each = var.network_interface_name in another block you will get the name from each.key instead of each.value.name.

Hi @apparentlymart thanks very much for your help. The code is worked like a charm after modifying as per your suggestion. Great and Thanks!

Hi @apparentlymart sorry to bother you again. I got an plan output for the above code creating three NIC’s but the name remains as web, app and db-subnet (refer the below plan output).

module.compute.azurerm_network_interface.ex-re-7[“app-subnet”] will be created

  • resource “azurerm_network_interface” “ex-re-7” {
    • applied_dns_servers = (known after apply)
    • dns_servers = (known after apply)
    • enable_accelerated_networking = false
    • enable_ip_forwarding = false
    • id = (known after apply)
    • internal_dns_name_label = (known after apply)
    • internal_domain_name_suffix = (known after apply)
    • location = “southeastasia”
    • mac_address = (known after apply)
    • name = “app-subnet”
    • private_ip_address = (known after apply)
    • private_ip_addresses = (known after apply)
    • resource_group_name = “Az3Tier”
    • virtual_machine_id = (known after apply)

But I would like to map web-nic to web-subnet and module compute has the following tomap functions to create NIC’s.
module “compute” {
source = “./modules/compute”
rg_name = module.resourcegroup.resource_group_name
location = module.resourcegroup.location_id
network_interface_ids = tomap({
for k, subnet_id in module.networking.subnet_ids : k => {
subnet_id = subnet_id }

})
variables.tf of computer module
variable “network_interface_ids” {
type = map(
object({
subnet_id = string
})
)
}

terraform.tfvars

network_interface_ids = {
“web-nic” = {
subnet_id = “web-subnet”
}
“app-nic” = {
subnet_id = “app-subnet”
}
“db-nic” = {
subnet_id = “db-subnet”
}
}

Could you please assist here how I can map the each subnet to each NIC’s? Appreciate your kind support on this.

Regards,
hajee_78