Day 10— Terraform Conditional Expressions, Dynamic Blocks and Splat Expressions
Source: Dev.to
Conditional Expressions
Conditional expressions evaluate a condition and return one of two values based on whether the condition is true or false.
Syntax
condition ? true_value : false_value
Use Cases
- Choose instance types based on environment (
devvsprod) - Enable/disable monitoring based on configuration
- Select different AMIs based on region
- Set different resource counts for environments
- Apply environment‑specific tags
Benefits
- Single configuration for multiple environments
- Reduces code duplication
- Makes environment differences explicit
- Simplifies configuration management
- Easy to understand and maintain
When to Use
- Environment‑specific configurations
- Feature flags (enable/disable features)
- Conditional resource creation
- Region‑specific settings
- Cost optimization (smaller resources in dev)
When Not to Use
- Complex logic with many conditions (use
localsinstead) - When separate environment files are clearer
- When all environments should be identical
Example
resource "aws_instance" "conditional_example" {
ami = "ami-28765345876"
instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"
tags = {
Name = "conditional-instance-${var.environment}"
}
}
Running terraform plan shows the selected instance type based on the environment variable:
terraform plan
data.aws_ami.amazon_linux: Reading...
data.aws_ami.amazon_linux: Read complete after 1s [id=ami-02610f36df0c59544]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
+ create
# aws_instance.conditional_example will be created
+ resource "aws_instance" "conditional_example" {
+ ami = "ami-02610f36df0c59544"
+ instance_type = "t2.micro"
# ... other attributes omitted for brevity
}
In this example, if var.environment is set to "prod" the instance type will be t3.large; otherwise it defaults to t2.micro.
Dynamic Block
Dynamic blocks generate multiple nested blocks within a resource based on a collection (list or map), eliminating the need to repeat similar block configurations.
Syntax
dynamic "block_name" {
for_each = var.collection
content {
# Block configuration using each.key and each.value
}
}
for_eachiterates over a list or map.contentdefines what each generated block should contain.- Access values with
each.keyandeach.value.
Use Cases
- Security group ingress/egress rules
- Multiple EBS volumes on EC2 instances
- IAM policy statements
- Load balancer listeners
- Route table routes
- Any repeating nested block structure
Benefits
- Eliminates repetitive code
- Easy to add or remove items
- Configuration driven by variables
- Cleaner, more maintainable code
- Supports complex data structures
When to Use
- Multiple similar nested blocks
- Variable number of configurations
- Security group rules
- Inline policies
- Any repeating block pattern
When Not to Use
- Single or few static blocks (overhead not worth it)
- When it makes the code harder to read
- For top‑level resources (use
countorfor_eachinstead)
Example
resource "aws_security_group" "dynamic_sg" {
name = "dynamic-sg-${var.environment}"
description = "Security group with dynamic rules"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dynamic-sg-${var.environment}"
}
}
variable "ingress_rules" {
description = "List of ingress rules for security group"
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
description = string
}))
default = [
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
}
]
}
Running terraform plan shows the dynamically created ingress rules:
# aws_security_group.dynamic_sg will be created
+ resource "aws_security_group" "dynamic_sg" {
+ arn = (known after apply)
+ description = "Security group with dynamic rules"
+ egress = [
{
+ cidr_blocks = ["0.0.0.0/0"]
+ from_port = 0
+ protocol = "-1"
+ to_port = 0
}
]
+ ingress = [
{
+ from_port = 80
+ to_port = 80
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ description = "HTTP"
},
{
+ from_port = 443
+ to_port = 443
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ description = "HTTPS"
}
]
# ... other attributes omitted for brevity
}
The dynamic block iterates over var.ingress_rules, creating an ingress block for each rule without manual duplication.
