Terraform 21: Conditionals (ternary) & dynamic blocks
easy⏱ 5 mincourseterraform
The ternary conditional
condition ? true_value : false_value returns one of two values — the only conditional expression in HCL (there is no if statement). Use it for env-specific settings: bigger SKUs in prod, cheaper in dev; a null to fall back to a provider default.
variable "environment" {
type = string
default = "dev"
}
locals {
is_prod = var.environment == "prod"
sku_name = local.is_prod ? "P1v3" : "B1" # value chosen by env
capacity = local.is_prod ? 3 : 1
}
resource "azurerm_service_plan" "api" {
name = "orders-api-plan"
resource_group_name = azurerm_resource_group.main.name
location = "westus2"
os_type = "Linux"
sku_name = local.sku_name
}
dynamic blocks generate nested blocks
Some resources take repeatable nested blocks (e.g. security_rule, ip_restriction). A dynamic "<block>" iterates a collection with for_each and emits one block per item; inside content, <block>.value is the current element. It's for_each for nested blocks instead of whole resources.
variable "ingress_rules" {
type = list(object({
name = string
priority = number
port = string
}))
default = [
{ name = "https", priority = 100, port = "443" },
{ name = "http", priority = 110, port = "80" },
]
}
resource "azurerm_network_security_group" "app" {
name = "orders-app-nsg"
resource_group_name = azurerm_resource_group.main.name
location = "westus2"
dynamic "security_rule" {
for_each = var.ingress_rules
content {
name = security_rule.value.name
priority = security_rule.value.priority
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = security_rule.value.port
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
}
Empty collection = zero blocks
Feed a dynamic block an empty list to emit no nested blocks — the idiom for optional configuration: for_each = var.docker == null ? [] : [var.docker]. Don't over-use dynamic; a couple of static blocks are more readable than a one-item loop.
Model a ternary + dynamic expansion
Write skuFor(env) returning "P1v3" when env === "prod" else "B1" (a ternary). Then write expandDynamic(rules) returning one { name, port } per rule (what a dynamic block emits). With the two default rules, log skuFor("dev") and expandDynamic(rules).length — expect a line containing B1 and a line containing 2.