Terraform如何传入两个for_each输出实现Azure子网与NSG关联
Azure Terraform 子网与NSG关联实现方案
问题场景
部署Azure资源时需要创建VNET、子网、NSG三类资源,已通过for_each元参数实现多子网、多NSG的批量创建,但无法通过azurerm_subnet_network_security_group_association资源完成子网与NSG的关联绑定。当前已将子网ID、NSG ID配置为map类型的模块输出,但未找到建立子网ID与对应NSG ID关联关系的实现方案。
匹配规则要求按资源名称前缀绑定:public_subnet关联public_nsg,private_subnet关联private_nsg,无对应NSG的子网跳过绑定。
现有配置代码
子模块 main.tf
resource "azurerm_virtual_network" "vnet" { name = format("%s-%s-vnet", var.owner_custom, var.purpose_custom) location = var.location resource_group_name = format("rg-%s-%s", var.owner_custom, var.purpose_custom) address_space = var.address_space } resource "azurerm_subnet" "subnet" { for_each = var.subnets name = each.value["name"] address_prefixes = each.value["address_space"] resource_group_name = format("rg-%s-%s", var.owner_custom, var.purpose_custom) virtual_network_name = azurerm_virtual_network.vnet.name } resource "azurerm_network_security_group" "nsg" { for_each = var.nsg name = each.value["name"] location = var.location resource_group_name = format("rg-%s-%s", var.owner_custom, var.purpose_custom) } resource "azurerm_subnet_network_security_group_association" "nsg_association" { subnet_id = # 待补充 network_security_group_id = # 待补充 }
子模块 variables.tf
variable "owner_custom" { description = "Short name of owner" } variable "purpose_custom" { description = "Custom purpose" } variable "location" { description = "Location where resource is to be created" } variable "address_space" { type = list description = "VNET CIDR Range" } variable "subnets" { description = "A map to create multiple subnets" type = map(object({ name = string address_space = list(string) })) } variable "nsg" { description = "A map of NSGs" type = map(object({ name = string })) }
子模块 output.tf
output "vnet_id" { value = azurerm_virtual_network.vnet.id } output "subnet_id" { value = tomap({ for k, s in azurerm_subnet.subnet : k => s.id }) } output "nsg_id" { value = tomap({ for k,s in azurerm_network_security_group.nsg: k => s.id }) }
tfvars 变量赋值文件
#Referenced common across modules owner_custom = "raghav" purpose_custom = "demo" #Referenced in resource-group module owner = "test@test.com" purpose = "test" location = "australiaeast" org = "org" #Referenced in network module address_space = ["10.10.0.0/21"] subnets = { subnet1 = { name = "public_subnet" address_space = ["10.10.1.0/26"] } subnet2 = { name = "private_subnet" address_space = ["10.10.1.64/26"] } subnet3 = { name = "privatelink_subnet" address_space = ["10.10.1.128/26"] } subnet4 = { name = "AzureFirewallSubnet" address_space = ["10.10.1.192/26"] } } nsg = { public_nsg = { name = "public_nsg" } private_nsg = { name = "private_nsg" } }
实现代码
直接在子模块main.tf中添加本地值映射,替换原有空的关联资源块即可,不需要调整现有变量、输出和tfvars结构:
# 构造按NSG资源名称索引的映射表,方便名称匹配 locals { nsg_by_name = { for nsg_item in azurerm_network_security_group.nsg : nsg_item.name => nsg_item } # 筛选需要绑定NSG的子网:名称符合xxx_subnet格式、且存在对应xxx_nsg的才纳入关联范围 subnet_nsg_bind_map = { for subnet_item in azurerm_subnet.subnet : subnet_item.name => subnet_item if can(regex("^(.*)_subnet$", subnet_item.name)) && contains(keys(local.nsg_by_name), "${regex("^(.*)_subnet$", subnet_item.name)[1]}_nsg") } } resource "azurerm_subnet_network_security_group_association" "nsg_association" { for_each = local.subnet_nsg_bind_map subnet_id = each.value.id network_security_group_id = local.nsg_by_name["${regex("^(.*)_subnet$", each.value.name)[1]}_nsg"].id }
逻辑说明
- 按NSG实际资源名构造索引,不依赖tfvars中map的key值,适配当前tfvars中subnet key为subnet1/subnet2、NSG key为public_nsg/private_nsg的结构
- 自动过滤不需要绑定NSG的子网:名称不符合
xxx_subnet格式、或没有对应前缀NSG的子网(比如示例中的privatelink_subnet、AzureFirewallSubnet)会被直接跳过,不会触发资源不存在的报错 - 如果后续调整命名规则,只需要修改正则匹配和名称拼接逻辑即可,不需要改动其他资源代码
内容的提问来源于stack exchange,提问作者rb16
相关产品推荐
相关产品推荐

