Terraformのcreate_before_destroyでゼロダウンタイム更新する方法|ALBリスナールール・ACM証明書切り替えとlifecycle設計の実践

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Terraform > Terraformのcreate_before_destroyでゼロダウンタイム更新する方法|ALBリスナールール・ACM証明書切り替えとlifecycle設計の実践
「terraform applyを実行したら、一瞬サービスが落ちた」
特定のリソースを更新するとき、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設計パターンも解説


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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を設定すると、この挙動が「+/-」(作成してから削除)に変わる。

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 } }

この設定を加えると、Terraformは更新時に「新リソースを作成 → 依存先の切り替え → 旧リソースを削除」の順で動く。

重要な仕様として、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 } }

terraform applyを実行すると次のような出力になる。

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.

「deposed object」と表示されているのが旧リソースの削除フェーズだ。新リソースの作成と検証が完了した後で削除が走っているのが確認できる。

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リスナー自体にcreate_before_destroyを持たせることで、新しいリスナー(新証明書)が443ポートで受け付け可能になってから、旧リスナーが削除される。

ただし、同一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 } }

aws_acm_certificate_validation.mainは「証明書発行の完了を待つ」ためのリソースであり、返すARNはaws_acm_certificate.main.arnと同一の値だ。参照元を直接certificateリソースに変えても、値は変わらない。循環の輪から抜け出すための「参照の付け替え」が有効な理由はここにある。

変更後に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と組み合わせると連動再作成もダウンタイムゼロで行える
create_before_destroyとprevent_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を用意する
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Terraformのlifecycle設計やIaCの実践ノウハウをまとめたTerraform実践講座もぜひご覧ください。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。