Network_security_group_id with for each function

HI guys
I opening this topic to get your help to help me progress on terraform coding.
I try to create multiple ressources using for each functions but I don’t know how to get network_security_group_id from azure resources create with for each.

## I Creating a network security group for all my hub subnets
resource and I want do the association  
"azurerm_network_security_group" "hub_network_security_group" {
  for_each            = var.hub_network_security_group
  name                = each.value
  location            = azurerm_resource_group.hub_resource_group.location
  resource_group_name = azurerm_resource_group.hub_resource_group.name
}

## Creating a security rule to deny inbound hub traffic 
resource "azurerm_network_security_rule" "deny_internet_inbound" {
  for_each = var.hub_network_security_group
  name                       = "Deny-internet-Outbound"
  priority                   = "4096" #beetween 100 - 4096
  direction                  = "outbound"
  access                     = "Deny"
  protocol                   = "*"
  source_port_range          = "*"
  destination_port_range     = "*"
  source_address_prefix      = "VirtualNetwork"
  destination_address_prefix = "Internet"
  resource_group_name         = azurerm_resource_group.hub_resource_group.name
  network_security_group_name = each.value
}

# Associating the network security group with subnets hosting session hosts

resource "azurerm_subnet_network_security_group_association" "hub_deny_inbound_traffic" {
  for_each                  = azurerm_subnet.hub_subnets
  subnet_id                 = each.value.id
  network_security_group_id =azurerm_network_security_group.hub_network_security_group.id    

\ thing my syntax is not good and I dont know what I need to use to get the network_security_group.hub_network_security_group id at this step.
could you help me please.
I guessing I can use a locals value but I asking before change the way I declare my ressources.

Thx

the error message I received

Error: Incorrect attribute value type

  on security.tf line 31, in resource "azurerm_subnet_network_security_group_association" "hub_deny_inbound_traffic":
  31:   network_security_group_id = azurerm_network_security_group.hub_network_security_group[*].id
    |----------------
    | azurerm_network_security_group.hub_network_security_group is object with 2 attributes

Inappropriate value for attribute "network_security_group_id": string
required.

The for_each meta-argument creates maps of resources.
Also 1 subnet can have only 1 nsg associated with it.
In your error text I see
network_security_group_id = azurerm_network_security_group.hub_network_security_group[*].id which is incorrect.
It should look like :
network_security_group_id = azurerm_network_security_group.hub_network_security_group["TheActualKeyNameGoesHere"].id

So using a lookup against the map will work.

Incase you have generated multiple networksecurity groups using for_each = var.hub_network_security_group , the hub_network_security_group variable should store a mapping between the subnets and the nsgs, this way you can do lookups.

Example :

nsgs = {
    nsg1 = {
      name   = "nsg1",
      subnet = "snet1"
    }
  }
  subnets = {
    snet1 = {
      name             = "snet1"
      nsg              = "nsg1"
      address_prefixes = ["10.0.0.0/26"]
    }
resource "azurerm_subnet" "subnet" {
  for_each             = local.subnets
  name                 = each.value.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  resource_group_name  = azurerm_resource_group.resourcegroup.name
  address_prefixes     = each.value.address_prefixes
}

resource "azurerm_network_security_group" "nsg" {
  for_each            = local.nsgs
  name                = each.value.name
  location            = local.location
  resource_group_name = azurerm_resource_group.resourcegroup.name
}

resource "azurerm_subnet_network_security_group_association" "nsg-assosiation" {
  for_each                  = azurerm_subnet.subnet
  subnet_id                 = each.value.id
  network_security_group_id = azurerm_network_security_group.nsg[lookup(local.subnets, each.value.name, null).nsg].id
}

I have ran a plan and apply against the sample code and it works as intended. good luck!

HI thank for your answer I try but still not working did you try with multiple subnet ?
because with one it’s work for me but with two he won’t work
I change my nsg variables to locals.tf for all net resources create but the nsg association is still an issue for me. I used the way you advice me but for now no solution for me Grrrr

the error message :slight_smile:

Error: Invalid index

This value is null, so it does not have any attributes.


Error: Attempt to get attribute from null value

  on security.tf line 31, in resource "azurerm_subnet_network_security_group_association" "nsg-assosiation":
  31:   network_security_group_id = azurerm_network_security_group.core_net_nsgs[lookup(local.subnets, each.value.name, null).nsg].id
    |--**--------------**
**    | each.value.name is "snet-prd-weu-core-Shared-Infra-01"**
**    | local.subnets is object with 4 attributes**

This value is null, so it does not have any attributes.

what I did wrong ?

sample of the code :

locals {
location = "westeurope"

subnets = {
    snet-prd-weu-core-SharedInfra-01 = {
      name             = "snet-prd-weu-core-Shared-Infra-01"
      nsg              = "nsg-prd-weu-sub-shared-infra-01"
      adress_prefix    = ["10.98.225.0/24"] 

    }
    snet-prd-weu-core-ad-01 = {
      name             = "snet-prd-weu-core-ad-01"
      nsg              = "nsg-prd-weu-core-ad-01"
      adress_prefix    = ["10.98.239.160/27"]
    }

# Creating a network security group for all hub subnet
resource "azurerm_network_security_group" "core_net_nsgs" {
  for_each            = local.subnets
  name                = each.value.nsg
  location            = local.location
  resource_group_name = azurerm_resource_group.hub_resource_group.name
}

## Creating a security rule to deny inbound hub traffic 
resource "azurerm_network_security_rule" "deny_internet_inbound" {
  for_each = azurerm_network_security_group.core_net_nsgs
  name                       = "Deny-internet-Outbound"
  priority                   = "4096" #beetween 100 - 4096
  direction                  = "outbound"
  access                     = "Deny"
  protocol                   = "*"
  source_port_range          = "*"
  destination_port_range     = "*"
  source_address_prefix      = "VirtualNetwork"
  destination_address_prefix = "Internet"
  resource_group_name         = azurerm_resource_group.hub_resource_group.name
  network_security_group_name = each.value.name
}

#Associating the network security group with subnets core vnet

resource "azurerm_subnet_network_security_group_association" "nsg-assosiation" {
  for_each                  = azurerm_subnet.hub_subnets
  subnet_id                 = each.value.id
  network_security_group_id = azurerm_network_security_group.core_net_nsgs[lookup(local.subnets, each.value.name, null).nsg].id
}

Yes it works with multiple subnets and nsgs.
Here’s the locals ( I am using locals instead of vars, but the concept is still the same )

locals {
  location      = "australiaeast"
  resourcegroup = "rg-mssqlvm"
  nsgs = {
    nsg1 = {
      name   = "nsg1",
      subnet = "snet1"
    },
    nsg2 = {
      name   = "nsg2",
      subnet = "snet2"
    }
  }
  subnets = {
    snet1 = {
      name             = "snet1"
      nsg              = "nsg1"
      address_prefixes = ["10.0.0.0/26"]
    },
    snet2 = {
      name             = "snet2"
      nsg              = "nsg2"
      address_prefixes = ["10.0.0.64/26"]
    }

  }
}

the code which does nsg , subnet creation, and nsg-subnet association

resource "azurerm_subnet" "subnet" {
  for_each             = local.subnets
  name                 = each.value.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  resource_group_name  = azurerm_resource_group.resourcegroup.name
  address_prefixes     = each.value.address_prefixes
}

resource "azurerm_network_security_group" "nsg" {
  for_each            = local.nsgs
  name                = each.value.name
  location            = local.location
  resource_group_name = azurerm_resource_group.resourcegroup.name
}

resource "azurerm_subnet_network_security_group_association" "nsg-assosiation" {
  for_each                  = azurerm_subnet.subnet
  subnet_id                 = each.value.id
  network_security_group_id = azurerm_network_security_group.nsg[lookup(local.subnets, each.value.name, null).nsg].id
}

And this is the terraform plan output


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_network_security_group.nsg["nsg1"] will be created
  + resource "azurerm_network_security_group" "nsg" {
      + id                  = (known after apply)
      + location            = "australiaeast"
      + name                = "nsg1"
      + resource_group_name = "rg-mssqlvm"
      + security_rule       = (known after apply)
    }

  # azurerm_network_security_group.nsg["nsg2"] will be created
  + resource "azurerm_network_security_group" "nsg" {
      + id                  = (known after apply)
      + location            = "australiaeast"
      + name                = "nsg2"
      + resource_group_name = "rg-mssqlvm"
      + security_rule       = (known after apply)
    }

  # azurerm_resource_group.resourcegroup will be created
  + resource "azurerm_resource_group" "resourcegroup" {
      + id       = (known after apply)
      + location = "australiaeast"
      + name     = "rg-mssqlvm"
    }

  # azurerm_subnet.subnet["snet1"] will be created
  + resource "azurerm_subnet" "subnet" {
      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.0.0.0/26",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "snet1"
      + resource_group_name                            = "rg-mssqlvm"
      + virtual_network_name                           = "vnet-mssql"
    }

  # azurerm_subnet.subnet["snet2"] will be created
  + resource "azurerm_subnet" "subnet" {
      + address_prefix                                 = (known after apply)
      + address_prefixes                               = [
          + "10.0.0.64/26",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "snet2"
      + resource_group_name                            = "rg-mssqlvm"
      + virtual_network_name                           = "vnet-mssql"
    }

  # azurerm_subnet_network_security_group_association.nsg-assosiation["snet1"] will be created
  + resource "azurerm_subnet_network_security_group_association" "nsg-assosiation" {
      + id                        = (known after apply)
      + network_security_group_id = (known after apply)
      + subnet_id                 = (known after apply)
    }

  # azurerm_subnet_network_security_group_association.nsg-assosiation["snet2"] will be created
  + resource "azurerm_subnet_network_security_group_association" "nsg-assosiation" {
      + id                        = (known after apply)
      + network_security_group_id = (known after apply)
      + subnet_id                 = (known after apply)
    }

  # azurerm_virtual_network.vnet will be created
  + resource "azurerm_virtual_network" "vnet" {
      + address_space         = [
          + "10.0.0.0/24",
        ]
      + guid                  = (known after apply)
      + id                    = (known after apply)
      + location              = "australiaeast"
      + name                  = "vnet-mssql"
      + resource_group_name   = "rg-mssqlvm"
      + subnet                = (known after apply)
      + vm_protection_enabled = false
    }

Plan: 8 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.


indeed one of my keys value in local nsgs does not match one of a key name in my subnets.
stupid mistake.Appreciate it