Loops and Conditionals: Dynamic Configurations with Count & For_Each
Published on: November 12, 2025 | Author: DevOps Engineering Team
Welcome to Part 4 of our Terraform Mastery Series! Learn how to create flexible, scalable infrastructure using Terraform's powerful looping and conditional constructs. Master count, for_each, and conditional expressions to eliminate configuration duplication and build dynamic systems.
What You'll Learn
Why Use Loops in Terraform?
Loops and conditionals transform static infrastructure configurations into dynamic, scalable systems. Instead of copying and pasting resource blocks, you can create multiple similar resources with a single, maintainable definition.
Benefits of Using Loops
- DRY Principle: Don't Repeat Yourself
- Scalability: Easily scale resources up or down
- Maintainability: One change updates all instances
- Consistency: Ensure uniform configuration
- Flexibility: Adapt to different environments
Common Use Cases
- Multiple similar EC2 instances
- Creating subnets across availability zones
- Setting up security group rules
- Deploying to multiple regions
- Environment-specific configurations
Terraform Loop Constructs
Terraform provides two main looping mechanisms: count for creating multiple identical resources, and for_each for creating resources from a map or set of strings. Each has its strengths and use cases.
The Count Meta-Argument
The count meta-argument creates multiple resource instances based on a numeric value. It's perfect for creating identical or nearly identical resources.
How Count Works
The For_Each Meta-Argument
The for_each meta-argument creates resources from a map or set of strings, providing more flexibility and better state management than count.
For_Each with Sets
# Create instances for each environment
resource "aws_instance" "app" {
for_each = toset(["api", "worker", "cache"])
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
tags = {
Name = "${var.environment}-${each.key}"
Role = each.value
}
}
For_Each with Maps
# Create instances with specific configurations
resource "aws_instance" "servers" {
for_each = {
"web" = "t3.micro"
"app" = "t3.small"
"db" = "t3.medium"
}
ami = "ami-0c02fb55956c7d316"
instance_type = each.value # Use the value from map
tags = {
Name = "${var.environment}-${each.key}"
Role = each.key
}
}
Count vs For_Each: When to Use Each
Understanding when to use count versus for_each is crucial for effective Terraform configurations.
| Feature | Count | For_Each |
|---|---|---|
| Use Case | Identical resources | Distinct resources with different configurations |
| Input Type | Number | Map or set of strings |
| State Management | Index-based (0, 1, 2...) | Key-based (more stable) |
| Adding/Removing | Can cause resource recreation | More granular, less recreation |
| Reference Syntax | resource.type.name[0] |
resource.type.name["key"] |
| Best For | Scaling identical resources | Multiple similar but distinct resources |
Use Count When:
- Creating multiple identical resources
- Simple scaling based on a number
- Conditional resource creation (0 or 1)
- Resources don't need unique identifiers
Use For_Each When:
- Resources have different configurations
- You need stable resource identifiers
- Working with maps of configuration data
- Adding/removing specific resources
Avoid This Count Pitfall
When using count, adding or removing items from the middle of the list can cause unexpected resource recreation. for_each with maps provides more stable resource addressing.
Conditional Expressions
Terraform's conditional expressions allow you to make decisions within your configurations, enabling environment-specific logic and feature toggles.
Dynamic Blocks for Nested Configuration
Dynamic blocks allow you to dynamically construct nested configuration blocks within resources, perfect for security group rules, tags, and other repetitive nested structures.
Dynamic Blocks Interactive Example
See how dynamic blocks transform list/map data into nested configuration:
Real-World Examples
Let's look at some practical examples that combine loops, conditionals, and dynamic blocks for real-world scenarios.
Multi-Region Deployment
locals {
regions = {
"us-east-1" = "primary"
"us-west-2" = "secondary"
"eu-west-1" = "backup"
}
}
# Create resources in multiple regions
resource "aws_s3_bucket" "cross_region" {
for_each = local.regions
bucket = "${var.app_name}-${each.key}-${random_pet.bucket.id}"
region = each.key
tags = {
Region = each.key
Role = each.value
App = var.app_name
}
}
Environment-Specific Configurations
locals {
environment_configs = {
"dev" = {
instance_type = "t3.micro"
instance_count = 1
enable_monitoring = false
}
"staging" = {
instance_type = "t3.small"
instance_count = 2
enable_monitoring = true
}
"production" = {
instance_type = "m5.large"
instance_count = 3
enable_monitoring = true
}
}
config = local.environment_configs[var.environment]
}
# Use environment-specific configuration
resource "aws_instance" "app" {
count = local.config.instance_count
ami = "ami-0c02fb55956c7d316"
instance_type = local.config.instance_type
monitoring = local.config.enable_monitoring
tags = {
Name = "${var.environment}-app-${count.index}"
Environment = var.environment
}
}
Best Practices and Pitfalls
Best Practices
- Use
for_eachwith maps for stable resource addressing - Prefer
toset()andtomap()for type conversion - Use
try()for safe attribute access - Validate input variables with condition blocks
- Use
nullfor optional resource arguments
Common Pitfalls
- Using
countwith lists that may change order - Not handling empty collections properly
- Forgetting that
countstarts at 0 - Mixing
countandfor_eachin same resource - Not testing edge cases (0, 1, many)
Key Takeaways
- Count creates multiple identical resources using numeric indexing
- For_Each creates distinct resources from maps or sets with stable addressing
- Conditional expressions enable environment-specific logic and feature toggles
- Dynamic blocks generate nested configuration blocks from collections
- Choose
for_eachovercountwhen resources have different configurations - Always test your loops with edge cases (0, 1, many instances)
In our next tutorial, we'll explore Terraform Data Sources and Dependencies, where you'll learn how to fetch information from existing infrastructure and manage complex dependency relationships.
No comments:
Post a Comment