AskLearn
Loading...
← Back to Terraform Course
IntermediateFundamentals

Manage Resource Targeting

Selective resource operations

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

  1. Complete Tutorial 14: Troubleshoot Terraform
  2. Learn about workspace management for environment isolation
  3. Explore advanced state management techniques
  4. Practice with complex targeting scenarios in modules

Additional Resources