Terraform Configuration · Topic 3 of 4
Expressions, Functions and Dependencies
Terraform Associate 004: conditionals, for expressions, dynamic blocks, built-in functions, implicit and explicit dependencies, depends_on and lifecycle.
- 4 min
- Conditional syntax is
condition ? a : b. [for ...]builds a list,{for ... : k => v}builds a map, andiffilters.- Dynamic blocks repeat nested blocks; the iterator takes the block label name.
What it is
Expressions compute values instead of hard-coding them. With conditionals, for expressions, dynamic blocks and built-in functions, one configuration can adapt to different inputs.
Dependencies decide the order in which Terraform acts. Most come from references between blocks; depends_on covers the rare case where no reference exists.
How it works
Conditional expressions
A conditional picks one of two values: condition ? true_value : false_value.
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.env == "prod" ? "m5.large" : "t3.micro"
}
Both results should have the same type. A common pattern is count = var.create ? 1 : 0 to create a resource only when a flag is true.
for expressions
A for expression transforms one collection into another. Square brackets produce a list or tuple, braces produce an object or map.
locals {
upper_names = [for n in var.names : upper(n)]
name_to_len = { for n in var.names : n => length(n) }
prod_only = [for s in var.servers : s.name if s.env == "prod"]
}
The optional if clause filters elements. A for expression builds a value; it does not create resources by itself.
dynamic blocks
A dynamic block generates repeated nested blocks, such as several ingress rules, from a collection.
resource "aws_security_group" "web" {
dynamic "ingress" {
for_each = var.ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
Inside content, the iterator is named after the block label, here ingress, with .key and .value. The iterator argument renames it. Dynamic blocks generate nested blocks only, never whole resources.
| Construct | Repeats | Driven by |
|---|---|---|
| count | Whole resource or module | A whole number |
| for_each | Whole resource or module | A map or a set of strings |
| dynamic block | Nested blocks inside a resource | Any collection |
| for expression | Values only | Any collection |
Built-in functions
Terraform has only built-in functions plus functions that providers supply; you cannot write your own. Calls look like name(arg1, arg2).
| Function | Result |
|---|---|
upper("web") | "WEB" |
join("-", ["a", "b"]) | "a-b" |
length(["a", "b", "c"]) | 3 |
lookup(var.amis, "eu", "none") | Value for key, or the default |
merge(map1, map2) | One map, later keys win |
cidrsubnet("10.0.0.0/16", 8, 1) | "10.0.1.0/24" |
file("init.sh") | File content as a string |
templatefile("init.tftpl", { port = 80 }) | Rendered template |
toset(["a", "a", "b"]) | A set of "a" and "b" |
Run terraform console to try expressions and functions against the current configuration and state without changing anything.
Implicit and explicit dependencies
A reference such as vpc_id = aws_vpc.main.id creates an implicit dependency. Terraform builds a dependency graph from these references and creates independent resources in parallel.
When a dependency is real but not visible in any argument, add depends_on with a list of references.
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.micro"
depends_on = [aws_iam_role_policy.app_s3]
}
Here the application needs an IAM policy at boot, but no argument refers to it. HashiCorp recommends depends_on only as a last resort, because it can make plans more conservative.
Lifecycle settings
The lifecycle block changes how Terraform replaces or protects a resource.
| Argument | Effect |
|---|---|
| create_before_destroy | Creates the replacement before destroying the old object |
| prevent_destroy | Rejects any plan that would destroy the resource |
| ignore_changes | Ignores changes to the listed attributes |
| replace_triggered_by | Replaces the resource when a referenced item changes |
How the exam asks about this
- "Create a bastion host only when var.enable_bastion is true." Use
count = var.enable_bastion ? 1 : 0. - "An instance needs an S3 policy at boot but references nothing from it. How do you enforce order?" Add
depends_onto the instance. - "Generate one ingress rule per port in a list." Use a
dynamic "ingress"block withfor_each.
What to remember for the exam
- Conditional syntax is
condition ? a : b. [for ...]builds a list,{for ... : k => v}builds a map, andiffilters.- Dynamic blocks repeat nested blocks; the iterator takes the block label name.
- Only built-in and provider-defined functions exist; there are no user-defined functions.
- References create implicit dependencies;
depends_onadds explicit ones for hidden relationships. terraform consoletests expressions interactively.
for_each accepts a map or a set of strings, not a list. Convert a list with toset(var.names) before using it.
Common mistakes
Adding depends_on everywhere. If an argument already refers to another resource, the dependency exists. Extra depends_on entries add nothing and can slow plans down.
Using a dynamic block to repeat resources. Dynamic blocks only generate nested blocks. To create several resources, use count or for_each on the resource itself.
Mixing result types in a conditional. A conditional that returns a string on one side and a list on the other causes a type error. Both results must convert to one type.
Check yourself
How does Terraform decide the order in which to create resources?
It builds a dependency graph from references between blocks plus any
depends_onentries, then creates independent resources in parallel.What does
[for s in var.names : upper(s)]return?A list with every name in
var.namesconverted to upper case.Can you define your own function in Terraform configuration?
No. You can call built-in functions and functions supplied by providers, but the language has no way to define new ones.
Topic 3 of 4 in Terraform Configuration
Test yourself on Terraform Configuration
The full HashiCorp Terraform Associate 004 question bank has 600 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by HashiCorp. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.