aws_acm_certificate 似乎已经更改了它的状态输出,可能是由于提供商更新——我做错了吗?

aws_acm_certificate seems to have changed its state output possibly due to a provider update- am I doing this wrong?

Terraform v0.12.12
+ provider.aws v3.0.0
+ provider.template v2.1.2

在我这样做之前:

resource "aws_route53_record" "derps" {
  name    = aws_acm_certificate.mycert[0].resource_record_name
  type    = aws_acm_certificate.mycert[0].resource_record_type
  zone_id = var.my_zone_id
  records = aws_acm_certificate.mycert[0].resource_record_value
  ttl     = 60
}

一周前对我来说效果很好。

我刚刚做了一个计划,但出现了一个错误:

records = [aws_acm_certificate.mycert.domain_validation_options[0].resource_record_value]

This value does not have any indices.

现在我不固定提供程序版本,所以我假设我拉了一个更新的版本并且资源发生了变化。

与此抗争并意识到它不是一个列表(即使在做 show state 时它确实看起来像一个)我现在这样做是为了让它成为一个列表:

resource "aws_route53_record" "derps" {
  name    = sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_name)[0]
  type    = sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_type)[0]
  zone_id = var.my_zone_id
  records = [sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_value)[0]]
  ttl     = 60
} 

结果没有发生任何变化,这很好。但是,如果我使用文档中的示例来执行此操作,他们现在使用 for_each: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation

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

以上是现在正确的做法吗?我会 运行 以我目前的方式处理问题吗?像上面那样做会导致 destroy/recreate(我想我可以自己导入它,但这很痛苦)。

按我的方式做不会导致意外的差异吗?

编辑

所以,更具体地针对我的问题。这是我在查看状态时看到的:

terraform state show aws_acm_certificate.mycert

    ...
    domain_name               = "*.mydom.com"
    domain_validation_options = [
        {
            domain_name           = "*.mydom.com"
            resource_record_name  = "_11111111111.mydom.com."
            resource_record_type  = "CNAME"
            resource_record_value = "_1111111111.11111111.acm-validations.aws."
        },
        {
            domain_name           = "mydom.com"
            resource_record_name  = "_11111111111.mydom.com."
            resource_record_type  = "CNAME"
            resource_record_value = "_1111111111.111111111.acm-validations.aws."
        },
    ]
    ... 

通过使用排序,我有效地使用了计数,如果顺序发生变化,这当然会导致 destroy/recreate。但就我而言,我认为这不太可能?我也不完全理解仅使用通配符验证配置中的值与同时使用它们之间的区别。

AWS Terraform 提供程序最近升级到版本 3.0。此版本附带 a list of breaking changes. I recommend consulting the AWS provider 3.0 upgrade guide.

您遇到的问题是因为 domain_validation_options 属性现在是一个集合而不是列表。来自该指南:

Since the domain_validation_options attribute changed from a list to a set and sets cannot be indexed in Terraform, the recommendation is to update the configuration to use the more stable resource for_each support instead of count

我建议按照升级指南的建议使用新的 foreach 语法,以避免意外的差异。该指南指出您需要使用 terraform state mv 将旧配置状态移动到新配置,以防止重新创建资源。

你也可以用tolist作弊。

例如将 aws_acm_certificate.mycert.domain_validation_options[*].resource_record_name 重写为 tolist(aws_acm_certificate.mycert.domain_validation_options)[*].resource_record_name

我必须在一个模块中执行此操作,因为这个特定的模块资源中已经有一个 count,而且我知道我只会有一个条目。

这与我们刚才面临的问题相同。我们使用 for_each 根据提供的局部变量定义多个站点的托管,现在,由于我们已经使用 for_each 我们可以'不要用它来解决他们的变化..不幸的是。

我不想进行排序,所以我检查了 Jimmy 写的内容,但由于输出是索引,它不适用于这种情况,我使用 [0] 而不是 [*] 修复了它:

resource "aws_route53_record" "cert_validation" {
  for_each = local.web_pages
  allow_overwrite = true
  name            = tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_name
  type            = tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_type
  records         = [tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_value]
  zone_id         = var.aws_hosted_zone
  ttl             = 60
}

现在为我们工作 ;)

下面这 3 组代码有效。 (我用的是Terraform v0.15.0

*1st2nd的区别是[0].0

第一名:

resource "aws_route53_record" "myRecord" {
  zone_id = aws_route53_zone.myZone.zone_id
  name    = tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_name
  type    = tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_type
  records = [tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_value]
  ttl = "60"
  allow_overwrite = true
}

第二名:

resource "aws_route53_record" "myRecord" {
  zone_id = aws_route53_zone.myZone.zone_id
  name    = tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_name
  type    = tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_type
  records = [tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_value]
  ttl = "60"
  allow_overwrite = true
}

第三名:

resource "aws_route53_record" "myRecord" {
  for_each = {
    for dvo in aws_acm_certificate.myCert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

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