I'm hitting a bit of difficulty on Azure trying to create subnets with conditional NSG associations through Terraform.
I have a single vnet with some subnets, some of the subnets should have nsgs attached/assigned, others should not.
Module code looks like this:
(NOTE: here I'm trying to include a reference that an NSG should be associated with a subnet if one has been supplied by the calling module):
Subnet module
subnets.tf
resource "azurerm_subnet" "subnet" {
resource_group_name = var.resource_group_name
virtual_network_name = var.virtual_network_name
name = var.name
address_prefixes = var.address_prefixes
private_endpoint_network_policies = var.private_endpoint_network_policies
dynamic "delegation" {
for_each = var.delegation
content {
name = delegation.value["delegation_name"]
service_delegation {
name = delegation.value["service_delegation_name"]
actions = delegation.value["service_delegation_actions"]
}
}
}
}
# If the subnet has a NSG specified associate it
resource "azurerm_subnet_network_security_group_association" "subnet_nsg_link" {
count = var.network_security_group == null ? 0 : 1
subnet_id = azurerm_subnet.subnet.id
network_security_group_id = var.network_security_group
}
vars for subnets:
variables.tf
variable "name" {
type = string
description = "Name for subnet resource"
}
variable "resource_group_name" {
type = string
description = "Resource group the subnet resource to fall under"
}
variable "virtual_network_name" {
type = string
description = "Name of the virtual network this subnet will be connected with"
}
variable "address_prefixes" {
type = list(string)
description = "List of ip address"
}
variable "private_endpoint_network_policies" {
type = string
description = "Setting for network policy to be allowed for security group"
default = null
}
variable "network_security_group" {
type = string
description = "Network security group id to connect with"
default = null
}
variable "delegation" {
type = list(object({
delegation_name = string,
service_delegation_name = string,
service_delegation_actions = list(string)
}))
description = "(Optional) The delegations for the subnet."
default = []
}
NSG module:
nsg.tf
resource "azurerm_network_security_group" "this" {
name = var.name
location = var.location
resource_group_name = var.resource_group_name
security_rule {
name = "test123"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
vars for nsg:
variables.tf
variable "name" {
type = string
description = "Name for the network security group"
}
variable "location" {
type = string
description = "Azure region that the network security group resource will be created under"
}
variable "resource_group_name" {
type = string
description = "Resource group the network security group will be created under"
}
I'm trying this to call the module:
main.tf
# Resource group
resource "azurerm_resource_group" "network" {
name = "rg-temp"
location = "uksouth"
}
# Default NSG (empty)
module "default" {
source = "./modules/azure-network-security-group"
name = "nsg-default"
location = azurerm_resource_group.network.location
resource_group_name = azurerm_resource_group.network.name
}
# VNETs
# Internal vnet
resource "azurerm_virtual_network" "vnet" {
name = "my-uksouth-int-vn01"
location = azurerm_resource_group.network.location
resource_group_name = azurerm_resource_group.network.name
address_space = "10.10.10.0/24"
}
module "firewall_subnet" {
source = "./modules/azure-subnet"
resource_group_name = azurerm_resource_group.network.name
virtual_network_name = azurerm_virtual_network.vnet
name = "AzureFirewallSubnet"
address_prefixes = "10.10.10.0/27"
}
module "vpn_subnet" {
source = "./modules/azure-subnet"
resource_group_name = azurerm_resource_group.network.name
virtual_network_name = azurerm_virtual_network.vnet
name = "vpnout"
address_prefixes = "10.10.10.32/27"
network_security_group = module.default.id
}
The first two Azure specific subnets should not have any NSG associated with them, but the x2 VPN ones should.
I was hoping the logic of the azurerm_subnet_network_security_group_association
in the subnets module would be "if the subnet 'network_security_group' is null skip this bit otherwise associate the NSG ID"?
But when I run the code I get this error:
╷
│ Error: Invalid count argument
│
│ on modules/azure-subnet/main.tf line 22, in resource "azurerm_subnet_network_security_group_association" "sublink":
│ 22: count = var.network_security_group == null ? 0 : 1
│
│ The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use
│ the -target argument to first apply only the resources that the count depends on.
╵
This doesn't seem to be a syntax issue, but an ordering condition problem? I'm not quite sure how to proceed?
Anyone have any ideas?
Thanks.
The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created.
The message seems clear - you cannot set count
with a dynamic value such as the resource id of another resource that is being managed by Terraform.
Consider adding a new boolean variable to manage the association between the subnet and NSG:
# other variables here
# new variable
variable "associate_network_security_group" {
type = bool
description = "Associate subnet to a NSG?"
default = true
}
# existing variable
variable "network_security_group" {
type = string
description = "Network security group id to connect with"
default = null
}
Using the variable:
module "firewall_subnet" {
source = "./modules/azure-subnet"
resource_group_name = azurerm_resource_group.network.name
virtual_network_name = azurerm_virtual_network.vnet
name = "AzureFirewallSubnet"
address_prefixes = "10.10.10.0/27"
// use new variable here
associate_network_security_group = false
}
module "vpn_subnet" {
source = "./modules/azure-subnet"
resource_group_name = azurerm_resource_group.network.name
virtual_network_name = azurerm_virtual_network.vnet
name = "vpnout"
address_prefixes = "10.10.10.32/27"
network_security_group = module.default.id
// No need to set associate_network_security_group (true be default)
}
Subnet - NSG association:
resource "azurerm_subnet_network_security_group_association" "subnet_nsg_link" {
count = var.associate_network_security_group ? 0 : 1
subnet_id = azurerm_subnet.subnet.id
network_security_group_id = var.network_security_group
}