如何让Terraform部署Azure委托子网具备可重入性?
解决Azure策略下委托子网与NSG关联的Terraform部署问题
问题背景
我的Azure环境有一条强制策略:禁止创建未关联网络安全组(NSG)的子网。现在需要为PostgreSQL灵活服务器创建委托子网,且该子网必须与虚拟网络(VNet)分开创建。
最初的Terraform配置(不可行)
最初尝试通过独立的子网资源和NSG关联资源实现,但会触发策略违规:
resource "azurerm_virtual_network" "vnet_hub" { name = "HubVnet" location = var.location resource_group_name = var.resource_group_name address_space = ["10.0.0.0/21"] } resource "azurerm_subnet" "db" { name = "db-subnet" resource_group_name = var.resource_group_name virtual_network_name = azurerm_virtual_network.vnet_hub.name address_prefixes = ["10.0.3.0/24"] delegation { name = "delegation" service_delegation { name = "Microsoft.DBforPostgreSQL/flexibleServers" actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] } } } resource "azurerm_network_security_group" "db" { name = "db-nsg" 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 = "*" } } resource "azurerm_subnet_network_security_group_association" "db" { subnet_id = azurerm_subnet.db.id network_security_group_id = azurerm_network_security_group.db.id }
问题原因:Terraform会先创建子网,再执行NSG关联操作,导致子网存在短暂的无NSG状态,违反Azure策略。
当前脚本方案的问题
通过null_resource调用Azure CLI脚本创建子网,首次执行正常,但二次执行terraform apply时,Terraform会认为该子网不在其管理范围内,可能将其删除,导致PostgreSQL服务器失去可用子网。
解决方案
方案一:Terraform原生配置(无需shell脚本)
核心是利用azurerm_subnet资源的network_security_group_id参数,在创建子网时直接绑定NSG,避免中间无NSG的状态,完全符合策略要求:
resource "azurerm_virtual_network" "vnet_hub" { name = "HubVnet" location = var.location resource_group_name = var.resource_group_name address_space = ["10.0.0.0/21"] } resource "azurerm_network_security_group" "db" { name = "db-nsg" 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 = "*" } } resource "azurerm_subnet" "db" { name = "db-subnet" resource_group_name = var.resource_group_name virtual_network_name = azurerm_virtual_network.vnet_hub.name address_prefixes = ["10.0.3.0/24"] # 直接在子网创建时关联NSG,避免策略违规 network_security_group_id = azurerm_network_security_group.db.id delegation { name = "delegation" service_delegation { name = "Microsoft.DBforPostgreSQL/flexibleServers" actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] } } service_endpoints = ["Microsoft.Storage"] }
优势:完全通过Terraform原生资源管理,多次执行terraform apply不会删除子网,且符合Azure策略要求,无需额外脚本。
方案二:修复shell脚本方案(保留脚本)
如果必须保留shell脚本的方式,需要将已创建的子网导入Terraform状态,让Terraform识别并管理该资源:
- 首次执行部署:运行
terraform apply创建VNet、NSG,并通过脚本生成子网。 - 导入子网到Terraform状态:
terraform import azurerm_subnet.db /subscriptions/<你的订阅ID>/resourceGroups/<资源组名>/providers/Microsoft.Network/virtualNetworks/HubVnet/subnets/db-subnet - 添加子网的原生Terraform配置:在你的配置文件中添加和方案一相同的
azurerm_subnet资源定义(包含delegation、NSG关联等)。 - 验证并更新:执行
terraform plan确认无变更,之后再执行terraform apply就不会删除子网。
另外,也可以为null_resource添加triggers,让脚本仅在配置变更时执行:
resource "null_resource" "database_subnet" { depends_on = [azurerm_virtual_network.vnet_hub] # 仅当以下配置变更时,才重新执行脚本 triggers = { subnet_name = "db-subnet" address_prefix = "10.0.3.0/24" network_security_group_id = azurerm_network_security_group.db.id } provisioner "local-exec" { command = format("%s/%s", path.module, "add_database_subnet.sh") interpreter = ["sh"] environment = { RESOURCE_GROUP_NAME = var.resource_group_name SUBNET_NAME = "db-subnet" VNET_NAME = azurerm_virtual_network.vnet_hub.name ADDRESS_PREFIX = "10.0.3.0/24" NETWORK_SECURITY_GROUP_ID = azurerm_network_security_group.db.id } } }
内容的提问来源于stack exchange,提问作者Laurent Michel
相关产品推荐
相关产品推荐

