绕过Terraform的"known after apply"问题:如何检测未知变量?
解决Terraform "known after apply" 导致的for_each参数无效问题
问题场景
在部署Azure存储账户对应的Defender防护资源时,使用for_each遍历存储账户ID,若部分存储账户为首次创建(其ID属于"known after apply"的未知值),会触发如下报错:
╷ │ Error: Invalid for_each argument │ │ on storage.tf line 291, in resource "azurerm_security_center_storage_defender" "defender_for_storage": │ 291: for_each = { │ 292: for id in [ │ 293: azurerm_storage_account.backups.id, │ 294: azurerm_storage_account.media_blobs.id, │ 295: ] : id => id │ 296: } │ ├──────────────── │ │ azurerm_storage_account.backups.id is "***" │ │ azurerm_storage_account.media_blobs.id is a string, known only after apply │ │ The "for_each" map includes keys derived from resource attributes that │ cannot be determined until apply, and so Terraform cannot determine the │ full set of keys that will identify the instances of this resource. │ │ When working with unknown values in for_each, it's better to define the map │ keys statically in your configuration and place apply-time results only in │ the map values. │ │ Alternatively, you could use the -target planning option to first apply │ only the resources that the for_each value depends on, and then apply a │ second time to fully converge. ╵
原代码如下:
resource "azurerm_security_center_storage_defender" "defender_for_storage" { for_each = { for id in [ azurerm_storage_account.backups.id, azurerm_storage_account.media_blobs.id, ] : id => id } storage_account_id = each.value malware_scanning_on_upload_enabled = true malware_scanning_on_upload_cap_gb_per_month = 10 sensitive_data_discovery_enabled = true }
解决方案
使用Terraform的isunknown函数过滤未知ID,动态调整for_each的遍历集合,代码如下:
resource "azurerm_security_center_storage_defender" "defender_for_storage" { for_each = { for id in [ isunknown(azurerm_storage_account.backups.id) ? "" : azurerm_storage_account.backups.id, isunknown(azurerm_storage_account.media_blobs.id) ? "" : azurerm_storage_account.media_blobs.id, ] : id => id if id != "" } storage_account_id = each.value malware_scanning_on_upload_enabled = true malware_scanning_on_upload_cap_gb_per_month = 10 sensitive_data_discovery_enabled = true }
方案原理
- 过滤未知值:通过
isunknown判断存储账户ID是否为部署后可知的未知值,将未知ID转为空字符串 - 动态生成集合:使用
if id != ""过滤掉空值,确保for_each的键集合在部署前完全确定 - 分阶段部署适配:第一次部署时,仅为已存在(ID已知)的存储账户创建Defender资源;第二次部署时,所有存储账户ID已确定,自动创建剩余的防护资源
优势
- 无需修改代码(适配多环境共用场景,避免打乱Git历史)
- 无需使用
-target参数,减少额外部署步骤,提升部署确定性和效率 - 一键部署即可适配不同环境的资源状态
内容的提问来源于stack exchange,提问作者Simao Gomes Viana
相关产品推荐
相关产品推荐

