AskLearn
Loading...
← Back to Terraform Course
AdvancedConfiguration

Resource Targeting

Selective deployments

Tutorial 24: Resource Targeting and Partial Application

Learning Objectives

By the end of this tutorial, you will be able to:

  • Use resource targeting to apply changes to specific resources
  • Implement partial application strategies for large infrastructures
  • Apply selective updates and rollbacks using targeting
  • Design safe deployment patterns with targeted operations
  • Troubleshoot and debug using resource targeting techniques

Prerequisites

  • Understanding of Terraform state management
  • Completed Tutorial 13: Manage Resource Targeting
  • Knowledge of Terraform CLI operations

Introduction

Resource targeting allows you to focus Terraform operations on specific resources or modules, enabling precise control over infrastructure changes. This capability is essential for large, complex infrastructures where you need to apply changes incrementally or troubleshoot specific components.

Basic Resource Targeting

Single Resource Targeting

# main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name = "${var.project_name}-vpc"
  }
}

resource "aws_subnet" "public" {
  count = length(var.availability_zones)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index + 1)
  availability_zone = var.availability_zones[count.index]
  
  map_public_ip_on_launch = true
  
  tags = {
    Name = "${var.project_name}-public-subnet-${count.index + 1}"
    Type = "public"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = {
    Name = "${var.project_name}-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 = "${var.project_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
}

resource "aws_security_group" "web" {
  name        = "${var.project_name}-web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id
  
  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  tags = {
    Name = "${var.project_name}-web-sg"
  }
}

resource "aws_instance" "web" {
  count = var.instance_count
  
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.public[count.index % length(aws_subnet.public)].id
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = templatefile("${path.module}/user_data.sh", {
    server_name = "${var.project_name}-web-${count.index + 1}"
  })
  
  tags = {
    Name = "${var.project_name}-web-${count.index + 1}"
    Type = "web-server"
  }
}

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]
  
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }
}

Targeting Examples

# Target a specific resource
terraform plan -target=aws_instance.web[0]
terraform apply -target=aws_instance.web[0]

# Target multiple specific resources
terraform plan -target=aws_instance.web[0] -target=aws_instance.web[1]
terraform apply -target=aws_instance.web[0] -target=aws_instance.web[1]

# Target all instances of a resource type
terraform plan -target=aws_instance.web
terraform apply -target=aws_instance.web

# Target a specific security group
terraform plan -target=aws_security_group.web
terraform apply -target=aws_security_group.web

# Target network infrastructure
terraform plan -target=aws_vpc.main -target=aws_subnet.public -target=aws_internet_gateway.main
terraform apply -target=aws_vpc.main -target=aws_subnet.public -target=aws_internet_gateway.main

Module Targeting

Multi-Module Infrastructure

# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = var.enable_dns_hostnames
  enable_dns_support   = var.enable_dns_support
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-vpc"
  })
}

resource "aws_subnet" "public" {
  count = length(var.public_subnets)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.public_subnets[count.index]
  availability_zone = var.availability_zones[count.index]
  
  map_public_ip_on_launch = true
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-public-${count.index + 1}"
    Type = "public"
  })
}

resource "aws_subnet" "private" {
  count = length(var.private_subnets)
  
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnets[count.index]
  availability_zone = var.availability_zones[count.index]
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-private-${count.index + 1}"
    Type = "private"
  })
}

# modules/vpc/outputs.tf
output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "IDs of the public subnets"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "IDs of the private subnets"
  value       = aws_subnet.private[*].id
}

# modules/compute/main.tf
resource "aws_launch_template" "app" {
  name_prefix   = "${var.project_name}-app-"
  image_id      = var.ami_id
  instance_type = var.instance_type
  
  vpc_security_group_ids = [var.security_group_id]
  
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    app_name = var.project_name
    environment = var.environment
  }))
  
  tag_specifications {
    resource_type = "instance"
    tags = merge(var.common_tags, {
      Name = "${var.project_name}-app-instance"
    })
  }
}

resource "aws_autoscaling_group" "app" {
  name                = "${var.project_name}-asg"
  vpc_zone_identifier = var.subnet_ids
  target_group_arns   = [var.target_group_arn]
  health_check_type   = "ELB"
  
  min_size         = var.min_instances
  max_size         = var.max_instances
  desired_capacity = var.desired_instances
  
  launch_template {
    id      = aws_launch_template.app.id
    version = "$Latest"
  }
  
  tag {
    key                 = "Name"
    value               = "${var.project_name}-asg"
    propagate_at_launch = false
  }
  
  tag {
    key                 = "Environment"
    value               = var.environment
    propagate_at_launch = true
  }
}

# modules/database/main.tf
resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet-group"
  subnet_ids = var.subnet_ids
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-db-subnet-group"
  })
}

resource "aws_db_instance" "main" {
  identifier     = "${var.project_name}-database"
  engine         = var.engine
  engine_version = var.engine_version
  instance_class = var.instance_class
  
  allocated_storage = var.allocated_storage
  storage_encrypted = var.storage_encrypted
  
  db_name  = var.db_name
  username = var.username
  password = var.password
  
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [var.security_group_id]
  
  backup_retention_period = var.backup_retention_period
  backup_window          = var.backup_window
  maintenance_window     = var.maintenance_window
  
  skip_final_snapshot = var.skip_final_snapshot
  
  tags = merge(var.common_tags, {
    Name = "${var.project_name}-database"
  })
}

# Root main.tf
module "vpc" {
  source = "./modules/vpc"
  
  project_name = var.project_name
  vpc_cidr     = var.vpc_cidr
  
  public_subnets     = var.public_subnets
  private_subnets    = var.private_subnets
  availability_zones = var.availability_zones
  
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  common_tags = local.common_tags
}

module "security" {
  source = "./modules/security"
  
  project_name = var.project_name
  vpc_id       = module.vpc.vpc_id
  vpc_cidr     = var.vpc_cidr
  
  common_tags = local.common_tags
}

module "load_balancer" {
  source = "./modules/load_balancer"
  
  project_name       = var.project_name
  vpc_id            = module.vpc.vpc_id
  public_subnet_ids = module.vpc.public_subnet_ids
  security_group_id = module.security.alb_security_group_id
  
  common_tags = local.common_tags
}

module "compute" {
  source = "./modules/compute"
  
  project_name      = var.project_name
  environment       = var.environment
  ami_id           = data.aws_ami.ubuntu.id
  instance_type    = var.instance_type
  
  subnet_ids        = module.vpc.private_subnet_ids
  security_group_id = module.security.app_security_group_id
  target_group_arn  = module.load_balancer.target_group_arn
  
  min_instances     = var.min_instances
  max_instances     = var.max_instances
  desired_instances = var.desired_instances
  
  common_tags = local.common_tags
}

module "database" {
  source = "./modules/database"
  
  project_name = var.project_name
  
  engine         = "mysql"
  engine_version = "8.0"
  instance_class = var.db_instance_class
  
  allocated_storage = var.db_allocated_storage
  storage_encrypted = true
  
  db_name  = var.db_name
  username = var.db_username
  password = var.db_password
  
  subnet_ids        = module.vpc.private_subnet_ids
  security_group_id = module.security.db_security_group_id
  
  backup_retention_period = var.db_backup_retention
  backup_window          = "03:00-04:00"
  maintenance_window     = "sun:04:00-sun:05:00"
  
  skip_final_snapshot = var.environment != "prod"
  
  common_tags = local.common_tags
}

locals {
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
    CreatedAt   = formatdate("YYYY-MM-DD", timestamp())
  }
}

Module Targeting Examples

# Target entire VPC module
terraform plan -target=module.vpc
terraform apply -target=module.vpc

# Target specific resources within a module
terraform plan -target=module.vpc.aws_vpc.main
terraform apply -target=module.vpc.aws_vpc.main

# Target multiple modules
terraform plan -target=module.vpc -target=module.security
terraform apply -target=module.vpc -target=module.security

# Target compute infrastructure only
terraform plan -target=module.compute -target=module.load_balancer
terraform apply -target=module.compute -target=module.load_balancer

# Target database module
terraform plan -target=module.database
terraform apply -target=module.database

# Target all network-related modules
terraform plan -target=module.vpc -target=module.security -target=module.load_balancer
terraform apply -target=module.vpc -target=module.security -target=module.load_balancer

Advanced Targeting Patterns

Environment-Specific Targeting

# environments/dev/main.tf
module "infrastructure" {
  source = "../../modules/infrastructure"
  
  project_name = var.project_name
  environment  = "dev"
  
  # Development-specific configurations
  instance_count = 1
  instance_type  = "t3.micro"
  
  enable_monitoring = false
  enable_backups   = false
  
  vpc_cidr = "10.0.0.0/16"
  
  tags = {
    Environment = "dev"
    Cost-Center = "engineering"
  }
}

# environments/staging/main.tf
module "infrastructure" {
  source = "../../modules/infrastructure"
  
  project_name = var.project_name
  environment  = "staging"
  
  # Staging-specific configurations
  instance_count = 2
  instance_type  = "t3.small"
  
  enable_monitoring = true
  enable_backups   = true
  
  vpc_cidr = "10.1.0.0/16"
  
  tags = {
    Environment = "staging"
    Cost-Center = "engineering"
  }
}

# environments/prod/main.tf
module "infrastructure" {
  source = "../../modules/infrastructure"
  
  project_name = var.project_name
  environment  = "prod"
  
  # Production-specific configurations
  instance_count = 5
  instance_type  = "t3.medium"
  
  enable_monitoring = true
  enable_backups   = true
  enable_multi_az  = true
  
  vpc_cidr = "10.2.0.0/16"
  
  tags = {
    Environment = "prod"
    Cost-Center = "engineering"
    Critical    = "true"
  }
}

Deployment Pipeline Targeting

#!/bin/bash
# deploy.sh - Deployment script with targeting

set -e

ENVIRONMENT=${1:-dev}
TARGET=${2:-all}
ACTION=${3:-apply}

echo "Deploying to $ENVIRONMENT with target: $TARGET"

cd "environments/$ENVIRONMENT"

case $TARGET in
  "network")
    echo "Deploying network infrastructure..."
    terraform $ACTION -target=module.infrastructure.module.vpc \
                     -target=module.infrastructure.module.security \
                     -auto-approve
    ;;
  
  "compute")
    echo "Deploying compute infrastructure..."
    terraform $ACTION -target=module.infrastructure.module.compute \
                     -target=module.infrastructure.module.load_balancer \
                     -auto-approve
    ;;
  
  "database")
    echo "Deploying database infrastructure..."
    terraform $ACTION -target=module.infrastructure.module.database \
                     -auto-approve
    ;;
  
  "monitoring")
    echo "Deploying monitoring infrastructure..."
    terraform $ACTION -target=module.infrastructure.module.monitoring \
                     -auto-approve
    ;;
  
  "all")
    echo "Deploying all infrastructure..."
    terraform $ACTION -auto-approve
    ;;
  
  *)
    echo "Unknown target: $TARGET"
    echo "Available targets: network, compute, database, monitoring, all"
    exit 1
    ;;
esac

echo "Deployment completed successfully!"

Blue-Green Deployment with Targeting

# variables.tf
variable "deployment_color" {
  description = "Current deployment color (blue or green)"
  type        = string
  default     = "blue"
  
  validation {
    condition     = contains(["blue", "green"], var.deployment_color)
    error_message = "Deployment color must be either 'blue' or 'green'."
  }
}

variable "traffic_weight" {
  description = "Traffic weight for current deployment (0-100)"
  type        = number
  default     = 100
  
  validation {
    condition     = var.traffic_weight >= 0 && var.traffic_weight <= 100
    error_message = "Traffic weight must be between 0 and 100."
  }
}

# main.tf
locals {
  current_color  = var.deployment_color
  inactive_color = var.deployment_color == "blue" ? "green" : "blue"
}

# Blue deployment
module "blue_deployment" {
  source = "./modules/deployment"
  
  project_name = var.project_name
  environment  = var.environment
  color        = "blue"
  
  instance_count = var.deployment_color == "blue" ? var.instance_count : 0
  
  vpc_id            = module.vpc.vpc_id
  subnet_ids        = module.vpc.private_subnet_ids
  security_group_id = module.security.app_security_group_id
  
  ami_id        = var.blue_ami_id
  instance_type = var.instance_type
  
  tags = merge(local.common_tags, {
    Color = "blue"
  })
}

# Green deployment
module "green_deployment" {
  source = "./modules/deployment"
  
  project_name = var.project_name
  environment  = var.environment
  color        = "green"
  
  instance_count = var.deployment_color == "green" ? var.instance_count : 0
  
  vpc_id            = module.vpc.vpc_id
  subnet_ids        = module.vpc.private_subnet_ids
  security_group_id = module.security.app_security_group_id
  
  ami_id        = var.green_ami_id
  instance_type = var.instance_type
  
  tags = merge(local.common_tags, {
    Color = "green"
  })
}

# Load balancer with weighted routing
resource "aws_lb_listener_rule" "blue" {
  listener_arn = aws_lb_listener.app.arn
  priority     = 100
  
  action {
    type             = "forward"
    target_group_arn = module.blue_deployment.target_group_arn
    
    forward {
      target_group {
        arn    = module.blue_deployment.target_group_arn
        weight = var.deployment_color == "blue" ? var.traffic_weight : (100 - var.traffic_weight)
      }
      
      target_group {
        arn    = module.green_deployment.target_group_arn
        weight = var.deployment_color == "green" ? var.traffic_weight : (100 - var.traffic_weight)
      }
    }
  }
  
  condition {
    path_pattern {
      values = ["/*"]
    }
  }
}

Blue-Green Deployment Scripts

#!/bin/bash
# blue-green-deploy.sh

set -e

CURRENT_COLOR=${1:-blue}
NEW_AMI_ID=${2}
ENVIRONMENT=${3:-staging}

if [ -z "$NEW_AMI_ID" ]; then
    echo "Usage: $0 <current_color> <new_ami_id> [environment]"
    exit 1
fi

INACTIVE_COLOR=$([ "$CURRENT_COLOR" = "blue" ] && echo "green" || echo "blue")

echo "Starting blue-green deployment..."
echo "Current color: $CURRENT_COLOR"
echo "Deploying to: $INACTIVE_COLOR"
echo "New AMI ID: $NEW_AMI_ID"

cd "environments/$ENVIRONMENT"

# Step 1: Update the inactive deployment
echo "Step 1: Deploying new version to $INACTIVE_COLOR environment..."
terraform apply \
    -target="module.${INACTIVE_COLOR}_deployment" \
    -var="deployment_color=$INACTIVE_COLOR" \
    -var="${INACTIVE_COLOR}_ami_id=$NEW_AMI_ID" \
    -auto-approve

# Step 2: Health check the new deployment
echo "Step 2: Performing health checks..."
sleep 30

# Get the inactive deployment target group ARN
INACTIVE_TG_ARN=$(terraform output -raw "${INACTIVE_COLOR}_target_group_arn")

# Check health of targets (simplified example)
aws elbv2 describe-target-health --target-group-arn "$INACTIVE_TG_ARN" \
    | jq -r '.TargetHealthDescriptions[].TargetHealth.State' \
    | grep -q "healthy" || {
        echo "Health check failed! Rolling back..."
        terraform apply \
            -target="module.${INACTIVE_COLOR}_deployment" \
            -var="deployment_color=$CURRENT_COLOR" \
            -var="${INACTIVE_COLOR}_ami_id=" \
            -auto-approve
        exit 1
    }

echo "Health checks passed!"

# Step 3: Gradually shift traffic
echo "Step 3: Shifting traffic..."
for weight in 10 25 50 75 100; do
    echo "Setting traffic weight to $weight% on $INACTIVE_COLOR..."
    terraform apply \
        -target="aws_lb_listener_rule.blue" \
        -var="deployment_color=$INACTIVE_COLOR" \
        -var="traffic_weight=$weight" \
        -auto-approve
    
    echo "Waiting 2 minutes before next traffic shift..."
    sleep 120
    
    # Additional health checks could go here
done

# Step 4: Shutdown old deployment
echo "Step 4: Shutting down old $CURRENT_COLOR deployment..."
terraform apply \
    -target="module.${CURRENT_COLOR}_deployment" \
    -var="deployment_color=$INACTIVE_COLOR" \
    -auto-approve

echo "Blue-green deployment completed successfully!"
echo "Active deployment is now: $INACTIVE_COLOR"

Targeted Rollback Strategies

Safe Rollback Patterns

# rollback.tf
variable "enable_rollback_mode" {
  description = "Enable rollback mode to restore previous state"
  type        = bool
  default     = false
}

variable "rollback_ami_id" {
  description = "AMI ID to rollback to"
  type        = string
  default     = ""
}

variable "rollback_target" {
  description = "Target for rollback (compute, database, all)"
  type        = string
  default     = "all"
  
  validation {
    condition     = contains(["compute", "database", "network", "all"], var.rollback_target)
    error_message = "Rollback target must be one of: compute, database, network, all."
  }
}

locals {
  # Use rollback AMI if in rollback mode, otherwise use current AMI
  active_ami_id = var.enable_rollback_mode && var.rollback_ami_id != "" ? var.rollback_ami_id : var.ami_id
  
  # Conditional configurations for rollback
  rollback_config = var.enable_rollback_mode ? {
    skip_health_checks = true
    force_update      = true
    ignore_tags       = true
  } : {
    skip_health_checks = false
    force_update      = false
    ignore_tags       = false
  }
}

# Compute resources with rollback support
resource "aws_launch_template" "app" {
  name_prefix   = "${var.project_name}-app-"
  image_id      = local.active_ami_id
  instance_type = var.instance_type
  
  vpc_security_group_ids = [aws_security_group.app.id]
  
  # Add rollback metadata
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    app_version    = var.enable_rollback_mode ? "rollback" : var.app_version
    rollback_mode  = var.enable_rollback_mode
    deployment_id  = var.enable_rollback_mode ? "rollback-${timestamp()}" : var.deployment_id
  }))
  
  lifecycle {
    create_before_destroy = true
    ignore_changes = var.enable_rollback_mode ? [] : [user_data]
  }
  
  tag_specifications {
    resource_type = "instance"
    tags = merge(var.common_tags, {
      Name         = "${var.project_name}-app"
      RollbackMode = var.enable_rollback_mode
    })
  }
}

Rollback Scripts

#!/bin/bash
# rollback.sh

set -e

ROLLBACK_TARGET=${1:-all}
ROLLBACK_AMI_ID=${2}
ENVIRONMENT=${3:-dev}

echo "Starting rollback procedure..."
echo "Target: $ROLLBACK_TARGET"
echo "Environment: $ENVIRONMENT"

cd "environments/$ENVIRONMENT"

# Get current state backup
echo "Creating state backup..."
cp terraform.tfstate "terraform.tfstate.backup.$(date +%Y%m%d_%H%M%S)"

case $ROLLBACK_TARGET in
  "compute")
    echo "Rolling back compute resources..."
    if [ -n "$ROLLBACK_AMI_ID" ]; then
        terraform apply \
            -target=module.infrastructure.module.compute \
            -var="enable_rollback_mode=true" \
            -var="rollback_ami_id=$ROLLBACK_AMI_ID" \
            -auto-approve
    else
        echo "Error: AMI ID required for compute rollback"
        exit 1
    fi
    ;;
  
  "database")
    echo "Rolling back database resources..."
    # Database rollbacks typically require point-in-time recovery
    echo "Warning: Database rollback requires manual intervention"
    echo "Please use AWS RDS point-in-time recovery or restore from backup"
    ;;
  
  "network")
    echo "Rolling back network resources..."
    # Network rollbacks are usually safe but may cause downtime
    terraform apply \
        -target=module.infrastructure.module.vpc \
        -target=module.infrastructure.module.security \
        -var="enable_rollback_mode=true" \
        -auto-approve
    ;;
  
  "all")
    echo "Rolling back all resources..."
    terraform apply \
        -var="enable_rollback_mode=true" \
        -var="rollback_ami_id=$ROLLBACK_AMI_ID" \
        -auto-approve
    ;;
  
  *)
    echo "Unknown rollback target: $ROLLBACK_TARGET"
    exit 1
    ;;
esac

echo "Rollback completed!"
echo "Please verify the rollback was successful before proceeding."

Troubleshooting with Targeting

Debugging Specific Resources

# Debug specific resource
terraform plan -target=aws_instance.web[0] -out=debug.tfplan
terraform show debug.tfplan

# Check dependencies for a specific resource
terraform graph -target=aws_instance.web[0] | dot -Tpng > dependencies.png

# Force refresh specific resource
terraform apply -target=aws_instance.web[0] -refresh-only

# Import existing resource into state
terraform import aws_instance.web[0] i-1234567890abcdef0

# Remove specific resource from state without destroying
terraform state rm aws_instance.web[0]

# Taint specific resource to force recreation
terraform taint aws_instance.web[0]
terraform apply -target=aws_instance.web[0]

State Surgery with Targeting

#!/bin/bash
# state-surgery.sh - Advanced state manipulation

OPERATION=${1}
RESOURCE=${2}
NEW_ADDRESS=${3}

case $OPERATION in
  "move")
    echo "Moving resource from $RESOURCE to $NEW_ADDRESS"
    terraform state mv "$RESOURCE" "$NEW_ADDRESS"
    ;;
  
  "remove")
    echo "Removing resource $RESOURCE from state"
    terraform state rm "$RESOURCE"
    ;;
  
  "import")
    echo "Importing resource $NEW_ADDRESS with ID $RESOURCE"
    terraform import "$NEW_ADDRESS" "$RESOURCE"
    ;;
  
  "replace")
    echo "Replacing resource $RESOURCE"
    terraform apply -replace="$RESOURCE" -target="$RESOURCE"
    ;;
  
  *)
    echo "Usage: $0 <move|remove|import|replace> <resource> [new_address]"
    exit 1
    ;;
esac

Best Practices

1. Safe Targeting Practices

# Always plan before applying with targets
terraform plan -target=aws_instance.web -out=targeted.tfplan
terraform apply targeted.tfplan

# Use specific targets rather than wildcards when possible
terraform plan -target=aws_instance.web[0]  # Good
terraform plan -target=aws_instance.web     # Less precise

# Document your targeting strategy
echo "Applying database changes only" > deployment.log
terraform apply -target=module.database >> deployment.log 2>&1

2. Dependency Management

# Explicit dependencies for targeting
resource "aws_instance" "app" {
  count = var.instance_count
  
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.private[count.index % length(aws_subnet.private)].id
  
  vpc_security_group_ids = [aws_security_group.app.id]
  
  # Explicit dependency ensures proper targeting order
  depends_on = [
    aws_nat_gateway.main,
    aws_route_table_association.private
  ]
  
  tags = {
    Name = "${var.project_name}-app-${count.index + 1}"
  }
}

3. Testing Targeting Strategies

#!/bin/bash
# test-targeting.sh

echo "Testing targeting strategies..."

# Test 1: Plan with targeting to ensure no unexpected changes
echo "Test 1: Verify targeted plan"
terraform plan -target=module.compute -detailed-exitcode
if [ $? -eq 2 ]; then
    echo "✓ Changes detected as expected"
else
    echo "✗ No changes detected - check configuration"
fi

# Test 2: Validate dependencies
echo "Test 2: Validate dependencies"
terraform validate

# Test 3: Dry run with targeting
echo "Test 3: Dry run targeted apply"
terraform plan -target=module.compute -out=test.tfplan
terraform show -json test.tfplan | jq '.planned_values'

echo "All tests completed!"

Key Takeaways

  1. Precision: Use specific resource targeting for precise control over deployments
  2. Safety: Always plan before applying targeted changes
  3. Dependencies: Understand resource dependencies when targeting
  4. Documentation: Document targeting strategies and rationale
  5. Testing: Test targeting strategies in non-production environments first
  6. Rollback: Have rollback plans when using targeting for deployments

Next Steps

  • Tutorial 25: Learn about configuration validation and best practices
  • Practice targeting strategies in different scenarios
  • Experiment with blue-green deployments using targeting
  • Review the Terraform CLI Documentation

Additional Resources