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
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
Hi, I’m looking at expanding this excellent code to include vms with a name, size and subnet.
Here is what I have in locals:
vnet = {
vnet = {
name = "vnet"
address_space = ["10.0.0.0/16"]
}
}
nsgs = {
nsg01 = {
name = "nsg01"
subnet = "snet01"
},
nsg02 = {
name = "nsg02"
subnet = "snet02"
}
}
subnets = {
snet01 = {
name = "snet01"
nsg = "nsg01"
address_prefixes = ["10.0.1.0/24"]
},
snet02 = {
name = "snet02"
nsg = "nsg02"
address_prefixes = ["10.0.2.0/24"]
}
}
vms = {
vm01 = {
name = "vm01",
size = "Standard_DS1_v2"
subnet = "snet01"
}
vm02 = {
name = "vm02"
size = "Standard_D2s_v3"
subnet = "snet02"
}
}
My subnet and network interface resource:
resource "azurerm_subnet" "subnet" {
depends_on = [azurerm_virtual_network.vnet]
for_each = local.subnets
name = each.value.name
virtual_network_name = local.vnet.vnet.name
resource_group_name = local.resourcegroup
address_prefixes = each.value.address_prefixes
}
resource "azurerm_network_interface" "nic" {
depends_on = [azurerm_resource_group.rg]
for_each = local.vms
name = "${each.key}-nic"
location = local.location
resource_group_name = local.resourcegroup
enable_ip_forwarding = "false"
enable_accelerated_networking = "false"
ip_configuration {
name = "ipconfig"
subnet_id = azurerm_subnet.subnet[lookup(local.subnets, each.value.name, null).subnet].id
private_ip_address_allocation = "Dynamic"
primary = "true"
}
}
However I’m seeing:
│ Error: Attempt to get attribute from null value
│
│ on main.tf line 50, in resource "azurerm_network_interface" "nic":
│ 50: subnet_id = azurerm_subnet.subnet[lookup(local.subnets, each.value.name, null).subnet].id
│ ├────────────────
│ │ each.value.name is "vm02"
│ │ local.subnets is object with 2 attributes
│
│ This value is null, so it does not have any attributes.
╵
╷
│ Error: Attempt to get attribute from null value
│
│ on main.tf line 50, in resource "azurerm_network_interface" "nic":
│ 50: subnet_id = azurerm_subnet.subnet[lookup(local.subnets, each.value.name, null).subnet].id
│ ├────────────────
│ │ each.value.name is "vm01"
│ │ local.subnets is object with 2 attributes
│
│ This value is null, so it does not have any attributes.
What would be the correct approach to lookup the subnet_id under ip_configuration?
Hi @mgibson85,
I think the problem here is that the logic to treat a missing element as null
is too “early” and so later operations are failing by trying to work with the null value.
It seems like what you need here is to set subnet_id
to null if local.subnets
doesn’t contain an entry for each.value.name
. If so, here’s a different way to write that:
subnet_id = can(local.subnets[each.value.name]) ? azurerm_subnet.subnet[local.subnets[each.value.name]].id : null
Specifically the idea here is to deal with the null
case in the outermost expression, so that the rest will not be evaluated at all if the precondition isn’t met, and thus you can avoid problems related to trying to perform operations on a null value.
Thank you, I didn’t consider the can function.
Terraform plan executes successfully however terraform apply gives the error:
╷
│ Error: expanding `ip_configuration`: A Subnet ID must be specified for an IPv4 Network Interface.
│
│ with azurerm_network_interface.nic["vm02"],
│ on main.tf line 39, in resource "azurerm_network_interface" "nic":
│ 39: resource "azurerm_network_interface" "nic" {
│
╵
╷
│ Error: expanding `ip_configuration`: A Subnet ID must be specified for an IPv4 Network Interface.
│
│ with azurerm_network_interface.nic["vm01"],
│ on main.tf line 39, in resource "azurerm_network_interface" "nic":
│ 39: resource "azurerm_network_interface" "nic" {
What are your thoughts?
These new errors seem to be comign from the Azure provider itself, rather than from Terraform, and so unfortunately this is now outside of my scope of direct knowledge.
It seems like the provider is telling you that subnet_id
is actually a required argument and so it isn’t valid to set it to null
. If that’s true, I guess you’ll need to find some other suitable fallback value to use instead of null
so that the configuration can be valid even if there isn’t an entry in local.subnets
, or to make sure that your local.subnets
value is comprehensive for all possible keys and therefore the can
fallback to null
wouldn’t be necessary anymore.
Many thanks for the support and explanation.
I’ll look into another way of achieving the desired outcome.
Hi,
Error: Unsupported attribute
│
│ on main.tf line 38, in resource “azurerm_network_interface_security_group_association” “web_vmnic_nsg_associate”:
│ 38: network_security_group_id = azurerm_network_security_group.prod_subnet_nsg[lookup(var.nsg_names, each.value, null).prod_subnet_nsg].id
│ ├────────────────
│ │ each.value will be known only after apply
│ │ var.nsg_names is a map of string, known only after apply
│
│ Can’t access attributes on a primitive-typed value (string).
Resource-1: Create a single Network Security Group (NSG)
resource “azurerm_network_security_group” “prod_subnet_nsg” {
for_each = var.nsg_names
name = each.value
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
}
Resource-1: Create Network Interface
resource “azurerm_network_interface” “web_windows_vm_nic” {
for_each = var.nic_names
name = each.value
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
ip_configuration {
name = “web-windowsvm-ip-1”
#subnet_id = “{var.production_subnet_id}"
private_ip_address_allocation = "Dynamic"
#public_ip_address_id = "{var.public_ip_address_id}” // if you want you can disallocate
}
}
Resource-2: Associate NSG and NIC
resource “azurerm_network_interface_security_group_association” “web_vmnic_nsg_associate” {
for_each = azurerm_network_interface.web_windows_vm_nic
network_interface_id = each.value.id
network_security_group_id = azurerm_network_security_group.prod_subnet_nsg[lookup(var.nsg_names, each.value, null).prod_subnet_nsg].id
}
Please help me to fix this issue.