Terraform 1.2以降で追加されたlifecycle.replace_triggered_byは、他のリソースやモジュール出力の変化をトリガーに、対象リソースを明示的に置換(再作成)させるためのメタ引数です。通常は属性の差分に基づいて更新/置換が決まりますが、初期化時のみ参照される設定や、外部依存の安全性を担保したい場合に有効です。
この記事では、replace_triggered_byの正しい使い所と落とし穴、他機能との比較、試験で問われやすい観点、実務での検証パターンをまとめます。前提としてTerraform言語仕様とlifecycleの基本を把握している読者を想定します。
replace_triggered_byは、指定した参照(リソース全体やその属性、モジュール出力など)の値が変わった場合、当該リソースを更新ではなく置換として計画させます。これにより、プロバイダがインプレース更新をサポートしていても、意図的に作り直す設計が可能になります。
典型例は「初回作成時にのみ外部設定を取り込むコンポーネント」や「証明書・イメージのようにハッシュが変わったら確実に入れ替えたいリソース」です。参照先をリソース全体で指定すれば“そのリソースのいかなる差分でも”置換、属性で指定すれば“その属性値が変わったときのみ”置換が走ります。
| 機能 | 主目的 | トリガー対象/指定方法 | Planへの影響 |
|---|---|---|---|
| lifecycle.replace_triggered_by | 特定の外部変化で置換を強制 | リソース/属性/モジュール出力参照 | 指定参照が変化すると必ず置換(must be replaced) |
| depends_on | 作成/破棄の順序制御 | リソース/モジュール参照 | 差分には影響せず、依存グラフのみ変更 |
| null_resource.triggers | 値の変化でnull_resourceを再作成 | 任意の値(文字列/マップ) | null_resourceのみ再作成。他リソースには非適用 |
| lifecycle.ignore_changes | 特定属性の差分を無視 | 属性名(セット/リスト可) | 差分を抑止。置換/更新が計画されない(他要因の置換は別途発生し得る) |
replace_triggered_byの依存と置換の流れ
基本パターン: モジュール出力や属性の変化で置換を強制する
# モジュール(例): 証明書バンドルのハッシュを出力
module "ca" {
source = "./modules/ca" # 実体は任意
# ...
}
# 例1: モジュール出力でSecretを常に作り直す
resource "kubernetes_secret" "api" {
metadata {
name = "api-secret"
namespace = "default"
}
data = {
# 実際の内容は別の引数で参照していてもOK
ca_hash = module.ca.bundle_hash
}
lifecycle {
replace_triggered_by = [
module.ca.bundle_hash
]
}
}
# 例2: リソース属性の変化で置換
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.main.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = aws_acm_certificate.cert.arn
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
status_code = 200
message_body = "ok"
}
}
lifecycle {
replace_triggered_by = [
aws_acm_certificate.cert.arn # 証明書差し替え時はリスナーを置換
]
}
}replace_triggered_byは、Planの差分計算後に「トリガ参照の値が前回Stateから変わっているか」を確認し、該当すれば対象リソースに置換フラグを付与します。したがって、参照先がRefreshで更新されなければ変化として扱われません。
リソース全体を参照した場合は“当該インスタンスのいかなる計画変更でも”トリガ成立、属性参照であれば“その属性の値変化”に限定されます。count/for_eachの場合、各インスタンスで個別に評価されるため、変化があったインスタンスのみ置換されます。
for_eachと組み合わせた評価の最小化
# すべてのVMはベースイメージのダイジェストが変わったときだけ置換
locals {
vms = {
web = { size = "small" }
api = { size = "medium" }
}
}
module "base_image" {
source = "./modules/base_image"
# 出力: digest
}
resource "azurerm_linux_virtual_machine" "vm" {
for_each = local.vms
name = "${each.key}-vm"
resource_group_name = azurerm_resource_group.rg.name
size = each.value.size
# ... 他の必須引数
lifecycle {
replace_triggered_by = [
module.base_image.digest
]
}
}証明書やイメージのように、値の“中身”が変わるが参照名は同一のケースでは、プロバイダがインプレース更新を行っても安全側に倒すため置換を明示することがあります。特に初期化スクリプトで外部リソースを一度だけ読み込む系は再作成が無難です。
また、コンテナ画像のダイジェスト変更時にDeploymentのローリングを強制したいが、providerの挙動や差分検出で取りこぼしがある環境では、replace_triggered_byで確実に入れ替える設計が有効です。
コンテナ画像ダイジェストの変更でDeploymentを置換(例)
module "artifact" {
source = "./modules/artifact"
# output: image_digest
}
resource "kubernetes_deployment" "app" {
metadata { name = "app" }
spec {
replicas = 3
selector { match_labels = { app = "app" } }
template {
metadata { labels = { app = "app" } }
spec {
container {
name = "app"
image = "example.com/app@${module.artifact.image_digest}"
}
}
}
}
lifecycle {
replace_triggered_by = [
module.artifact.image_digest
]
}
}replace_triggered_byの指定が粗すぎると、ほぼ毎回置換されPlanが巨大化します。逆に細かすぎると期待どおり置換されません。参照粒度の設計が肝心です。また、時間ベースや乱数値のように“意図せず値が更新される”ものを参照すると、意図しない置換ループになります。
ignore_changesと併用する場合、ignore_changesで差分を無視していても、replace_triggered_byが成立すれば置換は発生します。差分抑制より“置換の意図”が優先される点に注意してください。
アンチパターン例: 周期的な値を参照してしまう
# 悪い例: time_rotatingは一定間隔で値が変わるため、定期的に置換が走る
resource "time_rotating" "weekly" {
rotation_days = 7
}
resource "some_resource" "target" {
# ...
lifecycle {
replace_triggered_by = [
time_rotating.weekly.id # 値がローテーションするたび置換されてしまう
]
}
}従来、外部値の変化に伴う再作成はnull_resourceのtriggersで代替することが多く、これに依存する別リソースの再適用を狙う設計が散見されました。しかし、この方法は意図が間接的で、依存グラフも読みにくくなります。
replace_triggered_byへ移行すると、“どの値の変化でどのリソースを置換するか”がコード上で直に表現でき、Planも読みやすくなります。順序制御が必要ならdepends_onを併用します。
移行例: null_resourceのtriggersからlifecycle.replace_triggered_byへ
# 旧: null_resourceを疑似トリガに利用
resource "null_resource" "trigger" {
triggers = {
ca_hash = module.ca.bundle_hash
}
}
# 旧: 実リソースはtriggerに依存
resource "kubernetes_secret" "api" {
# ...
depends_on = [null_resource.trigger]
}
# 新: 実リソース自身にトリガを集約
resource "kubernetes_secret" "api" {
# ...
lifecycle {
replace_triggered_by = [module.ca.bundle_hash]
}
# 順序が要るならdepends_onを追加
# depends_on = [module.ca]
}試験では、replace_triggered_byとdepends_on/ignore_changesの違い、リソース参照と属性参照の粒度、count/for_each時の評価単位が問われやすいです。null_resource.triggersとの比較も頻出です。
実務検証は、小さなサンプルで“参照が変わる/変わらない”の両方を試し、Plan出力にmust be replacedが現れることを確認します。RefreshOnlyでの挙動確認も有効です。
手元検証コマンド例
# 初回
terraform init
terraform plan -out=tfplan
terraform apply tfplan
# 参照値を更新(例: module出力を変化させるコミット/バージョン変更)
# 置換の発生を確認
terraform plan | grep -A2 "must be replaced" || true
# 差分がなくなることの確認
terraform plan -refresh-onlyPro
問題 1
モジュールmodule.caが証明書バンドルのハッシュ(bundle_hash)を出力しており、kubernetes_secret.apiはこの値が変わったときに必ず作り直したい。正しいTerraform構成はどれか。
正解: A
置換の意図を直接表現するにはlifecycle.replace_triggered_byが正解。depends_onは順序のみで置換は強制しない。null_resource+triggersは間接的かつ冗長。ignore_changesは差分を無視するだけで置換を強制しない。
replace_triggered_byに指定できるのは何ですか?変数やデータソースも使えますか?
主にリソース参照、リソース属性参照、モジュール出力など、Plan時に値変化を検出できる参照が対象です。変数のような静的値だけを指定しても“外部の変化”としては扱いにくく、設計上の明確さも損ないます。実務ではリソース/属性/モジュール出力に限定して設計するのが安全です。
ignore_changesと併用した場合、どちらが優先されますか?
ignore_changesは特定属性の差分を無視しますが、replace_triggered_byが成立するとリソースは置換されます。つまり、差分抑制より“置換の意図”が優先されると考えてください。
参照先がドリフトしたときも置換されますか?
PlanのRefreshで参照先の現在値がStateに反映され、その結果として“値が変わった”と認識できれば置換が計画されます。裏を返せば、参照先の変化をPlanが検出できない限り、置換は発生しません。定期的なplan/refreshと監視が重要です。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...