Terraform Features I Use Every Day (for_each, count, dynamic, locals, depends_on, lifecycle, ...)
Learn the Terraform features you'll use in almost every real-world project, including for_each, count, locals, dynamic blocks, depends_on, and lifecycle.

Terraform Features I Use Every Day
In the previous articles, we've learned:
- How Terraform works
- What Terraform State is
- Variables and Outputs
- Modules
- Data Sources
At this point, you already know enough to build a basic Terraform project.
However, if you browse production Terraform projects on GitHub or within enterprise codebases, you'll frequently encounter syntax like:
for_each = ...
count = ...
locals {
...
}
dynamic "ingress" {
...
}
lifecycle {
...
}
depends_on = [...]For newcomers, Terraform can suddenly feel overwhelming.
"Why does Terraform have so many keywords?"
Fortunately, it doesn't.
Terraform actually has a very small language.
In reality, there are only six or seven core features that appear in almost every project.
Once you understand them, you'll be able to read most Terraform code you'll encounter in the real world.
Terraform Is Not a Programming Language
This is an important mindset shift.
Terraform is not Java.
It's not Go.
It's not Python.
Terraform doesn't have:
- classes
- functions
- traditional loops
- full if/else statements
Terraform is a declarative language.
Instead of writing instructions step by step, you simply describe:
"This is what my infrastructure should look like."
Terraform figures out how to get there.
Because of that, features like:
for_eachcountdynamic
aren't loops in the traditional programming sense.
They simply allow Terraform to generate multiple resources from a single template.
The Features You'll Use Most Often
Across nearly every Terraform project I work on, these are the features I use repeatedly.
| Feature | Purpose |
|---|---|
| locals | Create intermediate values |
| for_each | Create multiple resources from a map or set |
| count | Create multiple resources by quantity |
| dynamic | Generate nested blocks |
| depends_on | Manually define dependencies |
| lifecycle | Control how Terraform updates resources |
If you're comfortable with these six features, you'll already understand 80–90% of production Terraform code.
locals — Intermediate Variables
Suppose multiple resources share the same naming prefix.
Instead of repeating it everywhere:
resource "aws_s3_bucket" "logs" {
bucket = "company-dev-logs"
}
resource "aws_s3_bucket" "backup" {
bucket = "company-dev-backup"
}You can define it once:
locals {
prefix = "company-dev"
}
resource "aws_s3_bucket" "logs" {
bucket = "${local.prefix}-logs"
}
resource "aws_s3_bucket" "backup" {
bucket = "${local.prefix}-backup"
}Then simply reference:
local.prefixwherever you need it.
locals Are Not Variables
This is one of the most common misconceptions.
A variable:
variable "environment" {}is provided from outside Terraform.
For example:
terraform apply \
-var="environment=dev"A local, however, is computed inside Terraform.
locals {
prefix = "company-${var.environment}"
}Think of it this way:
- Variables are inputs.
- Locals are calculated values derived from those inputs.
count — Create Multiple Resources by Quantity
Suppose you need three identical EC2 instances.
resource "aws_instance" "web" {
count = 3
instance_type = "t3.micro"
}Terraform creates:
web[0]
web[1]
web[2]
You can reference them like this:
aws_instance.web[0]When Should You Use count?
count works well when:
- every resource is identical
- only the index differs
Examples include:
- identical virtual machines
- identical disks
- identical worker nodes
The Downsides of count
Suppose you start with:
count = 3
Terraform creates:
0
1
2
Later you change it to:
count = 2
Terraform removes:
2
The real problem appears when the order of your input changes.
Because resources are identified by numeric indexes, Terraform may think existing resources have changed when they've merely shifted positions.
This is why modern Terraform projects generally prefer for_each whenever possible.
for_each — The Most Common Way to Create Multiple Resources
Consider this example:
locals {
buckets = {
logs = "company-logs"
backup = "company-backup"
assets = "company-assets"
}
}Then:
resource "aws_s3_bucket" "bucket" {
for_each = local.buckets
bucket = each.value
}Terraform creates:
bucket["logs"]
bucket["backup"]
bucket["assets"]
each.key vs each.value
For example:
locals {
buckets = {
logs = "company-logs"
}
}Then:
each.keyreturns:
logs
while:
each.valuereturns:
company-logs
Why Is for_each Better Than count?
Suppose your resources are:
logs
backup
assets
Later, you add:
media
Terraform only creates:
media
The existing resources remain untouched.
Nothing gets renumbered.
That's why for_each has become the preferred approach in most production Terraform projects.
dynamic Blocks — Generate Nested Blocks
Some resources contain repeated nested blocks.
For example:
resource "aws_security_group" "web" {
ingress {
...
}
ingress {
...
}
ingress {
...
}
}If you have dozens of rules, this quickly becomes difficult to maintain.
Terraform provides:
dynamic "ingress" {
}For example:
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidr
}
}Terraform automatically generates multiple:
ingress {}
ingress {}
ingress {}
blocks for you.
depends_on — Manual Dependencies
Most of the time, Terraform automatically determines resource dependencies.
For example:
resource "aws_instance" "web" {
subnet_id = aws_subnet.private.id
}Terraform understands:
Subnet
↓
EC2
without any additional configuration.
When Do You Need depends_on?
Sometimes dependencies aren't obvious.
For example:
depends_on = [
aws_iam_role.example
]Terraform will always create the IAM Role first.
Or perhaps:
CloudFront
↓
Route53
even though neither resource directly references the other.
Don't Overuse depends_on
Many beginners add:
depends_onto nearly every resource.
This usually makes Terraform's dependency graph more complicated than necessary.
Let Terraform infer dependencies whenever possible.
Only use depends_on when Terraform genuinely cannot determine the relationship itself.
lifecycle — Control Terraform's Behavior
One of Terraform's most powerful features is:
lifecycle {
}which provides several useful options.
create_before_destroy
By default, Terraform typically performs:
Destroy
↓
Create
This can introduce downtime.
Instead:
lifecycle {
create_before_destroy = true
}Terraform changes the order to:
Create
↓
Destroy
This is particularly useful when replacing:
- Load Balancers
- Launch Templates
- Auto Scaling Groups
- DNS resources
prevent_destroy
For example:
lifecycle {
prevent_destroy = true
}If someone accidentally runs:
terraform destroyTerraform refuses to delete the resource.
This is extremely valuable for critical infrastructure such as:
- Production databases
- KMS keys
- Data buckets
- DNS zones
ignore_changes
Example:
lifecycle {
ignore_changes = [
tags
]
}Terraform ignores changes to the tags attribute.
This is useful when:
- another system manages tags
- Azure Policy automatically adds tags
- AWS Organizations injects metadata
Without ignore_changes, every terraform plan would show unnecessary drift.
The Features I Use Most Often
Across my own Terraform projects, the usage frequency looks roughly like this:
| Feature | Frequency |
|---|---|
| locals | ⭐⭐⭐⭐⭐ |
| for_each | ⭐⭐⭐⭐⭐ |
| lifecycle | ⭐⭐⭐⭐ |
| data sources | ⭐⭐⭐⭐ |
| dynamic | ⭐⭐⭐ |
| depends_on | ⭐⭐ |
| count | ⭐⭐ |
In particular,
localsfor_each
appear in almost every module I write.
Summary
Terraform isn't a large language.
Most production code revolves around a handful of powerful features:
localsto eliminate duplicationfor_eachto create stable collections of resourcescountfor simple repeated resourcesdynamicto generate nested blocksdepends_onwhen Terraform cannot infer dependencieslifecycleto control how resources are created, updated, or destroyed
Once you're comfortable with these features, you'll be able to understand the majority of Terraform modules you find on GitHub or inside enterprise codebases.
In the next article, we'll move on to another important topic:
How should you organize a Terraform project as the number of resources keeps growing? In the next article, we'll explore how to structure modules, organize directories, and design a Terraform repository that is scalable, maintainable, and production-ready.
Continue the Series
Comments
Loading comments…