amazon-web-services - Terraform:创建和验证多个 ACM 证书
问题描述
我遇到了一个非常令人困惑的 Terraform 资源问题,该问题自动生成和 DNS 验证 ACM 中的 SSL 证书,以获取(Terraform 管理的)托管区域列表。代码也可以在这个 gist中找到。
我从引导引用此环境特定变量的托管区域开始。
hosted_zones = [
{
domain = "site1.com"
zone_id = "MANUALLY FILL"
}
]
我用来构建区域的块似乎工作可靠。
resource "aws_route53_zone" "zones" {
count = "${length(var.hosted_zones)}"
name = "${lookup(var.hosted_zones[count.index], "domain")}"
}
构建区域后,我手动将区域 ID 复制到变量中,因为考虑到 HCL 的限制和我缺乏使用它的经验,我还没有想出一个聪明的方法来自动化它。
我可以使用...可靠地为每个托管区域生成裸证书和 splat 证书。
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 验证时,事情变得棘手。单个托管区域的文档中有一个很好的示例,但我无法成功地将其移植到多个托管区域。我的尝试...
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]}"]
}
我在第一次运行时看到的错误是:
* 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
成功,然后取消评论并重新运行也会成功。
我已经尝试(感觉像)的每一个排列element()
lookup()
,list()
并map()
在第一个资源块的输出中通过索引来定位证书,但我遇到了记录在案的“平面列表”限制,这是我最接近成功的地方。我想了解为什么需要解决方法,以便我可以消除它。这感觉像是一个语法问题,或者我试图让 HCL 表现得更像一种 OO 语言。
感谢您提供任何可能有帮助的经验!
解决方案
我有一个类似的场景,解决它的关键是使用locals和flatten()。该方法也应该适用于您,因此您不需要两次通过来创建资源。
在这种情况下,有多个域,每个域都有子域,这些子域将出现在证书的 subjectAltName 部分。例如:
├── 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
为此,我们首先设置一些变量:
variable "domains" {
type = "list"
default = [
"demo.example.com",
"preview.example.com",
"staging.example.com",
"example.com"
]
}
variable "subdomains" {
type = "list"
default = [
"app",
"www"
]
}
接下来,我们创建包含子域作为 SAN 的证书资源。
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)
)
}"]
}
接下来,我们将需要一个局部变量来展平生成的域和子域集。这是必需的,因为从 0.11.7 版开始,terraform 不支持嵌套列表语法,既不通过element()
插值也不通过 `list[count]。
locals {
dvo = "${flatten(aws_acm_certificate.cert.*.domain_validation_options)}"
}
接下来,我们需要查找可在后续 Route 53 记录中使用的 Route 53 区域:
data "aws_route53_zone" "zone" {
count = "${length(var.domains) > 0 ? 1 : 0}"
name = "example.com."
private_zone = false
}
然后,我们创建 Route 53 DNS 记录,这些记录将填充来自证书资源的数据以进行 DNS 验证。我们正在向子域添加一个,以便我们还有一条未包含在子域列表中的基本域的记录。
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")}"]
}
最后,我们创建将等待证书颁发的证书验证资源。
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 代码不会继续/完成。
这应该可以在一次应用运行中运行,而无需-target
在第一遍中使用任何资源,尽管有一个明显已知的问题是通过 terraform 执行验证需要多长时间才能完成,因此它可能需要第二次通过,尽管没有更改代码或计划/应用调用。
推荐阅读
- python - 无法安装“mediapipe”库
- python-3.x - Python tkinter 窗口在将变量导入其他模块时在代码完成运行后第二次运行
- python - 在 Python 中计算数组的元素
- python - 如何从列表中删除 '\x00'
- javascript - EJS For 循环和 If 语句未显示正确的值
- reactjs - 反应打字稿 - 解析错误:意外的令牌 - 升级到 17.0.1 后
- flutter - Flutter:在应用程序崩溃之前捕获 ClientException(在收到完整标头之前关闭连接)
- python - 如何使用 Selenium 更改搜索参数?
- python-3.x - 如果列表中的项目存在于列表列表中,python打印项目和相应的列表
- google-sheets - 如何简化这个 IF(SUM() 公式?