azure - 使用基于另一个本地创建的 for_each 本地变量时,Terraform 出现问题

标签 azure foreach terraform nested-loops local-variables

我正在尝试使用引用数据资源的本地变量创建 azure keyvault secret 。我正在迭代包含我的环境的数组并创建一个 map 列表,其中 每个项目都是给定环境的一组 secret 。 然后,我使用另一个本地映射,创建两个列表(一个包含键,另一个包含值),然后将它们压缩,从而将这些映射合并为一个映射。

我最终在第二个本地使用 for_each 来创建资源。

如果我在没有创建实际 secret 资源(“azurerm_key_vault_secret”)的情况下运行根模块并再次使用它,则一切正常。

如果我尝试一次性完成所有操作,因为我想在 CI/CD 上实现,我会收到错误消息:

|错误:for_each 参数无效

|在变量.tf 第 239 行,资源“azurerm_key_vault_secret”“example”中:

│239: for_each = nonsensitive(local.example_map)

│ local.example_map只有在apply之后才知道

|“for_each”值取决于资源属性,这些属性在应用之前无法确定,因此 Terraform 无法预测将创建多少个实例。要解决此问题,请使用 -target 参数首先仅应用 for_each 所依赖的资源。

如果有人知道我如何工作。在我看来,本地人内部的这种数据转换不太有效。 也许我对整件事的理解都是错误的。任何指示将不胜感激。

这是我想要工作的代码:

variable "environment" {
    default = [ "dev", "prod"]
}

locals {
  example = distinct(flatten([
    for namespace in var.environment : {
        "${environment}-password1" = "${environment}-password",
        "${environment}-password2" = "{\"connection_string\" : \"${data.azurerm_storage_account.storage_account_example["${environment}"].primary_connection_string}\"}",
        "${environment}-password3" = "{\"client_id\" : \"${jsondecode("${data.azurerm_key_vault_secret.other_credentials["${environment}"].value}").clients["example"].client_id}\"}",
        "${environment}-password4" = "{\"password\" : \"${data.azurerm_key_vault_secret.k_password.value}\"}",
        "${environment}-password5" = "{\"azurestorageaccountname\" : \"${data.azurerm_storage_account.example.name}\", \"azurestorageaccountkey\" : \"${data.azurerm_storage_account.example.primary_access_key}\"}",
        "${environment}-password6" = "{\"connection_string\" : \"${module.some_module.connection_string}\"}",
  }]))

  example_map = zipmap(
    flatten(
      [for item in local.example : keys(item)]
    ),
    flatten(
      [for item in local.example : values(item)]
    )
  )
}

resource "azurerm_key_vault_secret" "example" {
  for_each     = nonsensitive(local.example_map)
  name         = each.key
  value        = each.value
  key_vault_id = module.keyvault.id
  content_type = "password"
}

Here is the data structures created by local.example and local.example_map


"example": {
  "value": [
    {
      "dev-password1" = "dev-password",
      "dev-password2" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}",
      "dev-password3" = "{\"client_id\" : \"myclientID\"}",
      "dev-password4" = "{\"password\" : \"password123\"}",
      "dev-password5" = "{\"azurestorageaccountname\" : \"somestorageaccount\", \"azurestorageaccountkey\" : \"XXXxxxxXXXXxxxxXXXxxxxxxe++++++NNNNNNNNNCCCccccccccccccccccc==}\"}",
      "dev-password6" = "{\"connection_string\" : \"${module.some_module.connection_string}\"}"
    },
    {
      "prod-password1" = "prod-password",
      "prod-password2" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}",
      "prod-password3" = "{\"client_id\" : \"myclientID\"}",
      "prod-password4" = "{\"password\" : \"password123\"}",
      "prod-password5" = "{\"azurestorageaccountname\" : \"somestorageaccount\", \"azurestorageaccountkey\" : \"XXXxxxxXXXXxxxxXXXxxxxxxe++++++NNNNNNNNNCCCccccccccccccccccc==}\"}",
      "prod-password6" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=yetanotherone;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}"
    }
  ]
}

"example_map": {
    "value": {
      "dev-password1" = "dev-password",
      "dev-password2" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}",
      "dev-password3" = "{\"client_id\" : \"myclientID\"}",
      "dev-password4" = "{\"password\" : \"password123\"}",
      "dev-password5" = "{\"azurestorageaccountname\" : \"somestorageaccount\", \"azurestorageaccountkey\" : \"XXXxxxxXXXXxxxxXXXxxxxxxe++++++NNNNNNNNNCCCccccccccccccccccc==}\"}",
      "dev-password6" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=yetanotherone;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}"
      "prod-password1" = "prod-password",
      "prod-password2" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}",
      "prod-password3" = "{\"client_id\" : \"myclientID\"}",
      "prod-password4" = "{\"password\" : \"password123\"}",
      "prod-password5" = "{\"azurestorageaccountname\" : \"somestorageaccount\", \"azurestorageaccountkey\" : \"XXXxxxxXXXXxxxxXXXxxxxxxe++++++NNNNNNNNNCCCccccccccccccccccc==}\"}",
      "prod-password6" = "{\"connection_string\" : \"DefaultEndpointsProtocol=https;AccountName=yetanotherone;AccountKey=blablablablblabalbalbalbalblablablablablalbalbalbl==;EndpointSuffix=foo.bar.net\"}"
    },
    "type": [
    "object",
    {
        "dev-password1": "string",
        "dev-password2": "string",
        "dev-password3": "string",
        "dev-password4": "string",
        "dev-password5": "string",
        "dev-password6": "string",
        "prod-password1": "string",
        "prod-password2": "string",
        "prod-password3": "string",
        "prod-password4": "string",
        "prod-password5": "string",
        "prod-password6": "string",
    }
    ]
}

最让我困惑的是,如果我使用以下数据结构,这是硬编码,而不是基于 namespace 进行第一次转换。从另一个模块获取信息的条目不会引起任何问题,而且一切都工作得很好。

locals {
  hardcoding_namespaces = {
    "dev-password1" = "dev-password"
    "dev-password2" = "{\"connection_string\" : \"${data.azurerm_storage_account.storage_account_example["dev"].primary_connection_string}\"}"
    "dev-password3" = "{\"connection_string\" : \"${module.some_module.connection_string}\"}"
    "prod-password1" = "prod-password"
    "prod-password2" = "{\"connection_string\" : \"${data.azurerm_storage_account.storage_account_example["prod"].primary_connection_string}\"}"
    "prod-password3" = "{\"connection_string\" : \"${module.some_module.connection_string}\"}"

  }
}

resource "azurerm_key_vault_secret" "another_example" {
  for_each     = local.hardcoding_namespaces
  name         = each.key
  value        = each.value
  key_vault_id = module.keyvault.id
  content_type = "password"

} 

如果生成的数据结构相同,为什么 for_each 适用于其中一个而不适用于另一个? [1]:/image/cTq5f.png

最佳答案

来自文档

Sensitive values, such as sensitive input variables, sensitive outputs, or sensitive resource attributes, cannot be used as arguments to for_each. The value used in for_each is used to identify the resource instance and will always be disclosed in UI output, which is why sensitive values are not allowed. Attempts to use sensitive values as for_each arguments will result in an error.(visite https://www.terraform.io/language/meta-arguments/for_each#limitations-on-values-used-in-for_each)

如果输入敏感,

Keys () 将始终返回敏感值,因此请尝试以下操作:

  example_map = zipmap(
flatten(
  [for item,value in local.example : item]
),
flatten(
  [for item, value in local.example : value]
)

)

关于azure - 使用基于另一个本地创建的 for_each 本地变量时,Terraform 出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71405642/

相关文章:

azure - Databricks 笔记本时间表

azure - 自动过期孤立订阅 (Azure ServiceBus Messaging SubscriptionClient)

c# - 无法使用 SendGrid 触发有关 Blob 存储更改的电子邮件

c# - foreach 在循环函数结果时如何工作?

php - 如何在 PHP 中迭代数组并获取键值

amazon-web-services - 使用新记录验证证书

node.js - azure webapp Node 默认启动脚本

java - Java foreach 循环是否是重复执行的矫枉过正

azure - 使用 terraform 在 AKS 中创建 PV 使用默认存储类

git - Terraform 不遵守 git 的 ssh 配置