Tutorial 13: Manage Resource Targeting
Learning Objectives
- Understand resource targeting and its use cases
- Learn to target specific resources in Terraform operations
- Practice selective apply and destroy operations
- Implement targeting strategies for complex infrastructures
- Handle dependencies and targeting edge cases
What is Resource Targeting?
Resource targeting allows you to limit Terraform operations to specific resources or sets of resources, rather than operating on the entire configuration. This is useful for:
- Selective Updates: Apply changes to specific components
- Troubleshooting: Test changes on individual resources
- Large Infrastructures: Manage deployment order and timing
- Emergency Fixes: Quick fixes without full deployment
- Dependency Management: Handle resource dependencies manually
Important Warnings
⚠️ Resource targeting should be used carefully:
- Can lead to incomplete deployments
- May create inconsistent infrastructure state
- Should not be used as a standard practice
- Dependencies might not be properly handled
Basic Targeting Syntax
Target Specific Resources
# Target single resource
terraform apply -target=aws_instance.web
# Target multiple resources
terraform apply -target=aws_instance.web -target=aws_security_group.web
# Target with module
terraform apply -target=module.web_servers.aws_instance.web
Target Resource Types
# Target all instances of a resource type
terraform apply -target=aws_instance
# Target all resources in a module
terraform apply -target=module.web_servers
Practical Targeting Examples
Example Infrastructure Setup
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-west-2"
}
# Data sources
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
data "aws_availability_zones" "available" {
state = "available"
}
# VPC and networking
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-${count.index + 1}"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "public-rt"
}
}
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
# Security groups
resource "aws_security_group" "web" {
name_prefix = "web-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-security-group"
}
}
resource "aws_security_group" "database" {
name_prefix = "db-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
tags = {
Name = "database-security-group"
}
}
# Compute resources
resource "aws_instance" "web" {
count = 2
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_id = aws_subnet.public[count.index].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>Web Server ${count.index + 1}</h1>" > /var/www/html/index.html
EOF
tags = {
Name = "web-server-${count.index + 1}"
}
}
# Load balancer
resource "aws_lb" "web" {
name = "web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.web.id]
subnets = aws_subnet.public[*].id
enable_deletion_protection = false
tags = {
Name = "web-alb"
}
}
resource "aws_lb_target_group" "web" {
name = "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 = "web-target-group"
}
}
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
}
}
resource "aws_lb_target_group_attachment" "web" {
count = length(aws_instance.web)
target_group_arn = aws_lb_target_group.web.id
target_id = aws_instance.web[count.index].id
port = 80
}
Targeting Scenarios
Scenario 1: Deploy Infrastructure Incrementally
Step 1: Deploy Network Infrastructure First
# Deploy VPC and networking components only
terraform apply \
-target=aws_vpc.main \
-target=aws_subnet.public \
-target=aws_internet_gateway.main \
-target=aws_route_table.public \
-target=aws_route_table_association.public
Step 2: Deploy Security Groups
# Deploy security groups after networking
terraform apply \
-target=aws_security_group.web \
-target=aws_security_group.database
Step 3: Deploy Compute Resources
# Deploy EC2 instances
terraform apply -target=aws_instance.web
Step 4: Deploy Load Balancer
# Deploy load balancer and related resources
terraform apply \
-target=aws_lb.web \
-target=aws_lb_target_group.web \
-target=aws_lb_listener.web \
-target=aws_lb_target_group_attachment.web
Step 5: Complete Deployment
# Ensure everything is deployed correctly
terraform apply
Scenario 2: Update Specific Resource Types
Update Only EC2 Instances
# Plan changes for instances only
terraform plan -target=aws_instance.web
# Apply changes to instances only
terraform apply -target=aws_instance.web
Update Security Group Rules
# First, modify security group in configuration
# Then apply only security group changes
terraform apply -target=aws_security_group.web
Scenario 3: Troubleshoot Failed Resources
Handle Failed Load Balancer
# If load balancer creation failed, retry just that component
terraform apply \
-target=aws_lb.web \
-target=aws_lb_target_group.web \
-target=aws_lb_listener.web
Recreate Specific Instance
# Taint and recreate specific instance
terraform taint aws_instance.web[0]
terraform apply -target=aws_instance.web[0]
Scenario 4: Emergency Security Fix
Quick Security Group Update
# Update security group to remove problematic rule
resource "aws_security_group" "web" {
name_prefix = "web-sg"
vpc_id = aws_vpc.main.id
# Remove SSH access temporarily
# ingress {
# from_port = 22
# to_port = 22
# protocol = "tcp"
# cidr_blocks = ["10.0.0.0/16"]
# }
# Keep other rules...
}
# Apply security fix immediately
terraform apply -target=aws_security_group.web
Advanced Targeting Techniques
Targeting with Count and For-Each
Target Specific Count Instances
# Target first web server only
terraform apply -target=aws_instance.web[0]
# Target specific instances by index
terraform apply \
-target=aws_instance.web[0] \
-target=aws_instance.web[1]
Target For-Each Resources
# Configuration with for_each
resource "aws_instance" "web" {
for_each = {
frontend = "t2.micro"
backend = "t2.small"
}
ami = data.aws_ami.amazon_linux.id
instance_type = each.value
tags = {
Name = "${each.key}-server"
}
}
# Target specific for_each instance
terraform apply -target=aws_instance.web[\"frontend\"]
# Note: Use backslashes to escape quotes in bash
Targeting Modules
Module Structure Example
# modules/web-tier/main.tf
resource "aws_instance" "web" {
count = var.instance_count
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_ids[count.index]
tags = {
Name = "${var.name_prefix}-${count.index + 1}"
}
}
resource "aws_lb" "web" {
name = "${var.name_prefix}-alb"
subnets = var.subnet_ids
tags = {
Name = "${var.name_prefix}-alb"
}
}
# main.tf using module
module "web_tier" {
source = "./modules/web-tier"
instance_count = 3
ami_id = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
subnet_ids = aws_subnet.public[*].id
name_prefix = "production"
}
Targeting Module Resources
# Target entire module
terraform apply -target=module.web_tier
# Target specific resource in module
terraform apply -target=module.web_tier.aws_instance.web
# Target specific instance in module
terraform apply -target=module.web_tier.aws_instance.web[0]
# Target multiple modules
terraform apply \
-target=module.web_tier \
-target=module.database_tier
Targeting with Dependencies
Understanding Dependency Chains
# When targeting a resource, Terraform includes its dependencies
terraform apply -target=aws_instance.web
# This will also create:
# - aws_vpc.main (dependency)
# - aws_subnet.public (dependency)
# - aws_security_group.web (dependency)
Explicit Dependency Targeting
resource "aws_instance" "web" {
# ... configuration
# Explicit dependency
depends_on = [
aws_internet_gateway.main,
aws_route_table_association.public
]
}
# Targeting this will include explicit dependencies
terraform apply -target=aws_instance.web
Targeting Best Practices
1. Plan Before Targeting
# Always plan first to see what will be affected
terraform plan -target=aws_instance.web
# Review dependencies and side effects
terraform plan -target=aws_instance.web -out=target.plan
terraform show target.plan
2. Use Targeting for Specific Use Cases
# Good use cases:
# - Emergency fixes
terraform apply -target=aws_security_group.emergency_fix
# - Testing individual components
terraform apply -target=aws_instance.test
# - Large infrastructure phased deployments
terraform apply -target=module.phase1
# Avoid for regular operations
3. Document Targeting Decisions
#!/bin/bash
# deployment-script.sh
echo "Deploying infrastructure in phases..."
# Phase 1: Core networking
echo "Phase 1: Deploying core networking..."
terraform apply -auto-approve \
-target=aws_vpc.main \
-target=aws_subnet.public \
-target=aws_internet_gateway.main
# Phase 2: Security groups
echo "Phase 2: Deploying security groups..."
terraform apply -auto-approve \
-target=aws_security_group.web \
-target=aws_security_group.database
# Phase 3: Compute resources
echo "Phase 3: Deploying compute resources..."
terraform apply -auto-approve \
-target=aws_instance.web
# Final: Complete deployment
echo "Final: Completing full deployment..."
terraform apply -auto-approve
echo "Deployment complete!"
4. Validate After Targeting
# After targeted apply, validate full configuration
terraform plan
# Should show no changes if targeting was complete
# If changes remain, decide whether to apply them
5. Avoid Targeting Dependencies
# Bad: Targeting dependent resource without its dependencies
terraform apply -target=aws_lb_target_group_attachment.web
# This might fail if target group doesn't exist
# Good: Target the dependency chain
terraform apply \
-target=aws_lb_target_group.web \
-target=aws_lb_target_group_attachment.web
Targeting Patterns for Different Scenarios
Blue-Green Deployments
variable "active_environment" {
description = "Currently active environment (blue or green)"
type = string
default = "blue"
}
resource "aws_instance" "blue" {
count = var.active_environment == "blue" ? 2 : 0
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "blue-${count.index + 1}"
Environment = "blue"
}
}
resource "aws_instance" "green" {
count = var.active_environment == "green" ? 2 : 0
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
tags = {
Name = "green-${count.index + 1}"
Environment = "green"
}
}
# Deploy green environment
terraform apply \
-var="active_environment=green" \
-target=aws_instance.green
# Test green environment
# ... testing ...
# Switch traffic to green (update load balancer config)
terraform apply -var="active_environment=green"
# Clean up blue environment
terraform apply \
-var="active_environment=green" \
-target=aws_instance.blue
Rolling Updates
# Update instances one at a time
for i in 0 1 2; do
echo "Updating instance $i..."
# Taint specific instance
terraform taint aws_instance.web[$i]
# Recreate just that instance
terraform apply -target=aws_instance.web[$i]
# Wait for health check
sleep 60
echo "Instance $i updated successfully"
done
Disaster Recovery
# Quick recovery script
#!/bin/bash
# disaster-recovery.sh
COMPONENT=${1:-"all"}
case $COMPONENT in
"network")
echo "Recovering network infrastructure..."
terraform apply -auto-approve \
-target=aws_vpc.main \
-target=aws_subnet.public \
-target=aws_internet_gateway.main
;;
"compute")
echo "Recovering compute resources..."
terraform apply -auto-approve \
-target=aws_instance.web
;;
"loadbalancer")
echo "Recovering load balancer..."
terraform apply -auto-approve \
-target=aws_lb.web \
-target=aws_lb_target_group.web \
-target=aws_lb_listener.web
;;
"all")
echo "Full recovery..."
terraform apply -auto-approve
;;
*)
echo "Usage: $0 {network|compute|loadbalancer|all}"
exit 1
;;
esac
Troubleshooting Targeting Issues
Issue 1: Dependencies Not Created
# Problem: Target resource fails due to missing dependencies
# Solution: Include dependencies in targeting
# Check what dependencies are needed
terraform plan -target=aws_instance.web
# Include dependencies explicitly
terraform apply \
-target=aws_vpc.main \
-target=aws_subnet.public \
-target=aws_security_group.web \
-target=aws_instance.web
Issue 2: Targeting Doesn't Work as Expected
# Problem: Target doesn't match expected resources
# Solution: Use terraform plan to verify targets
# Check what would be targeted
terraform plan -target=aws_instance
# List current resources to verify names
terraform state list | grep aws_instance
Issue 3: Module Targeting Issues
# Problem: Can't target resources inside modules
# Solution: Use correct module syntax
# Wrong:
terraform apply -target=web_tier.aws_instance.web
# Correct:
terraform apply -target=module.web_tier.aws_instance.web
# List module resources
terraform state list | grep module.web_tier
Issue 4: Count/For-Each Targeting
# Problem: Can't target specific count instances
# Solution: Use proper indexing syntax
# For count:
terraform apply -target=aws_instance.web[0]
# For for_each (escape quotes):
terraform apply -target=aws_instance.web[\"frontend\"]
# Or use single quotes in bash:
terraform apply -target='aws_instance.web["frontend"]'
Key Takeaways
- Resource targeting should be used sparingly and with caution
- Always use
terraform plan -target=
to preview changes first - Targeting includes dependencies automatically
- Use targeting for emergency fixes, testing, and phased deployments
- Document when and why targeting is used
- Validate complete configuration after targeted operations
- Be careful with module targeting syntax
- Consider dependency chains when targeting
- Have rollback plans when using targeting in production
Next Steps
- Complete Tutorial 14: Troubleshoot Terraform
- Learn about workspace management for environment isolation
- Explore advanced state management techniques
- Practice with complex targeting scenarios in modules