Terraform:使用 Cloudflare DNS 验证 AWS 证书的迭代列表

Terraform: Iterating list for AWS Certificate validation with Cloudflare DNS

我在 tfvars 文件中有一个地图,其中包含 Cloudflare 区域 ID、站点地址和区域(域),我想遍历该地图,生成 ACM 证书,证书验证 DNS 记录为在 Cloudflare 中创建。 我的地图是这样的;

my_domains = {
  example1 = {
    cloudflare_zone_id = "00000000000000000000000000001"
    address            = "dev.example1.com"
    domain             = "example1.com"
  }
  example2 = {
    cloudflare_zone_id = "0000000000000000000000000000002"
    address            = "dev.example2.com"
    domain             = "example2.com"
  }
  example3 = {
    cloudflare_zone_id = "0000000000000000000000000000003"
    address            = "dev.library.example3.com"
    domain             = "example3.com"
  }
}

然后我有以下用于证书创建和验证的代码:

resource "aws_acm_certificate" "my_certs" {
  for_each          = var.my_domains
  domain_name       = each.value.address
  validation_method = "DNS"

  subject_alternative_names = [
    "*.${each.value.address}"
  ]

  lifecycle {
    create_before_destroy = true
  }
}
resource "cloudflare_zone" "my_zone" {
  for_each = var.my_domains
  zone     = each.value.domain
  type     = "full"
}

resource "cloudflare_record" "my_certificate_validation" {
  for_each = {
    for dvo in aws_acm_certificate.my_certs.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = cloudflare_zone.my_zone.id
  name    = each.value.name
  value   = trimsuffix(each.value.record, ".")
  type    = each.value.type
  ttl     = 1
  proxied = false
}

当我运行一个计划时,我得到以下错误:

Error: Missing resource instance key

on cfcertvalidation.tf line 23, in resource "cloudflare_record" "my_certificate_validation": 23: for dvo in aws_acm_certificate.my_certs.domain_validation_options : dvo.domain_name => {

Because aws_acm_certificate.my_certs has "for_each" set, its attributes must be accessed on specific instances.

For example, to correlate with indices of a referring resource, use: aws_acm_certificate.my_certs[each.key]

Error: Missing resource instance key

on cfcertvalidation.tf line 30, in resource "cloudflare_record" "my_certificate_validation": 30: zone_id = cloudflare_zone.my_zone.id

Because cloudflare_zone.cdt has "for_each" set, its attributes must be accessed on specific instances.

For example, to correlate with indices of a referring resource, use: cloudflare_zone.my_zone[each.key]

注意:我添加了 cloudflare_zone 资源而不是使用地图中已有的区域 ID 作为一种简化故障排除的方法。

我确信答案在使用 [each.key] 的建议中,但我不确定如何实施它。

如有任何帮助,我们将不胜感激。

我已经为我的解决方案稍微更改了地图,因此为了完整起见,我将更改后的地图包含在此处:

variable "my_domains" {
  type = map(any)
  default = {
    example1 = {
      cf_zone_id         = "0000000000000000000000000000"
      address            = "example1.com"
      zone               = "example1.com"
  }
  example2 = {
    cf_zone_id         = "0000000000000000000000000000"
    address            = "example2.com"
    zone               = "example2.com"
  }
  example3 = {
    cf_zone_id         = "0000000000000000000000000000"
    address            = "library.example3.com"
    zone               = "example3.com"
  }
 }
}

下面是可行的解决方案,我们首先创建一个列表类型的局部变量,循环遍历 my_domains 映射以获取我们需要的证书验证记录。然后将其转换为地图,然后 cloudflare_record 资源使用它来创建相关的 DNS 条目。

resource "aws_acm_certificate" "my_certs" {
  for_each                  = var.my_domains
  domain_name               = "${var.env_url_prefix}${var.my_domains[each.key] ["address"]}"
  validation_method         = "DNS"
  subject_alternative_names = ["*.${var.env_url_prefix}${var.my_domains[each.key]["address"]}"]
  lifecycle {
  create_before_destroy = true
 }
}

locals {
   validation = [
     for certs in keys(var.my_domains) : {
        for dvo in aws_acm_certificate.my_certs[certs].domain_validation_options : dvo.domain_name => {
            name    = dvo.resource_record_name
            value   = trimsuffix(dvo.resource_record_value, ".")
            type    = dvo.resource_record_type
            zone_id = var.my_domains[certs]["cf_zone_id"] # Get the zone id
          }
      }
    ]
 # transform the list into a map
    validation_map = { for item in local.validation : keys(item)[0] => values(item)[0]
    }
}


resource "cloudflare_record" "my_cert_validations" {
   for_each = local.validation_map

   zone_id = local.validation_map[each.key]["zone_id"]
   name    = local.validation_map[each.key]["name"]
   value   = local.validation_map[each.key]["value"]
   type    = local.validation_map[each.key]["type"]
   ttl     = 1
   proxied = false #important otherwise validation will fail
}