1. 概要
- 「意図しない置換(replace)」とは
- planで
-/+(destroy then create)が表示されるケース - 主な原因(ForceNew属性の変更・identifierの変更)
- 対処法(
ignore_changes・create_before_destroy・importによる回避)
terraform planで-/+(destroy then create)が表示されたとき、意図しない場合はリソースの停止・データ消失につながります。原因を理解して正しく対処する必要があります。
2. planで-/+が表示される例
Terraform will perform the following actions:
# aws_instance.web must be replaced
-/+ resource "aws_instance" "web" {
~ id = "i-0123456789abcdef0" -> (known after apply)
-/+ ami = "ami-old" -> "ami-new" # forces replacement
...
}
# forces replacementというコメントがどの属性変更が置換を引き起こすかを示しています。
3. よくある原因
原因1: AMI IDの変更
terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
variable "environment" {
description = "環境名"
type = string
default = "dev"
}
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
resource "aws_instance" "web" {
# most_recent = true のAMIはApplyのたびに新しいIDになる場合がある
# → 新しいAMI IDに変わると EC2インスタンスが置換される
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
lifecycle {
# ✅ AMIの変更による置換を無視する(既存インスタンスを維持)
ignore_changes = [ami]
}
tags = {
Name = "${var.environment}-web"
Environment = var.environment
ManagedBy = "terraform"
}
}
原因2: RDSのidentifier変更
# ❌ identifierを変更するとRDSが置換される(データ消失リスク)
resource "aws_db_instance" "main" {
identifier = "${var.environment}-postgres-v2" # 変更前: -v1
engine = "postgres"
engine_version = "16"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_encrypted = true
db_name = "myapp"
username = "admin"
password = "CHANGE_ME"
skip_final_snapshot = true
tags = {
Name = "${var.environment}-main-db"
Environment = var.environment
ManagedBy = "terraform"
}
}
4. 対処法1: ignore_changes で変更を無視
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
lifecycle {
# AMIの変更・タグ変更による置換を防ぐ
ignore_changes = [
ami,
tags["LastDeployedAt"],
]
}
tags = {
Name = "${var.environment}-web"
Environment = var.environment
ManagedBy = "terraform"
}
}
5. 対処法2: create_before_destroy で無停止置換
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
lifecycle {
# 置換が必要な場合: 先に新しいインスタンスを作り、その後古いのを削除
create_before_destroy = true
}
tags = {
Name = "${var.environment}-web"
Environment = var.environment
ManagedBy = "terraform"
}
}
6. 対処法3: replace/taint を使った意図的な置換
# 特定のリソースを意図的に置換する場合
terraform apply -replace=aws_instance.web
# ❌ terraform taint は非推奨(-replace を使う)
7. 関連記事
- lifecycle メタ引数 — ignore_changes・create_before_destroy
- replace_triggered_by — 意図的な置換トリガー
- err_ignore_changes — ignore_changesの注意点 — ignore_changesの落とし穴
- よくあるエラー集 — common_errors — 他のエラー一覧
- resourceブロック — 基本構文と使い方
- 「Cycle detected」循環依存エラーの解消方法
8. まとめ
- planで
-/+が表示されたら# forces replacementコメントで原因の属性を確認する - AMIのIDが変わる・リソース名や識別子を変えると多くのリソースが置換される
- データ消失リスクがある置換(RDS・ElastiCache等)は
ignore_changesで防ぐ - 無停止にしたい置換は
create_before_destroy = trueで先に新しいものを作る - 意図的な置換には
terraform apply -replace=resource.nameを使う(taintは非推奨)
動作確認バージョン: Terraform >= 1.9 公式ドキュメント: https://developer.hashicorp.com/terraform/language/resources/lifecycle