首页 > 解决方案 > 对于仅在应用后确定的资源的 terraform 计数是否有解决方法?

问题描述

在一个 terraform 模块中,我有一个本地用于为 ACM 证书中的所有 SAN 创建 route53 验证记录。

locals {
  my_domains = [ "some.net", "foo.com", "yack.meep" ]
  validation_domains = [for k, v in aws_acm_certificate.this.domain_validation_options : tomap(v) if contains(local.my_domains, v.domain_name)]
}

从上面看,validation_domains包含证书上所有主机名的列表(即所有 my_domains 以及验证它们所需的记录),我可以将其与 r53 记录一起用于验证。但是,我想使用 r53 中的域列表过滤此列表:

locals {
  cert_domains = [ "some.net", "foo.com", "*.yack.meep" ]
  r53_domains = [ "some.net", "yack.meep" ] 
  valiation_domains = distinct(flatten([
    for k, v in aws_acm_certificate.this.domain_validation_options : [
       for verify in local.r53_domains : tomap(v) if length(regexall("${verify}$", v.domain_name)) > 0
    ]
  ]))
}

现在,这只会在以来自的项目结尾的域上返回 ACM 验证local.r53_domains- 但是,因为我正在使用带有 ACM 返回值的regexall函数(或者它是函数?),我收到错误:length

   count = length(local.validation_domains)

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. 

除了使用 创建目标之外-target,是否有任何可以考虑的方法可以在配置后应用过滤?一种“懒惰”的解决方案如何?

这发生在 TF 0.13.x 中,不确定以后版本中是否有任何功能可能会有所帮助....

TIA。

标签: terraformrace-conditionterraform-provider-aws

解决方案


这里的一般规则是,您需要编写一个以已知长度列表结尾的表达式,即使列表中的值完全已知。这意味着length(...)它将返回一个已知数字,这足以count知道您声明的资源实例的数量。

此规则也适用于for_each,但方式略有不同:如果所有都已知,则可以知道映射的长度,因此只要我们对所有键都有已知值,就可以安全地使用其未知的映射按键。

对于aws_acm_certificate并且aws_acm_certificate_validation特别是有一个使用文档aws_route53_record中创建所需记录的示例:aws_acm_certificate_validation

resource "aws_acm_certificate" "example" {
  domain_name               = "example.com"
  subject_alternative_names = ["www.example.com", "example.org"]
  validation_method         = "DNS"
}

data "aws_route53_zone" "example_com" {
  name         = "example.com"
  private_zone = false
}

data "aws_route53_zone" "example_org" {
  name         = "example.org"
  private_zone = false
}

resource "aws_route53_record" "example" {
  for_each = {
    for dvo in aws_acm_certificate.example.domain_validation_options : dvo.domain_name => {
      name    = dvo.resource_record_name
      record  = dvo.resource_record_value
      type    = dvo.resource_record_type
      zone_id = dvo.domain_name == "example.org" ? data.aws_route53_zone.example_org.zone_id : data.aws_route53_zone.example_com.zone_id
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = each.value.zone_id
}

resource "aws_acm_certificate_validation" "example" {
  certificate_arn         = aws_acm_certificate.example.arn
  validation_record_fqdns = [for record in aws_route53_record.example : record.fqdn]
}

resource "aws_lb_listener" "example" {
  # ... other configuration ...

  certificate_arn = aws_acm_certificate_validation.example.certificate_arn
}

这个例子可以工作,因为aws_acm_certificate资源类型的设计使得domain_validation_options元素的数量在计划时总是已知的,特别是domain_name属性也总是已知的,因此适合用作 中的映射键for_each

您的示例具有过滤掉一些元素的额外约束,您应该能够通过向表达式添加if子句for并确保该条件的结果仅取决于 AWS 提供商在计划时可以知道的值来实现.


推荐阅读