Terraform部署Azure VM并配置策略豁免时遇count值预估值错误求助
问题场景
为Azure VM配置策略豁免的Terraform模块,在针对现有VM运行时正常,但部署新VM并同时创建豁免时,触发以下错误:
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.
模块通过local.exemption_scope解析var.scope的值(新VM的ID),自动判断豁免作用范围(管理组/订阅/资源组/资源),并以此控制对应azurerm_*_policy_exemption资源的count值。即使在模块调用时添加了depends_on依赖VM创建模块,错误依然存在。
错误原因
Terraform在plan阶段需要确定所有资源的count值,但新VM的virtual_machine_id是在apply阶段才生成的动态属性。local.exemption_scope中的逻辑依赖这个动态值来计算count,导致plan阶段无法确定最终要创建的豁免资源实例数量,从而触发错误。
depends_on仅控制资源的执行顺序,无法解决plan阶段的动态依赖问题——它不能让Terraform提前知道apply阶段才会生成的VM ID。
解决方法
核心思路是:避免在plan阶段依赖动态生成的资源属性来计算count,改为显式指定豁免的作用范围类型,让Terraform在plan阶段就能确定count值。
步骤1:修改策略豁免模块,新增exemption_type变量
在模块中添加一个输入变量,让调用方明确指定豁免的作用范围类型:
variable "exemption_type" { type = string description = "指定策略豁免的作用范围类型,可选值:mg(管理组)、sub(订阅)、rg(资源组)、resource(资源)" validation { condition = contains(["mg", "sub", "rg", "resource"], var.exemption_type) error_message = "有效值为 mg、sub、rg、resource。" } }
步骤2:更新local.exemption_scope逻辑
将原本通过解析var.scope判断范围的逻辑,改为基于exemption_type变量直接判断:
locals { exemption_scope = { mg = var.exemption_type == "mg" ? 1 : 0, sub = var.exemption_type == "sub" ? 1 : 0, rg = var.exemption_type == "rg" ? 1 : 0, resource = var.exemption_type == "resource" ? 1 : 0, } // 其余local配置保持不变 expires_on = var.expires_on != null ? "${var.expires_on}T23:00:00Z" : null metadata = var.metadata != null ? jsonencode(var.metadata) : null policy_definition_reference_ids = length(var.member_definition_names) > 0 ? [for name in var.member_definition_names : replace(substr(title(replace(name, "/-|_|\\s/", " ")), 0, 64), "/\\s/", "") ] : var.policy_definition_reference_ids exemption_id = try( azurerm_management_group_policy_exemption.management_group_exemption[0].id, azurerm_subscription_policy_exemption.subscription_exemption[0].id, azurerm_resource_group_policy_exemption.resource_group_exemption[0].id, azurerm_resource_policy_exemption.resource_exemption[0].id, "") }
步骤3:调用模块时显式指定exemption_type
在调用豁免模块时,明确传入exemption_type为resource(因为是针对VM资源的豁免):
module exemption_jumpbox_sql_vulnerability_assessment { count = var.enable_jumpbox == true ? 1 : 0 source = "../policy_exemption" name = "Exemption - SQL servers on machines should have vulnerability" display_name = "Exemption - SQL servers on machines should have vulnerability" description = "Not required for Jumpbox" scope = module.create_jumbox_vm[0].virtual_machine_id exemption_type = "resource" // 明确指定为资源级豁免 policy_assignment_id = module.security_center.azurerm_subscription_policy_assignment_id policy_definition_reference_ids = var.exemption_policy_definition_ids exemption_category = "Waiver" depends_on = [module.create_jumbox_vm,module.security_center] }
其他可选方案
如果希望保留自动判断逻辑,也可以通过以下方式规避:
- 先单独部署VM(使用
-target参数),再部署豁免模块;但这种方法需要分两次执行,效率较低。 - 将豁免模块中的
count改为for_each,并使用已知的静态键(比如[var.exemption_type]),但本质和显式指定类型逻辑一致。
内容的提问来源于stack exchange,提问作者One Developer

