Terraform:创建和验证多个 ACM 证书 [英] Terraform: Creating and validating multiple ACM certificates

本文介绍了Terraform:创建和验证多个 ACM 证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个非常令人困惑的 Terraform 资源问题,该问题在 ACM 中为(Terraform 管理的)托管区域列表自动生成 SSL 证书并进行 DNS 验证.代码也可以在in this gist中找到.

I'm running into a really confusing Terraform resource issue automating the generation and DNS validation of SSL certificates in ACM for a list of (Terraform-managed) hosted zones. Code can also be found in this gist.

我首先启动引用此环境特定变量的托管区域.

I'm starting by bootstrapping hosted zones referencing this environment-specific variable.

hosted_zones = [
    {
        domain = "site1.com"
        zone_id = "MANUALLY FILL"
    }
]

我用来构建区域的块似乎工作可靠.

The block I am using to build the zones seems to work reliably.

resource "aws_route53_zone" "zones" {
    count = "${length(var.hosted_zones)}"
    name  = "${lookup(var.hosted_zones[count.index], "domain")}"
}

在构建区域后,我手动将区域 ID 复制到变量中,因为考虑到 HCL 的限制和我缺乏使用它的经验,我还没有想出一个聪明的方法来自动化它.

After the zones are built, I am manually copying the zone ID into the variable because I haven't come up with a clever way to automate it given a combination of limitations of HCL and my lack of experience with it.

我可以使用...为每个托管区域可靠地生成裸证书和 splat 证书.

I can reliably generate naked and splat certificates for each hosted zone using...

resource "aws_acm_certificate" "cert" {
    count = "${length(var.hosted_zones)}"
    domain_name = "${lookup(var.hosted_zones[count.index], "domain")}"
    subject_alternative_names = ["*.${lookup(var.hosted_zones[count.index], "domain")}"]
    validation_method = "DNS"

    tags {
        Project = "${var.project}"
        Environment = "${var.environment}"
    }
}

当我尝试自动化证书的 DNS 验证时,事情变得棘手.对于单个托管区域,文档中有一个很好的示例,但我无法成功将其移植到多个托管区域.我的尝试...

Where things get hairy is when I try to automate the DNS validation for the certs. There is a good example in the documentation for a single hosted zone, but I haven't been able to successfully port it to multiple hosted-zones. My attempt...

resource "aws_route53_record" "cert_validation" {
    count = "${length(var.hosted_zones)}"

    name = "${aws_acm_certificate.cert.*.domain_validation_options.0.resource_record_name[count.index]}"
    type = "${aws_acm_certificate.cert.*.domain_validation_options.0.resource_record_type[count.index]}"
    zone_id = "${var.zone_override != "" ? var.zone_override : lookup(var.hosted_zones[count.index], "zone_id")}"
    records = ["${aws_acm_certificate.cert.*.domain_validation_options.0.resource_record_value[count.index]}"]
    ttl = 60
}

resource "aws_acm_certificate_validation" "cert" {
    count = "${length(var.hosted_zones)}"

    certificate_arn = "${aws_acm_certificate.cert.*.arn[count.index]}"
    validation_record_fqdns = ["${aws_route53_record.cert_validation.*.fqdn[count.index]}"]
}

我在第一次运行时看到的错误是:

The error I am seeing on first run is:

* module.acm.aws_route53_record.cert_validation: 1 error(s) occurred:
* module.acm.aws_route53_record.cert_validation: Resource 'aws_acm_certificate.cert' does not have attribute 'domain_validation_options.0.resource_record_value' for variable 'aws_acm_certificate.cert.*.domain_validation_options.0.resource_record_value'

令人讨厌的是,如果我注释了 validation 资源,apply 成功,然后取消注释并重新运行也成功.

The obnoxious part is that if I comment the validation resources, the apply succeeds, and then uncommenting them and re-running also succeeds.

我已经尝试过(感觉如何)element() lookup()list() 的每个排列map() 通过第一个资源块的输出中的索引来定位证书,但遇到了记录在案的平面列表"限制,这是我最接近成功的地方.我想了解为什么需要解决方法,以便我可以消除它.这感觉像是一个语法问题,或者我试图让 HCL 表现得更像一种 OO 语言.

I've tried (what feels like) every permutation of element() lookup(), list() and map() to target certificates by index in the output from the first resource block, but am running into documented "flat list" limitations and this is the closest I've gotten to success. I'd like to understand why the workaround is necessary so I can eliminate it. This feels like a syntax issue or me trying to get HCL to behave more like an OO language than it is.

感谢您提供任何可能有帮助的经验!

Thank you for any experience that may help!

推荐答案

我有一个类似的场景,解决它的关键是使用 locals展平().该方法也应该适用于您,因此您不需要两次通过来创建资源.

I had a similar scenario and the key to solving it was the use of locals and flatten(). The approach should also work for you such that you shouldn't need two passes to create the resources.

在这种情况下,有多个域,每个域都有子域,这些子域将出现在证书的 subjectAltName 部分.例如:

In this scenario there are multiple domains that each have subdomains that will appear in the subjectAltName section of the certificate. For example:

├── preview.example.com
│   ├── app.preview.example.com
│   └── www.preview.example.com
├── demo.example.com
│   ├── app.demo.example.com
│   └── www.demo.example.com
├── staging.example.com
│   ├── app.staging.example.com
│   └── www.staging.example.com
└── example.com
    ├── app.example.com
    └── www.example.com

为了实现这一点,我们首先设置了一些变量:

To achieve this we first set some variables:

variable "domains" {
    type = "list"
    default = [
        "demo.example.com",
        "preview.example.com",
        "staging.example.com",
        "example.com"
    ]
}
variable "subdomains" {
    type = "list"
    default = [
        "app",
        "www"
    ]
}

接下来,我们将包含子域的证书资源创建为 SAN.

Next we create the certificate resources that contain the subdomains as SANs.

resource "aws_acm_certificate" "cert" {
  count             = "${length(var.domains)}"
  domain_name       = "${element(var.domains, count.index)}"
  validation_method = "DNS"

  subject_alternative_names = ["${
    formatlist("%s.%s",
      var.subdomains,
      element(var.domains, count.index)
    )
  }"]
}

接下来,我们将需要一个局部变量来展平生成的域和子域集.这是必需的,因为 terraform 从版本 0.11.7 开始不支持嵌套列表语法,也不支持通过 element() 插值或 `list[count].

Next we're going to need a local variable to flatten the resulting set of domains and subdomains. This is needed because terraform doesn't support nested list syntax as of version 0.11.7, neither via the element() interpolation nor the `list[count].

locals {
  dvo = "${flatten(aws_acm_certificate.cert.*.domain_validation_options)}"
}

接下来我们需要查找 Route 53 区域,我们可以在后续 Route 53 记录中使用它:

We'll next need a lookup of the Route 53 zone that we can use in the subsequent Route 53 records:

data "aws_route53_zone" "zone" {
  count        = "${length(var.domains) > 0 ? 1 : 0}"
  name         = "example.com."
  private_zone = false
}

然后我们创建 Route 53 DNS 记录,这些记录将使用证书中的数据填充用于 DNS 验证的资源.我们正在向子域添加一个,以便我们也有一个未包含在子域列表中的基域的记录.

We then create the Route 53 DNS records that will be populated with data from the certificate resource for DNS validation. We're adding one to the subdomains so that we also have a record for the base domain not included in the list of subdomains.

resource "aws_route53_record" "cert_validation" {
  count   = "${length(var.domains) * (length(var.subdomains) + 1)}"
  zone_id = "${data.aws_route53_zone.zone.id}"
  ttl     = 60

  name    = "${lookup(local.dvo[count.index], "resource_record_name")}"
  type    = "${lookup(local.dvo[count.index], "resource_record_type")}"
  records = ["${lookup(local.dvo[count.index], "resource_record_value")}"]
}

最后我们创建证书验证资源,等待证书生效发布.

Finally we create the certificate validation resource that will wait for the certificate to be issued.

resource "aws_acm_certificate_validation" "cert" {
  count                   = "${length(var.domains) * (length(var.subdomains) + 1)}"
  certificate_arn         = "${element(aws_acm_certificate.cert.*.arn, count.index)}"
  validation_record_fqdns = ["${aws_route53_record.cert_validation.*.fqdn}"]
}

最后一个资源的一个警告是它会为每个资源创建一个实例请求证书,但每个实例将依赖于所有域中的所有 FQDN,并且子域.这不会影响 AWS 中的任何内容,但 terraform 代码不会继续/完成直到颁发所有证书.

The one caveat for this last resource is that it'll create one instance of the resource for every certificate requested, but each instance will depend on all the FQDNs across all domains and subdomains. This won't affect anything in AWS but the terraform code won't continue/complete until all certs are issued.

这应该可以在单个应用运行中运行,而无需在第一遍中-target任何资源,虽然在 验证需要多长时间完成什么时候通过 terraform 执行,因此可能需要第二次通过,尽管无需更改代码或计划/应用调用.

This should work in a single apply run with no need to -target any resources in a first pass, though there is an apparently known issue around how long it takes for the validations to complete when performed via terraform, and for this reason it may require a second pass, albeit without changing the code or plan/apply invocation.

这篇关于Terraform:创建和验证多个 ACM 证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆