简体   繁体   English

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

[英]Terraform issues when using for_each local variable created based on another local

I am trying to create azure keyvault secrets using locals which reference data resources.我正在尝试使用引用数据资源的本地人创建 azure keyvault 秘密。 I am iterating over an array containing my environments and creating a list of maps where each item is the set of secrets for a given environment.我正在遍历一个包含我的环境的数组并创建一个映射列表,其中每个项目都是给定环境的一组秘密。 Using another local, I then proceed to merge these maps into a single one by creating two lists, one with keys and another with values and then zipping them.使用另一个本地,然后我通过创建两个列表(一个带有键,另一个带有值)然后压缩它们,继续将这些映射合并为一个。

I finally use for_each on the second local to create the resource.我最终在第二个本地使用 for_each 来创建资源。

If I run my root module without creating the actual secret resources ("azurerm_key_vault_secret) and a second time with it, it all works fine.如果我在没有创建实际秘密资源(“azurerm_key_vault_secret”)的情况下运行我的根模块,并且第二次使用它,一切正常。

If I try to do it all in one go, as I want to implement on my CI/CD I get the error message:如果我尝试在一个 go 中完成所有操作,因为我想在我的 CI/CD 上实现,我会收到错误消息:

|Error: Invalid for_each argument |错误:无效的 for_each 参数

|on variables.tf line 239, in resource “azurerm_key_vault_secret” “example”: |在 variables.tf 第 239 行,在资源“azurerm_key_vault_secret”“example”中:

│239: for_each = nonsensitive(local.example_map) │239: for_each = nonsensitive(local.example_map)

│ local.example_map will be known only after apply │ local.example_map apply后才知道

| | The “for_each” value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. “for_each”值取决于资源属性,直到应用时才能确定,因此 Terraform 无法预测将创建多少个实例。 To work around this, use the -target argument to first apply only the resources that the for_each depends on.要解决此问题,请使用 -target 参数首先仅应用 for_each 所依赖的资源。

If anybody has any idea how i could make work.如果有人知道我该如何工作。 It seems to me that this data transformation within locals doesn't quite work.在我看来,本地人内部的这种数据转换并不十分有效。 Maybe I am going about the whole thing wrong.也许我做错了整件事。 Any pointers would be highly appreciated.任何指针将不胜感激。

Here is the code I am trying to make work:这是我试图使工作的代码:

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",
    }
    ]
}

Also what confuses me the most is that if I work with the following data structure, which is hard coding instead of doing the first transformation based on namespaces.同样让我最困惑的是,如果我使用以下数据结构,这是硬编码而不是基于名称空间进行第一次转换。 The entry getting information from another module doesn't cause any problems and it all works wonderfully.从另一个模块获取信息的条目不会导致任何问题,而且一切都很好。

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"

} 

if the resulting data structure is the same, why for_each works for one and not for the other?如果生成的数据结构相同,为什么 for_each 只适用于一个而不适用于另一个? [1]: https://i.stack.imgur.com/cTq5f.png [1]: https://i.stack.imgur.com/cTq5f.png

from the doc从文档

Sensitive values, such as sensitive input variables, sensitive outputs, or sensitive resource attributes, cannot be used as arguments to for_each.敏感值,例如敏感输入变量、敏感输出或敏感资源属性,不能用作 for_each 的 arguments。 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. for_each 中使用的值用于标识资源实例,并且将始终在 UI output 中公开,这就是不允许使用敏感值的原因。 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 )尝试将敏感值用作 for_each arguments 将导致错误。(访问https://www.terraform.io/language/meta-arguments/for_each#limitations-on-values-used-in-for_each

Keys () will always return a sensitive value if the input is sensitive, so instead try the following:如果输入敏感, Keys ()将始终返回敏感值,因此请尝试以下操作:

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

) )

This is probably because of module.some_module.connection_string .这可能是因为module.some_module.connection_string You can't use dynamic values in for_each .您不能在for_each中使用动态值。 As the error message says, you have to use target to first create those dynamic resources, and then your for_each will work.正如错误消息所说,您必须先使用target创建那些动态资源,然后您的for_each才会起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM