Using Terraform Data Sources Effectively
Learn how to use Terraform data sources to query existing resources, look up AMIs, reference remote state, and build dynamic configurations. Complete.
Cloud Computing
Complete guide to Terraform lifecycle rules. Learn prevent_destroy, create_before_destroy, ignore_changes
Terraform lifecycle rules control what happens when a resource needs to be created, updated, or destroyed. They're essential for production infrastructure — preventing accidental deletion, enabling zero-downtime deployments, and handling resources that are modified outside of Terraform.
resource "aws_instance" "web" {
# ... resource config ...
lifecycle {
create_before_destroy = true # Create new before destroying old
prevent_destroy = true # Block terraform destroy
ignore_changes = [tags] # Ignore external changes
replace_triggered_by = [null_resource.trigger.id] # Force replacement
}
}prevent_destroy = true makes Terraform refuse to destroy a resource. Any terraform destroy or plan that would destroy the resource produces an error instead.
resource "aws_db_instance" "production" {
identifier = "prod-database"
engine = "postgresql"
instance_class = "db.r6g.large"
allocated_storage = 100
lifecycle {
prevent_destroy = true
}
}If you try to destroy it:
Error: Instance cannot be destroyed
on main.tf line 1:
1: resource "aws_db_instance" "production" {
Resource aws_db_instance.production has lifecycle.prevent_destroy set,
but the plan calls for this resource to be destroyed.Use for:
To actually destroy it: Set prevent_destroy = false first, then run terraform destroy.
When Terraform needs to replace a resource (destroy + create), it normally destroys first, then creates. create_before_destroy = true reverses this order: create the new resource first, verify it works, then destroy the old one.
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
}
}Timeline without create_before_destroy:
1. Destroy old instance → DOWNTIME STARTS
2. Create new instance → DOWNTIME ENDSTimeline with create_before_destroy:
1. Create new instance → No downtime
2. Verify new instance
3. Destroy old instance → Seamless cutoverCommon use cases:
Auto Scaling Groups:
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = var.ami_id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
}
}Security Groups (referenced by instances):
resource "aws_security_group" "web" {
name_prefix = "web-"
vpc_id = aws_vpc.main.id
lifecycle {
create_before_destroy = true
}
}Watch out for naming conflicts: If a resource has a unique name (like an S3 bucket or IAM role), creating a new one before destroying the old one will fail with a "name already exists" error. Use name_prefix instead of name to avoid this.
ignore_changes tells Terraform to ignore changes to specific attributes. If something modifies the resource outside of Terraform (AWS Console, another tool, auto-scaling), Terraform won't try to revert it.
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
lifecycle {
ignore_changes = [tags, ami]
}
}Now if someone adds tags in the AWS Console or the AMI changes, terraform plan won't show a diff.
Ignore all attributes:
lifecycle {
ignore_changes = all
}This makes Terraform manage only the initial creation — after that, it ignores all changes. Useful for resources you want Terraform to create but not manage day-to-day.
Common use cases:
Auto-scaled instance counts:
resource "aws_autoscaling_group" "app" {
min_size = 2
max_size = 10
desired_capacity = 2
lifecycle {
ignore_changes = [desired_capacity]
}
}ECS services with auto-scaling:
resource "aws_ecs_service" "app" {
name = "app"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 2
lifecycle {
ignore_changes = [desired_count, task_definition]
}
}Tags managed by AWS (auto-tagging, Service Catalog):
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
lifecycle {
ignore_changes = [tags["aws:autoscaling:groupName"], tags["kubernetes.io/cluster"]]
}
}Added in Terraform 1.2. Forces a resource to be replaced when another resource or attribute changes:
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
lifecycle {
replace_triggered_by = [
aws_security_group.web.id,
null_resource.force_replace.id
]
}
}
resource "null_resource" "force_replace" {
triggers = {
deploy_version = var.deploy_version
}
}When var.deploy_version changes, null_resource.force_replace is recreated, which triggers replacement of aws_instance.web.
Use cases:
You can use multiple rules together:
# Production database: protected, ignores external tag changes
resource "aws_db_instance" "production" {
identifier = "prod-database"
engine = "postgresql"
instance_class = "db.r6g.large"
lifecycle {
prevent_destroy = true
ignore_changes = [tags, engine_version]
}
}
# Web server: zero-downtime replacement, ignore auto-scaling tags
resource "aws_launch_template" "web" {
name_prefix = "web-"
image_id = var.ami_id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
ignore_changes = [tags]
}
}resource "aws_db_instance" "main" {
identifier = "production-db"
engine = "postgresql"
engine_version = "15.4"
instance_class = "db.r6g.large"
allocated_storage = 100
backup_retention_period = 7
deletion_protection = true # AWS-level protection
lifecycle {
prevent_destroy = true # Terraform-level protection
ignore_changes = [engine_version] # Allow RDS auto minor upgrades
}
}resource "aws_lb_target_group" "app" {
name_prefix = "app-"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
path = "/health"
}
lifecycle {
create_before_destroy = true
}
}resource "aws_eks_node_group" "workers" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "workers"
instance_types = ["t3.medium"]
scaling_config {
desired_size = 3
min_size = 2
max_size = 10
}
lifecycle {
ignore_changes = [scaling_config[0].desired_size]
}
}You set prevent_destroy = true but are trying to remove the resource. Options:
prevent_destroy = false, apply, then removeterraform state rm to remove from state without destroyingError: A resource with the ID "my-security-group" already existsUse name_prefix instead of name:
resource "aws_security_group" "web" {
name_prefix = "web-" # Not name = "web-sg"
lifecycle {
create_before_destroy = true
}
}For map/object attributes, reference the whole attribute or specific keys:
# Ignore all tags
ignore_changes = [tags]
# Ignore specific tag key
ignore_changes = [tags["ManagedBy"]]Learn by doing with interactive courses on CopyPasteLearn:
Lifecycle rules are essential for production Terraform. Use prevent_destroy to protect databases and critical resources, create_before_destroy for zero-downtime deployments, ignore_changes to coexist with auto-scaling and external tools, and replace_triggered_by to chain resource replacements. Most production resources should have at least one lifecycle rule configured.
Learn how to use Terraform data sources to query existing resources, look up AMIs, reference remote state, and build dynamic configurations. Complete.
Master Terraform version constraints for Terraform core and providers. Covers operators, lock files, required_version, required_providers, and upgrade...
Learn how to use Terraform count and for_each to create multiple resources. Side-by-side comparison, practical examples, conditional creation
Master multi-account AWS management with Terraform. Learn provider aliases, cross-account IAM roles, AWS Organizations integration, and production-ready.