Terraform关联子网与NSG报错:for_each含待应用时确定的属性
目标
使用模块将环境中的多个子网关联到对应的NSG(NSG和子网已通过单独模块创建)
现有代码结构
根模块
main.tf
resource "azurerm_subnet_network_security_group_association" "root_subnet_nsg_association" { subnet_id = var.subnet_id network_security_group_id = var.nsg_id }
variables.tf
variable "subnet_id"{ type=number description="ID of the subnet which is to be attached to NSG" #default="" } variable "nsg_id"{ type=number description="ID of the NSG which is to be associated with a subnet" #default="" }
项目文件夹中的模块调用
nsg_subnet_association.tf
module "nsg_subnet_asosciation_module"{ source="../../Modules/network/nsg_subnet_association" #Variable names to be passed into the root module: #Use for_each to loop the module: #for_each accepts a set or map but not list as a value for_each = local.nsg_subnet_association subnet_id=each.key nsg_id=each.value }
locals块(传递值到调用模块)
注:可使用括号()在映射中定义动态键
locals{ //Key in subnet name and NSG name for each element of the LIST //Implicit dependence on Subnet and NSG being created before attempt to associate #It is possible to have dynamic keys using parenthesis () as seen on left below nsg_subnet_association={ (module.subnet_module["MGT-Subnet-1"].subnet_id)= module.nsg_module["HUB-NSG"].nsg_id (module.subnet_module["MGT-Subnet-1"].subnet_id) = module.nsg_module["MGT-NSG"].nsg_id (module.subnet_module["SEC-Subnet-1"].subnet_id) = module.nsg_module["SEC-NSG"].nsg_id } }
报错信息
"for_each 映射包含的键来自于只能在执行apply阶段才能确定的资源属性,因此Terraform无法提前确定该资源实例的完整键集合。
在for_each中处理未知值时,建议在配置中静态定义映射的键,仅将apply阶段才能得到的结果放在映射的值中。
或者,你可以使用-target规划选项,先仅应用for_each值依赖的资源,然后再执行第二次apply以完成收敛。"
解决方案
核心问题分析
报错的根源是for_each的键使用了子网ID这类apply阶段才能生成的动态值,Terraform在plan阶段无法提前知晓这些键的具体内容,无法确定要创建多少个关联资源实例。另外原locals里重复使用同一个子网ID作为键,会导致映射被覆盖,逻辑上也存在错误。
具体修复步骤
修改locals块,使用静态键
用子网名称(静态可提前确定的标识)作为映射的键,值里存放子网ID和对应的NSGID,确保plan阶段能明确键的集合,同时修正原代码中子网重复关联NSG的错误:locals { nsg_subnet_association = { "MGT-Subnet-1" = { subnet_id = module.subnet_module["MGT-Subnet-1"].subnet_id nsg_id = module.nsg_module["MGT-NSG"].nsg_id } "SEC-Subnet-1" = { subnet_id = module.subnet_module["SEC-Subnet-1"].subnet_id nsg_id = module.nsg_module["SEC-NSG"].nsg_id } } }调整模块调用的for_each和变量传递
模块调用时,for_each使用上述静态键的映射,从each.value中取出子网ID和NSGID传递给模块:module "nsg_subnet_association_module" { source = "../../Modules/network/nsg_subnet_association" for_each = local.nsg_subnet_association subnet_id = each.value.subnet_id nsg_id = each.value.nsg_id }修正根模块变量类型错误
子网ID和NSGID都是字符串类型,原variables.tf中错误定义为number类型,需要修正:variable "subnet_id" { type = string description = "ID of the subnet which is to be attached to NSG" } variable "nsg_id" { type = string description = "ID of the NSG which is to be associated with a subnet" }
其他可选方案(不推荐)
如果暂时不想调整代码结构,可以分两次执行apply:先创建子网和NSG资源,再创建关联关系:
terraform apply -target=module.subnet_module -target=module.nsg_module terraform apply
该方案需要手动分阶段执行,长期维护性差,优先推荐前面的静态键方案。
内容的提问来源于stack exchange,提问作者Aditya Garg

