1. count - Create Multiple Similar Resources

Creates multiple instances of a resource based on a numeric value.

# Basic count usage
resource "azurerm_storage_queue" "test_queues" {
  count                = var.env == "qa" ? 2 : 0
  name                 = "test-queue-${count.index}"
  storage_account_name = azurerm_storage_account.example.name
}
 
# Conditional resource creation
resource "aws_instance" "web" {
  count         = var.create_instances ? 3 : 0
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  tags = {
    Name = "web-server-${count.index}"
  }
}

Key Points:

  • count.index provides the current iteration index (0, 1, 2…)
  • Resources become arrays: aws_instance.web[0], aws_instance.web[1]
  • Changing count can cause resource destruction/recreation

2. for_each - Create Resources from Map/Set

Creates resources based on a map or set of strings, more flexible than count.

# Using set of strings
resource "azurerm_eventgrid_system_topic_event_subscription" "vm_events" {
  for_each = toset(["qa", "qa-test", "prod"])
  
  name         = "vm-lifecycle-events-${each.key}"
  system_topic = var.system_topic_name
  
  storage_queue_endpoint {
    queue_name = "vmlifecycle-events-${each.key}"
  }
}
 
# Using map
variable "environments" {
  default = {
    qa   = { instance_type = "t2.micro",  count = 1 }
    prod = { instance_type = "t2.medium", count = 3 }
  }
}
 
resource "aws_instance" "app" {
  for_each = var.environments
  
  ami           = "ami-12345678"
  instance_type = each.value.instance_type
  
  tags = {
    Environment = each.key
    Name        = "app-${each.key}"
  }
}

Key Points:

  • each.key = current map key or set element
  • each.value = current map value (not available for sets)
  • Resources referenced as: aws_instance.app["qa"]
  • More stable than count for adding/removing resources

IMPORTANT

Use ‘for_each’ instead of ‘count’ when possible. It’s more flexible and stable.

3. depends_on - Explicit Dependencies

Forces Terraform to create resources in specific order, beyond automatic dependency detection.

resource "azurerm_resource_group" "example" {
  name     = "eventgrid-${var.env}"
  location = "West US 2"
}
 
resource "azurerm_eventgrid_system_topic" "resources" {
  name                = "resource-events-${var.env}"
  resource_group_name = azurerm_resource_group.example.name
  
  # Explicit dependency (usually not needed due to automatic detection)
  depends_on = [azurerm_resource_group.example]
}
 
# More complex dependency
resource "null_resource" "app_deployment" {
  depends_on = [
    aws_instance.web,
    aws_db_instance.database,
    aws_security_group.app_sg
  ]
  
  provisioner "local-exec" {
    command = "./deploy-app.sh"
  }
}

When to Use:

  • When there are hidden dependencies Terraform can’t detect
  • Where ther are dependencies between modules
  • Ordering provisioners or null_resource actions

NOTE

This is useful when the dependency across module boundaries is not obvious.

4. provider - Specify Resource Provider

Selects which provider configuration to use when you have multiple providers.

# Configure multiple AWS providers
provider "aws" {
  alias  = "us_east_1"
  region = "us-east-1"
}
 
provider "aws" {
  alias  = "us_west_2"  
  region = "us-west-2"
}
 
# Use specific provider
resource "aws_instance" "east" {
  provider = aws.us_east_1
  
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}
 
resource "aws_instance" "west" {
  provider = aws.us_west_2
  
  ami           = "ami-87654321"
  instance_type = "t2.micro"
}

5. lifecycle - Control Resource Lifecycle

Controls how Terraform handles resource creation, updates, and deletion.

resource "aws_instance" "example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  lifecycle {
    # Prevent accidental deletion
    prevent_destroy = true
    
    # Create new before destroying old
    create_before_destroy = true
    
    # Ignore changes to specific attributes
    ignore_changes = [
      ami,           # Ignore AMI updates
      tags["Date"]   # Ignore specific tag changes
    ]
    
    # Conditional lifecycle rules
    precondition {
      condition     = var.env != "prod" || var.instance_type != "t2.nano"
      error_message = "Production instances cannot use t2.nano."
    }
    
    postcondition {
      condition     = self.public_ip != ""
      error_message = "Instance must have a public IP."
    }
  }
}

Lifecycle Options:

  • prevent_destroy - Block terraform destroy
  • create_before_destroy - Create replacement before destroying
  • ignore_changes - Ignore changes to specified attributes
  • replace_triggered_by - Force replacement when referenced resources change
  • precondition/postcondition - Validation checks

NOTE

Generally for simple lifecycle controls. Good side: it is supported by all resources.