Terraform's lifecycle meta-arguments directly affect deployment safety and observability — controlling change ordering, suppressing destructive changes, and triggering intentional replacements.
This article follows official documentation behavior, centers on the frequently-tested create_before_destroy, and is structured so you can quickly review both exam patterns (Associate/Pro) and real-world patterns.
lifecycle is a meta-argument that can be specified on each resource (and on modules in some recent Terraform versions) to control update strategy during Plan/Apply. The main ones are create_before_destroy (ordering control during replacement), prevent_destroy (preventing unintended deletion), ignore_changes (ignoring diffs on specific attributes), and replace_triggered_by (forcing replacement on external changes).
An important premise: Terraform builds plans based on a declarative dependency graph. lifecycle provides hints about ordering and replacement decisions on that graph, but the final behavior depends on each provider and remote API's constraints (for example, create_before_destroy may not work due to unique-name conflicts or quota limits). The exam frequently assumes this premise (provider/API-dependent constraints) as prerequisite knowledge.
Minimal example: Typical lifecycle specification
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [
# 例: 外部で上書きされやすいタグ
tags["owner"],
]
# replace_triggered_by は必要時のみ使う
}
}create_before_destroy takes effect on changes that cause resource replacement (new resource required). It shows as -/+ in the Plan and is effective when you want to switch from old to new. Use it when ensuring availability (zero-downtime switching) or when temporary dual-running is acceptable.
The caveat is that it assumes duplicate creation is allowed. If names are unique and conflict, the same IP/EIP can't be attached simultaneously, or quotas are reached, prior creation will fail. Standard workarounds include unique naming (random suffixes), careful attachment switching order, or isolating conflicting resources with a Blue/Green configuration.
create_before_destroy switching diagram
Old State New State (plan)
----------- -----------------
[instance v1] --create--> [instance v2]
| |
| traffic cutover | <-- attach / register
v v
--destroy--------------------> [instance v1 deleted]
時間軸: 作成 -> 接続/紐付け変更 -> 旧破棄Example: Zero-downtime replacement on AMI update (an AWS EC2 example)
resource "aws_instance" "app" {
ami = var.ami_id # AMI を更新すると置換が必要
instance_type = "t3.micro"
subnet_id = var.subnet_id
lifecycle {
create_before_destroy = true
}
# 固定名/固定 IP が衝突する場合は、切替を段階的に行う
# 例: EIP は新インスタンスへ付け替えてから旧を破棄
}
# 名前衝突が発生しうるリソースではサフィックスで緩和
resource "random_id" "name" { byte_length = 2 }
resource "aws_iam_role" "app" {
name = "app-role-${random_id.name.hex}"
assume_role_policy = data.aws_iam_policy_document.app.json
lifecycle { create_before_destroy = true }
}prevent_destroy errors out any plan containing Destroy and stops the apply. It's effective for preventing accidental deletion, but replacements (-/+) are also blocked because they involve Destroy. When deletion or replacement is truly necessary, the standard operational approach is to temporarily remove prevent_destroy before applying.
ignore_changes ignores diffs on specified attributes. It effectively reduces noise when external systems overwrite tags or specific settings, but in long-term operations, it preserves drift from the actual environment. When you want to later reconcile, remove ignore_changes and re-apply in a planned manner.
| Meta-argument | Main purpose | Behavior on Plan/Apply | Failure/caveat points |
|---|---|---|---|
| create_before_destroy | Create first, then delete during replacement | Resource is -/+ (new resource required). Order: create → switch → delete | Prior creation fails due to unique-name or fixed-resource conflicts, or quota limits |
| prevent_destroy | Prevent unintended deletion | Plans containing Destroy error out. -/+ is also blocked | If deletion/replacement is needed, temporarily release before applying |
| ignore_changes | Ignore diffs on specific attributes (tolerate drift) | Diffs on the attribute don't appear in Plan and aren't updated | Preserves long-term drift. Temporarily release to reconcile in the future |
| replace_triggered_by | Force replacement triggered by external changes | Updates to the reference target make the target -/+ | Risk of excessive replacement. Narrow the granularity of dependencies and replacements |
Example: Ignore tags, prevent accidental DB deletion
resource "aws_db_instance" "main" {
identifier = "app-db"
engine = "postgres"
# ... 省略 ...
lifecycle {
prevent_destroy = true
}
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.micro"
tags = {
owner = "platform-tooling" # 外部タグ付与と衝突しやすい例
}
lifecycle {
ignore_changes = [
tags["owner"],
]
}
}depends_on only adds resource creation ordering (an edge in the dependency graph) and does not force replacement. In contrast, replace_triggered_by replaces the target resource (-/+) when a referenced resource (or its attribute) changes. The former controls ordering; the latter adds a replacement trigger — that's the difference in roles.
In practice, replace_triggered_by is used to 'always make new' the dependent secret stores or settings when high-security materials like encryption keys, certificates, or KMS keys are updated. Combined with create_before_destroy, you can also bias the order during replacement toward safety.
Example: Force replacement of Secret on KMS key update
resource "aws_kms_key" "root" {
description = "root key for app secret"
}
resource "aws_secretsmanager_secret" "app" {
name = "app/secret"
kms_key_id = aws_kms_key.root.id
lifecycle {
# KMS キーが更新されたら Secret を新品にする
replace_triggered_by = [
aws_kms_key.root
]
create_before_destroy = true
}
}
# これに対し depends_on は順序を保証するだけで置換は起きない
# resource "..." "..." { depends_on = [aws_kms_key.root] }Blue/Green: Coexist by color-coding the actual resources, and only switch traffic. create_before_destroy allows dual-running inside each color's components (e.g., VM/ASG/TG attachments), and switching between colors uses DNS/ALB weighting to approach zero downtime.
Immutable: Always update via replacement, assuming AMI/image generation. Default to create_before_destroy and arrange for fixed resources (EIPs, unique names) to be switched last. Use random suffixes on unique names to avoid conflicts.
Conflict avoidance: For resources with strict unique names or limits, delegate state to another resource first (e.g., EIP attachment changes or Route53 switching) before deleting the old entity. Read where the Plan becomes -/+ and identify in advance whether the conflict is over names, bindings, or capacity.
Implementation hint (pseudo-code): Splitting Blue/Green switches
# 1) 実体は色付きで併存 (blue/green)。VM/ASG 側は create_before_destroy で安全に置換
resource "aws_autoscaling_group" "svc" {
# for_each = toset([var.active_color]) などで色を制御する設計も可
lifecycle { create_before_destroy = true }
}
# 2) トラフィックは後段で切替 (DNS/ALB target weight)
resource "aws_route53_record" "svc" {
# blue -> green に重みを寄せる。完了後に不要な色を縮退
}
# 3) 衝突資源(EIP/固定名)は最後に再アタッチ or rename 用の属性を用意
# ランダムサフィックスで同名衝突を避ける
resource "random_id" "suffix" { byte_length = 2 }
# name = "app-${random_id.suffix.hex}"Know Plan symbols and their meanings precisely. -/+ is replacement (new resource required). ~ is in-place change. + is new, - is deletion. Be able to explain that replacement order is ultimately determined by lifecycle and provider constraints.
prevent_destroy can't be disabled via CLI. Remove the setting before applying. replace_triggered_by has a different role from depends_on (replacement vs. ordering). ignore_changes comes with the cost of preserving drift.
Command examples: Basics of replacement/diff verification
# 置換を明示して適用(taint の代替)
terraform apply -replace=aws_instance.app
# Plan だけで挙動確認
terraform plan
# 注意: prevent_destroy は CLI で無視できない。設定を外してから apply するAssociate / Pro
問題 1
You want to replace a production EC2 instance with a new AMI. You aim for near-zero-downtime switching and need to avoid name conflicts. Which is the appropriate Terraform approach?
正解: A
To approach zero downtime, aim for the create-new → switch → delete-old order via create_before_destroy. To avoid name conflicts, make names unique with random suffixes or similar. prevent_destroy blocks deletion and prevents the replacement itself. ignore_changes ignores the diff and won't update. depends_on only controls ordering and doesn't force replacement.
When does create_before_destroy fail to work?
It fails when no replacement is needed (in-place update), or when provider/API constraints prevent duplicate creation (globally unique names, attach conflicts, quota limits). Solutions include adopting unique names, splitting the switch operation (change attachment first, then destroy the old resource), and isolating with Blue/Green deployments.
How do I later reconcile drift on attributes ignored by ignore_changes?
Temporarily remove the attribute from ignore_changes, then plan/apply to converge to the desired value. If forced replacement is needed, use terraform apply -replace=... alongside it.
How do I delete a resource that has prevent_destroy set?
Remove prevent_destroy (or set it to false), then plan/apply again. There is no CLI option to bypass it. Since the purpose is to prevent accidental deletion, an explicit configuration change is required.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...