TerraformでDNSレコード,ACM証明書,ALBをプロビジョニングする際に入れておいたほうが良いコード

2022.02.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

状況

ALBがフロントにあるWebアプリケーションです。https通信のためにACM証明書を作りALBにアタッチします。またクライアントがアクセスするFQDNに対応するDNSレコードはRoute53ホストゾーンに作成します。これらをすべてTerraformでプロビジョニングします。

dnsrecord-acm-alb

こういった状況において、Terraformのコードに2点加えておいたほうがいいものがあることがわかりましたのでレポートします。

なお、ベースとなるコードは以下のエントリのものです。

TerraformとAWS Providerのバージョンは以下です。

  • Terraform: 0.14.9
  • AWS Provider: 3.31.0

1. aws_acm_certificateにcreate_before_destroyを入れる

以下のように、aws_acm_certificateリソースにlifecycleブロックを作ってcreate_before_destroy = trueを書きます。

resource "aws_acm_certificate" sample" {
  domain_name       = data.aws_ssm_parameter.fqdn.value
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

これを入れておかないと、FQDN値を変更した際に問題になりました。
DNSレコードを変更してterraform applyすると、aws_acm_certificateの再作成(削除→作成)が走ります。ですがその削除がいつまで立っても終わりませんでした。

terraform apply出力の抜粋

module.base.aws_route53_record.hostname: Creating...
module.base.aws_route53_record.cert_validation["hoge.example.com"]: Destruction complete after 31s
module.base.aws_acm_certificate.sample: Destroying... [id=arn:aws:acm:ap-northeast-1:123456789012:certificate/ac0c0614-1f1e-490b-9748-eb6491c4c922]
module.base.aws_route53_record.hostname: Still creating... [10s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 10s elapsed]
module.base.aws_route53_record.hostname: Still creating... [20s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 20s elapsed]
module.base.aws_route53_record.hostname: Still creating... [30s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 30s elapsed]
module.base.aws_route53_record.hostname: Creation complete after 31s [id=Z0848872OXCSAFSLG4OP_hoge.example.com_A]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 40s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 50s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m0s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m10s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m20s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m30s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m40s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 1m50s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m0s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m10s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m20s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m30s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m40s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 2m50s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 3m0s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 3m10s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 3m20s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 3m30s elapsed]
module.base.aws_acm_certificate.sample: Still destroying... [id=arn:aws:acm:ap-northeast-1:123456789012...e/ac0c0614-1f1e-490b-9748-eb6491c4c922, 3m40s elapsed]
(ずっと続いたので中断しました)

削除が完了しなかった理由ですが、該当aws_acm_certificateaws_alb_listenerにて参照されている、つまりACM証明書がALBにアタッチされている状態なので、削除できなかった、という事だと考えられます。

そこで、create_before_destroy = trueを入れることで、

  1. 新しいFQDNのACM証明書が作成される
  2. ALBにアタッチされるACM証明書が旧から新に変わる
  3. 旧ACM証明書が削除される

という実行順序になり、3の時点では旧ACM証明書はALBへのアタッチが解除済なので削除可能になります。

2. aws_alb_listenerにdepends_onを入れる

aws_alb_listenerdepends_onargumentを指定して、値にaws_acm_certificate_validationを入れます。

resource "aws_alb_listener" "https" {
  load_balancer_arn = aws_alb.sample.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"
  certificate_arn   = aws_acm_certificate.sample.arn
  default_action {
    type             = "forward"
    target_group_arn = aws_alb_target_group.sample.arn
  }

  depends_on = [
    aws_acm_certificate_validation.sample
  ]
}

これを入れておかないと、これもFQDN値を変更した際にエラーになりました。

Error: Error modifying LB Listener: UnsupportedCertificate: The certificate 'arn:aws:acm:ap-northeast-1:123456789012:certificate/a5051cc8-d500-4eca-981c-b7fbc9bf4234' must have a fully-qualified domain name, a supported signature, and a supported key size.
    status code: 400, request id: 4d70d132-7224-4ef8-b26e-bd7511244f20

  on ../../modules/base/alb.tf line 28, in resource "aws_alb_listener" "https":
  28: resource "aws_alb_listener" "https" {

aws_acm_certificate_validationは、作ったACM証明書と、そのvalidation用に作ったDNSレコードを紐付けるリソースです。(前提としてACM証明書の検証方法としてDNS検証を選択しています。)検証完了=このリソース作成完了になります。

depends_onを入れておかないと、aws_acm_certificate_validation 再作成完了前にaws_alb_listenerの変更、つまりACM証明書の旧→新への付け直しが始まっています。検証完了前のACM証明書がALBに紐付けられることになります。そのためError modifying LB Listener: UnsupportedCertificate:というエラーが出たと考えられます。

terraform apply出力の抜粋

module.base.aws_alb_listener.https: Modifying... [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/sample-alb/15bc65f1258f01da/740d21f9c91f2e8f]
module.base.aws_route53_record.hostname: Still destroying... [id=Z0848872OXCSAFSLG4OP_hoge.example.com_A, 10s elapsed]
module.base.aws_route53_record.cert_validation["hoge.example.com"]: Still destroying... [id=Z0848872OXCSAFSLG4OP__86b98a079a13baf96...7b.hoge.example.com._CNAME, 10s elapsed]
module.base.aws_route53_record.cert_validation["fuga.example.com"]: Still creating... [10s elapsed]
module.base.aws_route53_record.hostname: Still destroying... [id=Z0848872OXCSAFSLG4OP_hoge.example.com_A, 20s elapsed]
module.base.aws_route53_record.cert_validation["hoge.example.com"]: Still destroying... [id=Z0848872OXCSAFSLG4OP__86b98a079a13baf96...7b.hoge.example.com._CNAME, 20s elapsed]
module.base.aws_route53_record.cert_validation["fuga.example.com"]: Still creating... [20s elapsed]
module.base.aws_route53_record.hostname: Still destroying... [id=Z0848872OXCSAFSLG4OP_hoge.example.com_A, 30s elapsed]
module.base.aws_route53_record.cert_validation["hoge.example.com"]: Still destroying... [id=Z0848872OXCSAFSLG4OP__86b98a079a13baf96...7b.hoge.example.com._CNAME, 30s elapsed]
module.base.aws_route53_record.hostname: Destruction complete after 31s
module.base.aws_route53_record.hostname: Creating...
module.base.aws_route53_record.cert_validation["hoge.example.com"]: Destruction complete after 30s
module.base.aws_route53_record.cert_validation["fuga.example.com"]: Still creating... [30s elapsed]
module.base.aws_route53_record.cert_validation["fuga.example.com"]: Creation complete after 31s [id=Z0848872OXCSAFSLG4OP__e0dc1d1c745acf92b971a7253f915766.fuga.example.com._CNAME]
module.base.aws_acm_certificate_validation.sample: Creating...
module.base.aws_route53_record.hostname: Still creating... [10s elapsed]
module.base.aws_acm_certificate_validation.sample: Creation complete after 2s [id=2022-02-03 09:15:27.466 +0000 UTC]
module.base.aws_route53_record.hostname: Still creating... [20s elapsed]
module.base.aws_route53_record.hostname: Still creating... [30s elapsed]
module.base.aws_route53_record.hostname: Creation complete after 30s [id=Z0848872OXCSAFSLG4OP_fuga.example.com_A]

Error: Error modifying LB Listener: UnsupportedCertificate: The certificate 'arn:aws:acm:ap-northeast-1:123456789012:certificate/a5051cc8-d500-4eca-981c-b7fbc9bf4234' must have a fully-qualified domain name, a supported signature, and a supported key size.
    status code: 400, request id: 4d70d132-7224-4ef8-b26e-bd7511244f20

  on ../../modules/base/alb.tf line 28, in resource "aws_alb_listener" "https":
  28: resource "aws_alb_listener" "https" {

depends_onを入れると、aws_acm_certificate_validation 再作成完了を待ってからaws_alb_listenerの変更=ACM証明書の旧→新への付け直しが始まるので、前述のエラーを回避できます。

terraform apply出力の抜粋

module.base.aws_route53_record.cert_validation["fuga.example.com"]: Still creating... [30s elapsed]
module.base.aws_route53_record.cert_validation["fuga.example.com"]: Creation complete after 31s [id=Z0848872OXCSAFSLG4OP__86b98a079a13baf96eca4c9b185a497b.fuga.example.com._CNAME]
module.base.aws_acm_certificate_validation.sample: Creating...
module.base.aws_route53_record.hostname: Still creating... [10s elapsed]
module.base.aws_acm_certificate_validation.sample: Creation complete after 2s [id=2022-02-03 09:51:53.476 +0000 UTC]
module.base.aws_alb_listener.https: Modifying... [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/sample-alb/15bc65f1258f01da/740d21f9c91f2e8f]
module.base.aws_alb_listener.https: Modifications complete after 1s [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/sample-alb/15bc65f1258f01da/740d21f9c91f2e8f]
module.base.aws_acm_certificate.sample: Destroying... [id=arn:aws:acm:ap-northeast-1:123456789012:certificate/15051cc8-d500-4eca-981c-b7fbc9bf4234]
module.base.aws_acm_certificate.sample: Destroying... [id=arn:aws:acm:ap-northeast-1:123456789012:certificate/84181cfd-7c19-424f-a6fe-0a9fa21881e1]

今回ALBのケースしか検証していませんが、CloudFrontにACM証明書を紐付ける場合も同様のことが起きるのではないかと思います。