如何在Terraform资源循环中使用非唯一键来创建唯一资源

如何在Terraform资源循环中使用非唯一键来创建唯一资源,terraform,digital-ocean,Terraform,Digital Ocean,我想使用一个通用模块为一个域创建每种类型的DNS记录,这样就可以使用如下内容调用它: module "example_com_dns" { source = "[PATH_TO_MODULES]/modules/dns" domain = "example.com" a_records = { "@" = [SOME IP] "www" = [SOME I

我想使用一个通用模块为一个域创建每种类型的DNS记录,这样就可以使用如下内容调用它:

module "example_com_dns" {
  source = "[PATH_TO_MODULES]/modules/dns"

  domain  = "example.com"

  a_records = {
    "@"    = [SOME IP]
    "www"  = [SOME IP]
    "home" = [SOME IP]
  }

  txt_records = {
    "@"                    = "txt-foobar1"
    "@"                    = "txt-foobar2"
    "mail._domainkey.self" = "foobar"
  }

  mx_entries = {
     "10" = "mail.someprovider.com"
     "20" = "mail2.someprovider.com"
  }

  cname_records {
    "cname-foo" = "cname-bar
  }
}
我有一些适合A、CNAME和MX记录的东西,但TXT有一个边缘案例,我需要解决这个问题。我的模块为每种类型的记录都有资源块,这些记录通过循环运行。我只粘贴TXT文件,但它们都是一样的:

resource "digitalocean_record" "this_txt_record" {
  for_each = var.txt_records

  domain = var.domain
  type   = "TXT"
  name   = each.key
  value  = each.value
}
这一切都很好,除了有两条记录的键为“@”,因此只创建最后一条记录(在我上面的示例中,这是“txt-foobar2”):


...
#module.example_com.digitalocean_记录。将创建此_txt_记录[“@”]
+资源“digitalocean\u记录”“此\u txt\u记录”{
+domain=“example.com”
+fqdn=(应用后已知)
+id=(应用后已知)
+name=“@”
+ttl=(应用后已知)
+type=“TXT”
+value=“txt-foobar2”
}
我想让它创建“txt-foobar1”和“txt-foobar2”,即使在地图中给定非唯一键

也许这是错误的方法,我只需要想出一个聪明的循环来解析这个结构

  txt_records = [
    { "@" = "foo" },
    { "@" = "bar"},
    { "mail._domainkey.self" = "foobar"}
  ]

如果是这样,我现在也失败了:)

资源不能通过
为每个
创建一个列表,因为必须有一个唯一的键才能成为地形资源名称的一部分。列表索引不能是一个可靠的键,因为如果您对列表中的项目重新排序,您的TF计划将全部混乱

另一方面,根据定义,地图确实具有唯一的关键点

不过,您可以从列表中生成地图!我发现了这个小把戏。注意:您还需要手动计算唯一映射键(
${txt_记录[0]}=${txt_记录[1]}
,在下面的示例中)

更新到位后的资源:

module "example_com_dns" {
  ...

  txt_records = [
    ["@",                     "txt-foobar1"],
    ["@",                     "txt-foobar2"],
    ["mail._domainkey.self",  "foobar"],
  ]
}
或者,如果您愿意,可以稍微详细一点:

module "example_com_dns" {
  ...

  txt_records = [
    {name: "@", value: "txt-foobar1"},
    {name: "@", value: "txt-foobar2"},
    {name: "mail._domainkey.self", value: "foobar"},
  ]
}

除了已经给出的方法外,还可以使用以下方法:

 variable "txt_records" {
  default = {
       "@" = ["foo", "bar"],
       "mail._domainkey.self" = ["foobar"]
       }
 }
然后,您可以使用以下方法展平
txt\u记录

locals {

  txt_records_flat = merge([
      for key, values in var.txt_records:
        {for value in values: 
           "${key}-${value}" => {"record_name" = key, "record_value" = value}
        }
  ]...)
}
这将导致以下内容的
local.txt\u记录\u平坦

{
  "@-bar" = {
    "record_name" = "@"
    "record_value" = "bar"
  }
  "@-foo" = {
    "record_name" = "@"
    "record_value" = "foo"
  }
  "mail._domainkey.self-foobar" = {
    "record_name" = "mail._domainkey.self"
    "record_value" = "foobar"
  }
}

然后你使用它:

resource "digitalocean_record" "this_txt_record" {
  for_each = local.txt_records_flat

  domain = var.domain
  type   = "TXT"
  name   = each.value.record_name
  value  = each.value.record_value
}

在你提议的新结构中,
“mail.\u domainkey.self”=“foobar”
会在哪里?哦,在同一个列表中,我只是忘了添加它。我现在编辑了问题中的示例,将其包括在内。是
mail.\u domainkey.self“
一个常量字符串,或者它可以采用不同的值?这一个值总是相同的,但这只是一个示例。大多数情况下,TXT会有一个唯一的记录映射(对于其他一些记录类型,我相信它甚至是强制性的),但对于我们需要设置SPF记录之类的东西的这个用例,我们总是使用“@“这就是我要找的。我正在努力达到
每一个.value.value
。另外,我不知道在这样的地图中使用
来分配任务。这和使用
=
有什么区别吗?我在HCL规范中找不到任何引用。@Afraz对,可以使用
=
。检查(
objectelem
definition)我还没有尝试过这个,我选择了Max的答案,但是我很确定它也会起作用,所以谢谢!
{
  "@-bar" = {
    "record_name" = "@"
    "record_value" = "bar"
  }
  "@-foo" = {
    "record_name" = "@"
    "record_value" = "foo"
  }
  "mail._domainkey.self-foobar" = {
    "record_name" = "mail._domainkey.self"
    "record_value" = "foobar"
  }
}

resource "digitalocean_record" "this_txt_record" {
  for_each = local.txt_records_flat

  domain = var.domain
  type   = "TXT"
  name   = each.value.record_name
  value  = each.value.record_value
}