【Terraform】ACMでSSL証明書を作成しDNS認証するときに遭遇したエラーたち
Terraformを使ってACM で SSL 証明書を発行しようとした際にいくつかエラーにはまったので、記事にしておきます。
ゴール
- Terraform を使って ACM で SSL 証明書を発行する
- 認証方法は Route53 を使用した DNS 認証
現状
現在の tf ファイルの中身はこんな感じ。
# ホストゾーンの参照
data "aws_route53_zone" "host_zone" {
# 便宜上、"example.com"としておきます
name = "example.com"
}
# ACMのDNS検証用レコード
resource "aws_route53_record" "cert" {
name = aws_acm_certificate.cert.domain_validation_options[0].resource_record_name
type = aws_acm_certificate.cert.domain_validation_options[0].resource_record_type
records = aws_acm_certificate.cert.domain_validation_options[0].resource_record_value
zone_id = data.aws_route53_zone.host_zone.zone_id
ttl = 60
}
# ACM証明書を定義
resource "aws_acm_certificate" "cert" {
# 便宜上、"www.example.com"としておきます
domain_name = "www.example.com"
validation_method = "DNS"
provider = aws.virginia
lifecycle {
create_before_destroy = true
}
}
# レコードのチェック
resource "aws_acm_certificate_validation" "cert" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = [ aws_route53_record.cert.fqdn ]
provider = aws.virginia
}
※Cloudfront で使用するためバージニア北部で SSL 証明書を作成しています。
このまま terraform plan をすると、エラーをはきます。
エラー ①:DNS レコードの定義でエラー
エラーの内容確認
$ terraform plan
╷
│ Error: Invalid index
│
│ on route53.tf line 22, in resource "aws_route53_record" "cert":
│ 22: name = aws_acm_certificate.cert.domain_validation_options[0].resource_record_name
│
│ Elements of a set are identified only by their value and don't have any separate index or key to select with, so it's only possible to perform operations across all elements of
│ the set.
╵
╷
│ Error: Invalid index
│
│ on route53.tf line 23, in resource "aws_route53_record" "cert":
│ 23: type = aws_acm_certificate.cert.domain_validation_options[0].resource_record_type
│
│ Elements of a set are identified only by their value and don't have any separate index or key to select with, so it's only possible to perform operations across all elements of
│ the set.
╵
╷
│ Error: Invalid index
│
│ on route53.tf line 24, in resource "aws_route53_record" "cert":
│ 24: records = aws_acm_certificate.cert.domain_validation_options[0].resource_record_value
│
│ Elements of a set are identified only by their value and don't have any separate index or key to select with, so it's only possible to perform operations across all elements of
│ the set.
インデックス系のエラーがでてますね。
DeepL でエラー文を翻訳してみると、、、
セットの要素は、その値によってのみ識別され、選択するための個別のインデックスやキーを持たないため、セットのすべての要素に対してのみ操作を行うことが可能です。
うーーん、いまいちよくわからない。。。
原因調査
エラー文で検索したところ、以下の記事を発見しました。
aws provider 3.0.0 以降は domain_validation_options が Set 型で返ってくるようになりました。
それ以前はList 型だったのでインデックスを指定できたのですが、 Set 型になり順序付けされなくなったため、インデックスを指定不可となったようです。
コード修正
下記のように aws_route53_record のコードを修正しました。
# aws_route53_record のみ抜粋
# ACMのDNS検証用レコード
resource "aws_route53_record" "cert" {
for_each = {
for dvo in aws_acm_certificate.cert.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
type = each.value.type
records = [ each.value.record ]
zone_id = data.aws_route53_zone.host_zone.zone_id
ttl = 60
}
for 文で map 型へ変換して、それを name, type, records に設定しています。
詳しい解説は以下をご覧ください
これで、terraform plan をしてみると、、、まだエラーが出ています。。。
エラー ②:検証待ちの FQDN 指定でエラー
エラーの内容確認
│ Error: Missing resource instance key
│
│ on acm.tf line 15, in resource "aws_acm_certificate_validation" "cert":
│ 15: validation_record_fqdns = [ aws_route53_record.cert.fqdn ]
│
│ Because aws_route53_record.cert has "for_each" set, its attributes must be accessed on specific instances.
│
│ For example, to correlate with indices of a referring resource, use:
│ aws_route53_record.cert[each.key]
DNS 検証用のレコードを指定している部分(validation_record_fqdns)でエラーになっています
どうやら aws_route53_record.cert も Set 型で返ってきているみたいです。
コード修正
# aws_acm_certificate_validation のみ抜粋
# レコードのチェック
resource "aws_acm_certificate_validation" "cert" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = flatten([ values(aws_route53_record.cert)[*].fqdn ])
provider = aws.virginia
}
values 関数を使用して、aws_route53_record.cert を Set→List へ変換しています。
そして、Splat 演算子([*])で aws_route53_record.cert 内のすべての要素から fqdn を取り出しています。
それらをflatten 関数で囲むことで取り出した fqdn をリスト内に並べています。
これで無事 plan, apply 両方通るコードになりました!
修正後のコード
最後に、修正後のコード全体を載せておきます
# ホストゾーンの参照
data "aws_route53_zone" "host_zone" {
# 便宜上、"example.com"としておきます
name = "example.com"
}
# ACMのDNS検証用レコード
resource "aws_route53_record" "cert" {
for_each = {
for dvo in aws_acm_certificate.cert.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
type = each.value.type
records = [ each.value.record ]
zone_id = data.aws_route53_zone.host_zone.zone_id
ttl = 60
}
# ACM証明書を定義
resource "aws_acm_certificate" "cert" {
# 便宜上、"www.example.com"としておきます
domain_name = "www.example.com"
validation_method = "DNS"
provider = aws.virginia
lifecycle {
create_before_destroy = true
}
}
# レコードのチェック
resource "aws_acm_certificate_validation" "cert" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = flatten([ values(aws_route53_record.cert)[*].fqdn ])
provider = aws.virginia
}
参考文献
Discussion