特定のリソースを更新するとき、Terraformはデフォルトで「削除→再作成」の順番で動く。ALBリスナーやACM証明書のように、削除した瞬間に通信が断たれるリソースでは、この挙動がダウンタイムに直結する。
この記事では、create_before_destroyを使って更新時のダウンタイムをゼロにする方法を解説する。lifecycleブロックの設定手順から、ACM証明書・ALBリスナーへの実践適用、循環依存(Cycle error)の回避方法まで順を追って説明する。
実行環境はTerraform 1.6以降、awsプロバイダー5.x系を前提としている。
この記事のポイント
・create_before_destroyで新リソースを先に作り、切り替え後に旧リソースを削除できる
・ACM証明書・ALBリスナーなど削除順序が危険なリソースに必須の設定
・依存関係の循環エラー(Cycle error)は参照を切ることで解決できる
・ignore_changesやprevent_destroyと組み合わせた安全なlifecycle設計パターンも解説
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Terraformがデフォルトで「削除→再作成」する理由
Terraformがリソースを更新するとき、変更の性質によって3通りの挙動をとる。・in-place update:リソースを停止せずに属性だけ変更する。タグの変更などがこれに当たる。
・destroy then create(デフォルト):既存リソースを削除してから新しいリソースを作る。
・create before destroy:新しいリソースを作ってから既存リソースを削除する。
「destroy then create」がデフォルトである理由は、多くのリソースでは「同一の識別子を持つリソースが同時に存在できない」という制約があるためだ。既存リソースを先に削除しなければ、新リソースの作成が名前の衝突エラーで失敗する。
ところがACM証明書やALBリスナーでは、この「削除→作成」の順序がダウンタイムを生む。証明書が削除された瞬間にHTTPS通信が失敗し始めるからだ。
terraform planを実行すると、デフォルトの挙動では次のように表示される。
# aws_acm_certificate.main must be replaced -/+ resource "aws_acm_certificate" "main" { ~ domain_name = "old.example.com" -> "new.example.com" # forces replacement ... } Plan: 1 to add, 0 to change, 1 to destroy.
create_before_destroyの設定方法
1. lifecycleブロックの基本構文
リソースブロック内にlifecycleブロックを追加し、create_before_destroy = trueを設定する。
resource "aws_acm_certificate" "main" { domain_name = "example.com" validation_method = "DNS" lifecycle { create_before_destroy = true } }
重要な仕様として、create_before_destroyは当該リソースに依存するすべてのリソースにも自動で伝播する。設定の影響範囲を意識しておく必要がある。
2. ACM証明書の更新に適用する
ACM証明書のドメイン変更はin-place updateが効かず、destroy then createが発動する。この挙動を制御するのがcreate_before_destroyだ。次の構成を確認してほしい。証明書・DNS検証レコード・validationリソースの3つすべてにcreate_before_destroyを設定する。
# ACM証明書 resource "aws_acm_certificate" "main" { domain_name = var.domain_name subject_alternative_names = var.san_domains validation_method = "DNS" lifecycle { create_before_destroy = true } } # DNS検証レコード resource "aws_route53_record" "cert_validation" { for_each = { for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = data.aws_route53_zone.main.zone_id lifecycle { create_before_destroy = true } } # 証明書の発行完了を待つ resource "aws_acm_certificate_validation" "main" { certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] lifecycle { create_before_destroy = true } }
Plan: 3 to add, 0 to change, 3 to destroy. aws_acm_certificate.main: Creating... aws_acm_certificate.main: Creation complete after 2s [id=arn:aws:acm:ap-northeast-1:123456789012:certificate/abc12345-1234-1234-1234-abcdef123456] aws_route53_record.cert_validation["new.example.com"]: Creating... aws_route53_record.cert_validation["new.example.com"]: Creation complete after 21s aws_acm_certificate_validation.main: Creating... aws_acm_certificate_validation.main: Still creating... [1m0s elapsed] aws_acm_certificate_validation.main: Creation complete after 3m22s aws_acm_certificate.main (deposed object): Destroying... aws_acm_certificate.main (deposed object): Destruction complete after 1s Apply complete! Resources: 3 added, 0 changed, 3 destroyed.
3. ALBリスナーの証明書切り替えに適用する
ALBのHTTPSリスナーに別のACM証明書を紐づけるケースを考えてみる。aws_lb_listenerはcertificate_arnが変わると再作成が走る。resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.main.arn port = "443" protocol = "HTTPS" # 証明書切り替え時のダウンタイムを防ぐ certificate_arn = aws_acm_certificate.main.arn default_action { type = "forward" target_group_arn = aws_lb_target_group.main.arn } lifecycle { create_before_destroy = true } }
ただし、同一ALBの同一ポートに2つのリスナーを同時に作成しようとすると、AWSのAPI側で「A listener already exists on this port」エラーになる場合がある。このケースは後述のCycle errorのセクションで対処法を説明する。
依存関係の循環エラー(Cycle error)を回避する
1. なぜCycle errorが発生するか
create_before_destroyを設定したときに最も詰まりやすいエラーが「Cycle error」だ。Error: Cycle: aws_acm_certificate_validation.main, aws_lb_listener.https (destroy), aws_acm_certificate.main (expand)
・aws_lb_listener.httpsが
aws_acm_certificate_validation.main.certificate_arnを参照している・aws_acm_certificate_validation.mainはaws_acm_certificate.mainに依存している
・aws_acm_certificate.mainはcreate_before_destroyを持つ
・Terraformは「aws_lb_listenerを先に削除してから旧証明書を削除したい」と「証明書validation完了まで待ちたい」を同時に解決できず、循環に陥る。
2. 依存参照を切ることで解決する
解決策は、aws_lb_listenerのcertificate_arnの参照元をaws_acm_certificate_validation.mainではなく、aws_acm_certificate.mainに変更することだ。resource "aws_lb_listener" "https" { load_balancer_arn = aws_lb.main.arn port = "443" protocol = "HTTPS" # aws_acm_certificate_validation.main.certificate_arn ではなく # aws_acm_certificate.main.arn を直接参照して循環を断ち切る certificate_arn = aws_acm_certificate.main.arn default_action { type = "forward" target_group_arn = aws_lb_target_group.main.arn } lifecycle { create_before_destroy = true } }
変更後にterraform planを実行して循環が解消されていることを確認する。
# Cycle errorが解消されてplanが正常に通った例 Plan: 4 to add, 0 to change, 4 to destroy.
まとめて覚えるlifecycle設計のパターン
create_before_destroyはlifecycleブロック内の他の設定と組み合わせることが多い。よく使う組み合わせパターンを整理する。| 設定 | 主な用途 | create_before_destroyとの組み合わせ |
|---|---|---|
prevent_destroy = true |
本番RDS・S3の誤削除防止 | 組み合わせ可。旧リソース削除フェーズでもprevent_destroyが働くため、切り替え完了後に外す必要あり |
ignore_changes = [tags] |
コンソール手動更新したタグを無視 | 組み合わせ可。再作成を避けながら特定属性の差分を無視できる |
replace_triggered_by |
別リソース変更時に連動して再作成 | Terraform 1.2以降対応。create_before_destroyと組み合わせると連動再作成もダウンタイムゼロで行える |
prevent_destroyはリソースの「破棄」そのものをブロックする。create_before_destroyの旧リソース削除フェーズでも働くため、旧リソースにprevent_destroyが残ったままだとapplyが「旧リソースを削除できない」で止まる。新旧切り替えが完了したら、旧リソースのprevent_destroyは外してから再度destroyするか、terraform state rmで管理外にしてAWS側で手動削除するかを判断する必要がある。
また、lifecycleブロック内の値にvariableやlocalsを直接書くことはTerraformで対応していない点も覚えておきたい。Meta-argumentはexpression評価の外側で処理されるため、static valueのみ受け付ける。本番・ステージング・開発でcreate_before_destroyのオン・オフを切り替えたい場合は、環境ごとに別moduleを用意するか、lifecycle設定を持つmoduleを環境別に呼び分ける設計にする。
lifecycle設計の細かな判断軸について体系的に身につけたい方は、Terraformの設計実践も参考にしてほしい。
本記事のまとめ
| やりたいこと | 設定・方法 |
|---|---|
| 更新時のダウンタイムをゼロにする | lifecycle { create_before_destroy = true } |
| ACM証明書更新を安全に行う | 証明書・DNS検証レコード・validationの3リソースに設定 |
| ALBリスナーの証明書を切り替える | リスナーにも設定し、ARN参照は直接certificate.arnを参照 |
| Cycle errorを解消する | aws_acm_certificate_validation.mainへの参照をcertificate.arnに付け替える |
| lifecycle値を環境で切り替えたい | Meta-argumentはstatic valueのみ。環境ごとに別moduleを用意する |
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます