Tutorial 4: Change Infrastructure
Learning Objectives
- Understand how Terraform handles infrastructure changes
- Learn about different types of resource modifications
- Practice updating existing infrastructure
- Understand resource lifecycle management
Prerequisites
- Completed Tutorial 3: Build Infrastructure
- Understanding of basic Terraform workflow
- Existing infrastructure from previous tutorial (or recreate it)
Types of Infrastructure Changes
1. In-Place Updates
Changes that can be applied without destroying the resource:
- Tags modification
- Security group rule updates
- Instance metadata changes
2. Recreation Required
Changes that require destroying and recreating the resource:
- Instance type changes (usually)
- AMI changes
- Subnet changes
3. Addition/Removal
- Adding new resources
- Removing existing resources
Working with Existing Infrastructure
Current State Check
# List current resources
terraform state list
# Show detailed state
terraform show
# Check current outputs
terraform output
Verify Infrastructure
# Ensure everything is up to date
terraform plan
If you don't have infrastructure from Tutorial 3, recreate it:
# Apply the configuration from Tutorial 3
terraform apply
Making In-Place Changes
Example 1: Updating Tags
Modify your main.tf
to update instance tags:
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_id = aws_subnet.main.id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello from Terraform - Updated!</h1>" > /var/www/html/index.html
EOF
tags = {
Name = "terraform-web-server"
Environment = "development"
Project = "terraform-tutorial"
Owner = "DevOps Team"
LastModified = "2024-01-15"
}
}
Plan and Apply Changes
terraform plan
Expected output:
# aws_instance.web will be updated in-place
~ resource "aws_instance" "web" {
~ tags = {
+ "LastModified" = "2024-01-15"
+ "Owner" = "DevOps Team"
# (3 unchanged elements hidden)
}
~ tags_all = {
+ "LastModified" = "2024-01-15"
+ "Owner" = "DevOps Team"
# (3 unchanged elements hidden)
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
terraform apply
Example 2: Updating Security Group Rules
Add HTTPS access to the security group:
resource "aws_security_group" "web" {
name = "terraform-web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
# HTTP access
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# HTTPS access - NEW RULE
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# SSH access
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Restrict this in production!
}
# All outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "terraform-web-sg"
}
}
Apply the security group changes:
terraform plan
terraform apply
Making Changes That Require Recreation
Example 1: Changing Instance Type
Modify the instance type in main.tf
:
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro" # Changed from t2.micro
subnet_id = aws_subnet.main.id
vpc_security_group_ids = [aws_security_group.web.id]
# ... rest of configuration
}
Plan the Change
terraform plan
Expected output:
# aws_instance.web must be replaced
-/+ resource "aws_instance" "web" {
~ instance_type = "t2.micro" -> "t3.micro" # forces replacement
~ public_ip = "54.123.45.67" -> (known after apply)
# ... other changes
}
Plan: 1 to add, 0 to change, 1 to destroy.
Note: The -/+
indicates the resource will be destroyed and recreated.
Apply with Caution
terraform apply
Warning: This will cause downtime as the instance is destroyed and recreated.
Adding New Resources
Add an Application Load Balancer
Add to main.tf
:
# Create target group for load balancer
resource "aws_lb_target_group" "web" {
name = "terraform-web-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
enabled = true
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 5
interval = 30
path = "/"
matcher = "200"
}
tags = {
Name = "terraform-web-tg"
}
}
# Create Application Load Balancer
resource "aws_lb" "web" {
name = "terraform-web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.web.id]
subnets = [aws_subnet.main.id, aws_subnet.secondary.id]
enable_deletion_protection = false
tags = {
Name = "terraform-web-alb"
}
}
# Create additional subnet for ALB (requires 2 AZs)
resource "aws_subnet" "secondary" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-west-2b"
map_public_ip_on_launch = true
tags = {
Name = "terraform-subnet-secondary"
}
}
# Associate secondary subnet with route table
resource "aws_route_table_association" "secondary" {
subnet_id = aws_subnet.secondary.id
route_table_id = aws_route_table.main.id
}
# Create listener for load balancer
resource "aws_lb_listener" "web" {
load_balancer_arn = aws_lb.web.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web.arn
}
}
# Attach instance to target group
resource "aws_lb_target_group_attachment" "web" {
target_group_arn = aws_lb_target_group.web.id
target_id = aws_instance.web.id
port = 80
}
Update Outputs
Add to outputs.tf
:
output "load_balancer_dns" {
description = "DNS name of the load balancer"
value = aws_lb.web.dns_name
}
output "load_balancer_url" {
description = "URL to access the website via load balancer"
value = "http://${aws_lb.web.dns_name}"
}
Apply New Resources
terraform plan
terraform apply
Resource Dependencies and Ordering
Implicit Dependencies
Terraform automatically determines dependencies from resource references:
resource "aws_instance" "web" {
subnet_id = aws_subnet.main.id # Depends on subnet
# Subnet must be created before instance
}
Explicit Dependencies
Use depends_on
when implicit dependencies aren't sufficient:
resource "aws_instance" "web" {
# ... configuration
depends_on = [
aws_internet_gateway.main, # Ensure IGW exists first
aws_route_table_association.main
]
}
Understanding Resource Lifecycle
Lifecycle Rules
Control how Terraform handles resource changes:
resource "aws_instance" "web" {
# ... configuration
lifecycle {
# Prevent accidental deletion
prevent_destroy = true
# Create new resource before destroying old one
create_before_destroy = true
# Ignore changes to certain attributes
ignore_changes = [
tags["LastModified"],
user_data
]
}
}
Create Before Destroy
Useful for zero-downtime deployments:
resource "aws_launch_configuration" "web" {
# ... configuration
lifecycle {
create_before_destroy = true
}
}
Handling Resource Drift
Detect Drift
# Check for configuration drift
terraform plan
# Refresh state without applying changes
terraform refresh
Import Existing Resources
If resources were modified outside Terraform:
# Import manually created resource
terraform import aws_instance.web i-1234567890abcdef0
Ignore External Changes
Use ignore_changes
to ignore external modifications:
resource "aws_instance" "web" {
# ... configuration
lifecycle {
ignore_changes = [
tags, # Ignore tag changes made outside Terraform
security_groups # Ignore security group changes
]
}
}
Removing Resources
Remove from Configuration
Simply delete the resource block from your configuration:
# Remove this entire block to delete the resource
# resource "aws_lb" "web" {
# # ... configuration
# }
Plan and Apply Removal
terraform plan # Shows resources to be destroyed
terraform apply
Protect Important Resources
resource "aws_instance" "production_db" {
# ... configuration
lifecycle {
prevent_destroy = true # Prevents accidental deletion
}
}
Advanced Change Scenarios
Conditional Resources
Use count
to conditionally create resources:
variable "create_load_balancer" {
description = "Whether to create load balancer"
type = bool
default = false
}
resource "aws_lb" "web" {
count = var.create_load_balancer ? 1 : 0
# ... configuration
}
Toggle the load balancer:
# Create with load balancer
terraform apply -var="create_load_balancer=true"
# Remove load balancer
terraform apply -var="create_load_balancer=false"
Resource Replacement Strategies
1. Blue-Green Deployment Pattern
variable "environment_suffix" {
description = "Environment suffix for blue-green deployments"
type = string
default = "blue"
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "web-server-${var.environment_suffix}"
}
}
Switch environments:
# Deploy green environment
terraform apply -var="environment_suffix=green"
# Switch back to blue
terraform apply -var="environment_suffix=blue"
Testing Your Changes
Validate Configuration
terraform validate
Check Plan Output
terraform plan -out=tfplan
Test Infrastructure
# Test web server
curl http://$(terraform output -raw instance_public_ip)
# Test load balancer (if created)
curl http://$(terraform output -raw load_balancer_dns)
Rollback Strategies
Using Version Control
# Revert to previous configuration
git checkout HEAD~1 main.tf
terraform plan
terraform apply
State Backup
# Terraform automatically backs up state
ls terraform.tfstate*
# Restore from backup if needed
mv terraform.tfstate.backup terraform.tfstate
Targeted Operations
# Apply changes to specific resources only
terraform apply -target=aws_instance.web
# Destroy specific resources
terraform destroy -target=aws_lb.web
Best Practices for Changes
1. Always Plan First
terraform plan -out=tfplan
terraform apply tfplan
2. Use Version Control
- Commit configuration changes
- Tag releases
- Review changes via pull requests
3. Test in Non-Production
- Use separate environments
- Test changes thoroughly
- Validate functionality
4. Monitor Changes
- Watch for unexpected modifications
- Use resource tagging for tracking
- Monitor costs and usage
5. Document Changes
- Comment complex changes
- Maintain change logs
- Document rollback procedures
Key Takeaways
- Terraform handles three types of changes: in-place updates, recreation, and addition/removal
- Always run
terraform plan
to preview changes before applying - Some changes require resource recreation, which may cause downtime
- Use lifecycle rules to control resource behavior
- Resource dependencies are managed automatically by Terraform
- Practice changes in development environments first
- Version control your configurations for easy rollbacks
Next Steps
- Complete Tutorial 5: Destroy Infrastructure
- Learn about variables and input validation
- Explore modules for reusable configurations
- Practice with more complex change scenarios