# terraform / locals / expressions
Don't Repeat Yourself: Local Values
A local value assigns a name to an expression so you can use it multiple times within a module without repeating code. Unlike input variables, locals cannot be overridden from outside — they are computed internally to enforce naming rules, merge common tags, and simplify complex expressions.
What is a Local Value?
Think of Input Variables as function parameters that someone else passes in, and Local Values as internal local variables created inside the function body to store intermediate calculations.
When to Use Local Values
- Standardized Naming: Concatenating
"${var.env}-${var.app_name}-${var.region}"once instead of typing it on 20 resources. - Common Tag Sets: Enforcing mandatory compliance tags (Owner, Environment, CostCenter, ManagedBy) across all cloud assets.
- Complex Calculations: Storing ternary conditions or formatting CIDR blocks cleanly.
Scoped to Current Module
Locals are strictly internal. A parent module or CLI argument cannot override or read a child module's locals block. This encapsulates business logic safely.
Reference Any Variable or Resource
Locals can reference variables (var.foo), other locals (local.bar), and exported resource attributes (aws_vpc.main.id).
Declaring & Referencing Locals
locals { service_name = "billing-api" owner = "shan-devops" prefix = "${var.environment}-${local.service_name}" is_prod = var.environment == "production" ? true : false instance_type= local.is_prod ? "m5.large" : "t3.micro" }
resource "aws_instance" "web" { ami = "ami-0123456789abcdef0" instance_type = local.instance_type tags = { Name = "${local.prefix}-web-server" } }
Notice the reference syntax: You declare with locals { ... } (plural), but when referencing you use local.name (singular).
Enterprise Tagging with merge()
Every enterprise cloud governance policy requires tags for cost tracking, auditing, and ownership. Defining a central common_tags map inside a local block and combining it with resource-specific tags via the merge() function is the gold standard.
Single Place to Update
If finance requests a new CostCenter = "CC-904" tag across 50 resources, you add one line in locals.tf and Terraform updates every resource cleanly on next apply.
locals { common_tags = { Project = "ECommerce-Portal" Environment = var.environment ManagedBy = "Terraform" Owner = "Shan DevOps" } } resource "aws_s3_bucket" "app_data" { bucket = "shan-app-storage-${var.environment}" tags = merge(local.common_tags, { Name = "app-storage-bucket" DataPrivacy = "Confidential" }) }
Complex Expressions & String Manipulation
locals { # List slicing and joining azs = ["ap-south-1a", "ap-south-1b", "ap-south-1c"] active_azs = slice(local.azs, 0, var.az_count) # Dynamic S3 bucket naming (lower-cased and sanitized) bucket_name = lower(replace("${var.company}-${var.project}-${var.environment}", "_", "-")) # Conditional count enable_bastion = var.environment == "prod" ? 1 : 0 }
Clean Resource Blocks
Without locals, resource blocks become bloated with nested functions like lower(replace(...)) or complex ternary statements. Shifting computation into locals.tf keeps main.tf clean and declarative.
Debug with terraform console
You can test and evaluate local expressions interactively in the CLI using terraform console before applying changes.
Variables vs Locals vs Outputs vs Data
| Type | Syntax Prefix | Who Sets Value? | Overridable from CLI? | Primary Purpose |
|---|---|---|---|---|
| Input Variable | var.name | User / CI/CD pipeline | Yes | External parameters & environment tuning |
| Local Value | local.name | Internal module logic | No | DRY expressions, naming rules, tag maps |
| Output Value | output.name | Exported by resource | No | Return values for CLI or parent modules |
| Data Source | data.type.name | Cloud Provider API | No | Read existing infrastructure external to code |
Best Practices & Pitfalls to Avoid
✓ DO
- Use locals for values referenced multiple times in the same module.
- Use locals for common tag maps and standard naming prefixes.
- Store locals in a dedicated
locals.tffile when projects grow.
✗ DON'T
- Don't create a local for every single hardcoded literal used only once.
- Don't use locals when users need to customize the value per environment (use variables).
- Avoid creating circular local dependencies (e.g. A references B, B references A).