Locals are especially useful for name prefixes, standard tags, and environment-aware identifiers. That keeps resources consistent without copying the same string logic everywhere.
Variables, Locals & Outputs
Parameterize infrastructure cleanly, reduce duplication, and expose the right values between stacks and modules without hardcoding environment-specific details.
🧒 Simple Explanation (ELI5)
Variables are the inputs you can change, locals are the helper values you calculate once and reuse, and outputs are the important results you want to show or pass to something else.
If Terraform files are a recipe, variables are ingredients you swap by environment, locals are shorthand notes that simplify the recipe, and outputs are the final values you hand to the next team.
🤔 Why Do We Need It?
- Hardcoded values make reuse painful.
- Locals reduce repeated string building and naming logic.
- Outputs make integration between modules and systems clearer.
🔧 Technical Explanation
variable "environment" {
type = string
default = "dev"
}
locals {
name_prefix = "platform-${var.environment}"
}
output "resource_group_name" {
value = azurerm_resource_group.platform.name
}| Concept | Best Use |
|---|---|
| Variable | Inputs that change by environment, workspace, or caller |
| Local | Derived values and naming helpers inside the configuration |
| Output | Values exposed to callers, operators, or downstream automation |
Good Terraform design makes inputs explicit, internal composition readable, and outputs intentional. Not every value should become an output.
🌍 Real-World Use Case
A platform module may take environment, location, and address_space as variables, build standard naming and tagging with locals, then output subnet IDs and resource group names for an AKS module or CI/CD pipeline to consume later.
🛠️ Hands-on
Reusable Azure Resource Group Example
variable "environment" {
type = string
}
variable "location" {
type = string
default = "eastus"
}
locals {
rg_name = "rg-platform-${var.environment}"
common_tags = {
environment = var.environment
managed_by = "terraform"
}
}
resource "azurerm_resource_group" "platform" {
name = local.rg_name
location = var.location
tags = local.common_tags
}
output "resource_group_name" {
value = azurerm_resource_group.platform.name
}Try It Yourself
- Add a variable validation rule for allowed environments.
- Create a local for a naming prefix reused across multiple resources.
- Decide which outputs are genuinely useful and which would just create noise.
🐛 Debugging Scenario
Problem: Values are not changing between environments as expected.
- Check where the variable is actually set: default, tfvars file, CLI input, workspace pattern, or CI variables.
- Review whether locals are using the intended variable.
- Confirm outputs refer to the current resource or module value rather than stale naming logic.
Teams often overuse outputs and expose far more internal details than callers need. Keep outputs purposeful to avoid tight coupling between modules.
📋 Interview Questions
Beginner
A variable is an input value used to customize the configuration without editing the resource blocks directly.
A local is an internal named expression used to simplify repeated or derived values in a configuration.
An output exposes a value from the Terraform configuration for operators, modules, or automation to consume.
Because it reduces reuse, increases mistakes, and makes multi-environment maintenance harder.
Defaults, tfvars files, environment variables, CLI arguments, or automation inputs.
Intermediate
When the value is derived from other values and should not be set directly by the caller.
They provide explicit values such as subnet IDs or resource names that other modules or pipelines can consume safely.
Too many variables, unclear naming, and outputs that expose internal details the caller should not need to care about.
Do not hardcode sensitive values. Use secret-aware input mechanisms and mark variables sensitive where appropriate.
It enforces consistency and makes governance easier across many resources.
Scenario-Based
I reduce the interface to the real inputs that should be caller-controlled, move derived logic into locals, and split responsibilities if the module is doing too much.
Outputs are part of the module interface and should be treated as stable contracts, especially when CI/CD or other stacks consume them.
I would keep environment inputs explicit through variables or tfvars files, use locals for naming and tags, and output only the values later modules or pipelines truly need.
Because a silent default can provision into the wrong region, wrong size, or wrong environment if the caller forgets to override it intentionally.
They are the important values Terraform shows you after building something, like the name, ID, or endpoint another step will need.
🧾 Summary
Variables, locals, and outputs are how Terraform becomes reusable instead of hardcoded. Good value design makes infrastructure clearer, easier to compose, and safer to operate across environments.