我正在尝试解决我面临的“申请后已知”问题。 确切地说,这是我正在处理的代码:
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
}
错误:
╷
│ 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.
╵
这里的问题是存储帐户可能尚不存在,因此在这种情况下无法创建“defender_for_storage”,但如果我删除代码,让 Terraform 创建存储帐户,然后将代码添加回来,它就会起作用.
我知道上面的特定示例还有其他解决方案(例如使用
-target
或不使用 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
}
在这种情况下,Terraform 中的特殊函数
isunknown
对于尚不存在的存储帐户将默认为 ""
,过滤掉未知的帐户,然后继续处理存在的帐户。
然后我可以再次运行它,它将创建剩余的资源,不会出现错误。
我的目标是避免每次部署时都必须更改代码,因为有多个环境使用相同的代码,这确实会弄乱 Git 历史记录。
使用
-target
会使部署的确定性降低并且容易出错,并且必须添加额外的工作流程,所有这些都增加了部署过程的额外工作和不确定性(就错误和前进的能力而言),这应该理想情况下,无论使用什么状态,都只需单击一下即可。
“已知性”不会作为您可以在 Terraform 中编程的东西公开,因为这会破坏这样的承诺:当您应用计划时,Terraform 要么按照计划执行,要么返回一个错误来解释为什么它不能。 (这将引入在计划和应用阶段之间发生值变化的可能性,而 Terraform 的执行模型依赖于这种可能性。)
错误消息为您提供了两种选择。您已经排除了
-target
选项,但就您而言,我认为另一个选项更适用:“静态定义地图键”。
例如:
for_each = {
backups = azurerm_storage_account.backups.id
media_blobs = azurerm_storage_account.media_blobs.id
}
这将声明两个具有已知实例键的实例:
azurerm_security_center_storage_defender.defender_for_storage["backups"]
azurerm_security_center_storage_defender.defender_for_storage["media_blobs"]
每一个都会配置相应的存储帐户id。