Terraform Fundamentals - Locals
Learn how to use Terraform locals to store computed values like naming conventions, tag maps, or command templates.
As Terraform configurations grow more dynamic and modular, maintaining readability becomes important. That’s where locals come in. Locals are a way to define internal constants, calculated values, or shorthand expressions that can be referenced throughout the code.
If you’re coming from an Object-Oriented Programming (OOP) background, think of locals as private helper variables or constants—internal values that exist to support the logic inside a class or method, but aren’t meant to be externally exposed. Similarily, in Terraform, locals aren’t user inputs or outputs—they’re for the configuration’s internal use only.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.
The difference between variables and locals comes down to purpose. Variables are for input—we expect them to be set from the outside. Locals, on the other hand, are for internal logic.
Syntax and structure
We define locals using a locals
block, as shown below:
locals {
resource_name = "${var.project}-${var.env}-${var.role}"
ssh_cidr = "${var.operator_ip}/32"
timestamp = formatdate("YYYY-MM-DD", timestamp())
}
We can then access them like this:
name = local.resource_name
We can define as many values inside a single locals
block as needed—or split them into multiple blocks. Terraform will merge all locals
blocks together.
What locals can store?
Locals can store any valid Terraform expression or data type, including:
Strings and interpolated strings
Lists and maps
for
expressionsmerge()
,concat()
, and other functionsComplex nested logic
Here’s an example with multiple types:
locals {
region_map = {
"us-east-1" = "Virginia"
"us-west-2" = "Oregon"
}
tags = merge(
var.default_tags,
{
ManagedBy = "Terraform"
Environment = var.env
}
)
ssh_command = "ssh ec2-user@${aws_instance.server.public_ip}"
}
In this example, we have created a region map, a reusable tags
map and a dynamic ssh_command
string.
Locals are useful for scenarios like:
Creating naming conventions used across multiple resources.
Defining reusable maps and computed values (like CIDR blocks or region-to-zone mapping).
Storing commands, file paths, or formatted strings for outputs.
Combining multiple variables into structured outputs (like tags or identifiers).
For example, the following locals
block creates a complex naming pattern to be used later:
locals {
instance_name = "${var.prefix}-${var.env}-${var.region}-${var.role}"
}
resource "aws_instance" "web" {
ami = var.ami
instance_type = var.instance_type
tags = {
Name = local.instance_name
}
}
In the above example, the instance_name
locals block stores a naming pattern that is composed of a user-defined prefix, environment, region and role. We then use this local as local.instance_name
to tag the instance with a name.
Note:
We cannot uselocal.instance_name
in place of"web"
in the resource block label. This is because Terraform requires resource block labels (like"web"
inaws_instance "web"
) to be static identifiers at plan time. These labels are part of how Terraform tracks infrastructure state (e.g.,aws_instance.web
). If Terraform allowed these to be dynamic, it wouldn't be able to track resources reliably across plan and apply operations.
Best practices for defining locals
✅ Use locals to encapsulate business logic and reduce repetition.
✅ Name them clearly and keep them close to the logic they support.
✅ Group related locals together—consider breaking into modules if locals grow too large.
✅ Prefer locals for internal logic; use variables for external input.
🚫 Don’t define secrets in locals unless they’re passed in securely (via variables).
From a red team perspective, locals improve stealth and consistency: by centralizing logic, we reduce the chance of mismatched tags, incorrect IP ranges, or flawed DNS entries—details that could break an op or raise suspicion.
TL;DR
- Terraform locals are internal constants or expressions that are used to store computed values like naming conventions, tag maps, or command templates.
- Unlike variables, locals can't be overridden.
- Think of them like private variables in object-oriented programming—supporting logic that doesn’t need to be exposed.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.