Search code examples
terraformterraform-provider-azureterraform0.12+azure-rm

How to create Network Security Group for all subnets in loop with Terraform on Azure


Objective: Trying to create network security group for all subnets in vnet in Azure via Terraform with loop

Code that I am using:

Main.tf:

resource "azurerm_network_security_group" "nsg" {
  for_each            = var.subnets
  name                = lower("nsg_${each.key}_in")
  resource_group_name = var.resource_group_name
  location            = var.location
  tags                = merge({ "ResourceName" = lower("nsg_${each.key}_in") }, var.tags, )
  dynamic "security_rule" {
    for_each = concat(lookup(each.value, "nsg_inbound_rules", []), lookup(each.value, "nsg_outbound_rules", []))
    content {
      name                       = security_rule.value[0] == "" ? "Default_Rule" : security_rule.value[0]
      priority                   = security_rule.value[1]
      direction                  = security_rule.value[2] == "" ? "Inbound" : security_rule.value[2]
      access                     = security_rule.value[3] == "" ? "Allow" : security_rule.value[3]
      protocol                   = security_rule.value[4] == "" ? "Tcp" : security_rule.value[4]
      source_port_range          = "*"
      destination_port_range     = security_rule.value[5] == "" ? "*" : security_rule.value[5]
      source_address_prefix      = security_rule.value[6] == "" ? element(each.value.subnet_address_prefix, 0) : security_rule.value[6]
      destination_address_prefix = security_rule.value[7] == "" ? element(each.value.subnet_address_prefix, 0) : security_rule.value[7]
      description                = "${security_rule.value[2]}_Port_${security_rule.value[5]}"
    }
  }
}
resource "azurerm_subnet_network_security_group_association" "nsg-assoc" {
  for_each                  = var.subnets
  subnet_id                 = azurerm_subnet.snet[each.key].id
  network_security_group_id = azurerm_network_security_group.nsg[each.key].id
}

variables.tf:

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

  default = {
    "s1" = { name = "dns_snet"},
    "s2" = { name = "common_snet"},
    "s3" = { name = "gw_snet"},
    "s4" = { name = "clientdata_snet"}
}
}

Error I am getting

Error: Unsupported attribute
│ 
│   on main.tf line 167, in resource "azurerm_subnet" "snet":
│  167:   name                 = lower(format("snet-%s-${var.hub_vnet_name}-${var.location}", each.value.subnet_name))
│     ├────────────────
│     │ each.value is object with 1 attribute "name"
│ 
│ This object does not have an attribute named "subnet_name"

Please suggest where I am doing mistake ? Thanks in advance.


Solution

  • Reproduce in my environment, Its working fine. Below is the code you can make use of it and do changes in names of variables as per your requirement.

    main.tf

    provider "azurerm" {
      features{}
    }
    
    data "azurerm_resource_group" "example" {
      name = "v-rasXXXXree"
    }
    
    resource "azurerm_virtual_network" "example" {
      name                = "example-network"
      location            = data.azurerm_resource_group.example.location
      resource_group_name = data.azurerm_resource_group.example.name
      address_space       = ["10.0.0.0/16"]
    
      tags = {
        environment = "Production"
      }
    }
    
    resource "azurerm_subnet" "snet" {
      for_each = var.subnets
      resource_group_name = data.azurerm_resource_group.example.name
      virtual_network_name = azurerm_virtual_network.example.name
      name = each.value["name"]
      address_prefixes = each.value["address_prefixes"]
    }
    
    
    
    resource "azurerm_network_security_group" "nsg" {
      for_each            = var.subnets
      name                = lower("nsg_${each.key}_in")
      resource_group_name = data.azurerm_resource_group.example.name
      location            = data.azurerm_resource_group.example.location
      #tags                = merge({ "ResourceName" = lower("nsg_${each.key}_in") }, var.tags, )
      dynamic "security_rule" {
        for_each = concat(lookup(each.value, "nsg_inbound_rules", []), lookup(each.value, "nsg_outbound_rules", []))
        content {
          name                       = security_rule.value[0] == "" ? "Default_Rule" : security_rule.value[0]
          priority                   = security_rule.value[1]
          direction                  = security_rule.value[2] == "" ? "Inbound" : security_rule.value[2]
          access                     = security_rule.value[3] == "" ? "Allow" : security_rule.value[3]
          protocol                   = security_rule.value[4] == "" ? "Tcp" : security_rule.value[4]
          source_port_range          = "*"
          destination_port_range     = security_rule.value[5] == "" ? "*" : security_rule.value[5]
          source_address_prefix      = security_rule.value[6] == "" ? element(each.value.subnet_address_prefix, 0) : security_rule.value[6]
          destination_address_prefix = security_rule.value[7] == "" ? element(each.value.subnet_address_prefix, 0) : security_rule.value[7]
          description                = "${security_rule.value[2]}_Port_${security_rule.value[5]}"
        }
      }
    }
    resource "azurerm_subnet_network_security_group_association" "nsg-assoc" {
      for_each                  = var.subnets
      subnet_id                 = azurerm_subnet.snet[each.key].id
      network_security_group_id = azurerm_network_security_group.nsg[each.key].id
    }
    

    variable.tf

    variable "subnets" {
      type=map(any)
      default = {
        subnet_1 = {
            name="subnet_1"
            address_prefixes=["10.0.1.0/24"]
        }
        subnet_2={
            name="subnet-2"
            address_prefixes=["10.0.2.0/24"]
        }
        subnet_3={
            name="subnet_3"
            address_prefixes=["10.0.3.0/24"]
        }
      }
    }
    

    Ouput

    enter image description here

    enter image description here