Enterprise Azure Terraform Lab Phase 1 Part 2: Terraform Variables and Locals Explained
📁 GitHub Repository: lwc-wk/enterprise-azure-lab1
Enterprise Azure Terraform Lab Phase 1 Part 2: Terraform Variables and Locals Explained
🧩 Why This Note Exists
Before building the Azure infrastructure, I realized one important thing:
Terraform is not only about creating resources. It is about controlling how resources are described, named, reused, and managed.
In my Azure Terraform lab, two files are especially important:
infra/generic-input-variables.tf
infra/locals.tf
At first, these files may look like small supporting files. However, they are actually part of the foundation of the whole lab.
They control:
- What values can be changed from outside Terraform
- How resources are named
- How common tags are applied
- How the project stays consistent
- How the same Terraform code can be reused for different environments
This blog is a note-style explanation of how these files work and why they are necessary.
1. Basic Terraform Concept
Terraform is an Infrastructure as Code tool.
Instead of manually clicking resources in the Azure Portal, I describe the infrastructure using .tf files.
Example:
I want a Resource Group.
I want a Virtual Network.
I want two subnets.
I want a Storage Account.
I want a Log Analytics Workspace.
Terraform reads the configuration and talks to Azure through the AzureRM provider.
The normal Terraform workflow is:
terraform init
terraform fmt
terraform validate
terraform plan
terraform apply
terraform destroy
What Each Command Means
| Command | Purpose |
|---|---|
terraform init |
Initializes Terraform and downloads required providers |
terraform fmt |
Formats Terraform files consistently |
terraform validate |
Checks whether the configuration is valid |
terraform plan |
Shows what Terraform will create, update, or delete |
terraform apply |
Applies the changes to Azure |
terraform destroy |
Deletes Terraform-managed resources |
The most important mindset is:
Terraform does not just deploy resources. Terraform compares desired state with real state.
That means Terraform looks at the code, checks the current Azure environment, and decides what needs to change.
2. Why Split Terraform Files?
Terraform does not require every resource to be in one large file.
As long as the files are in the same folder, Terraform reads them together.
In this lab, the Terraform files are split by responsibility:
infra/
├── backend.tf
├── versions.tf
├── providers.tf
├── generic-input-variables.tf
├── locals.tf
├── resource-group.tf
├── network.tf
├── security.tf
├── storage.tf
├── monitoring.tf
├── diagnostics.tf
├── app-service.tf
├── budget.tf
├── rbac.tf
└── outputs.tf
This makes the lab easier to understand.
Instead of putting everything into one giant main.tf, each file has a clear purpose.
For example:
| File | Purpose |
|---|---|
generic-input-variables.tf |
Defines values that can be customized |
locals.tf |
Defines calculated values used repeatedly |
resource-group.tf |
Creates the Azure Resource Group |
network.tf |
Creates the VNet and subnets |
security.tf |
Creates NSG rules and associations |
storage.tf |
Creates the Storage Account |
outputs.tf |
Prints useful values after deployment |
3. What Is generic-input-variables.tf?
The generic-input-variables.tf file defines input variables.
Input variables are values that can be changed without rewriting the main Terraform resource code.
Example concept:
Instead of hardcoding the location as "centralus" everywhere,
I define a variable once and reuse it across the project.
A simplified example:
variable "resource_group_location" {
description = "Azure region where resources will be created"
type = string
default = "centralus"
}
Then other Terraform files can reference it:
location = var.resource_group_location
This is useful because I can change the region from one place instead of editing many resource files.
4. Example Variables Used in This Lab
In this lab, the variables are designed to support naming, environment separation, cost control, and ownership.
Example:
variable "business_division" {
description = "Business division or team name"
type = string
default = "infra"
}
variable "environment" {
description = "Environment name such as dev, test, or prod"
type = string
default = "dev"
}
variable "project_name" {
description = "Project name used for resource naming"
type = string
default = "azlab2"
}
variable "resource_group_location" {
description = "Azure region where resources are deployed"
type = string
default = "centralus"
}
variable "owner" {
description = "Owner tag value"
type = string
default = "lw"
}
These variables allow the same Terraform code to produce different environments.
For example:
business_division = infra
environment = dev
project_name = azlab2
can produce names like:
rg-infra-dev-azlab2
vnet-infra-dev-azlab2
nsg-infra-dev-azlab2
law-infra-dev-azlab2
This is much better than random manual names.
5. Why Variables Are Necessary
Variables are necessary because infrastructure should be flexible.
Without variables, I would need to hardcode values inside every resource.
Bad example:
resource "azurerm_resource_group" "lab" {
name = "rg-infra-dev-azlab2"
location = "centralus"
}
This works, but it is not reusable.
A better example:
resource "azurerm_resource_group" "lab" {
name = local.resource_group_name
location = var.resource_group_location
}
Now the resource is controlled by variables and locals.
This makes the Terraform code:
- Easier to reuse
- Easier to test
- Easier to change
- Easier to explain
- Closer to real enterprise infrastructure practice
6. What Is dev.tfvars?
Variables can have default values, but environment-specific values are usually placed inside a .tfvars file.
In this lab, the development environment uses:
env/dev.tfvars
Example:
business_division = "infra"
environment = "dev"
project_name = "azlab2"
resource_group_location = "centralus"
owner = "lw"
monthly_budget_amount = 10
budget_start_date = "2026-05-01T00:00:00Z"
Then Terraform can be run with:
terraform plan -var-file="../env/dev.tfvars"
terraform apply -var-file="../env/dev.tfvars"
This means the .tf files define the infrastructure logic, while the .tfvars file provides the environment-specific input values.
A simple way to understand it:
.tf files = infrastructure blueprint
.tfvars file = environment configuration
7. What Is locals.tf?
The locals.tf file defines local values.
Locals are calculated values used inside the Terraform configuration.
They are not passed from outside like variables.
They are created inside Terraform to avoid repeating logic.
Example:
locals {
resource_name_prefix = "${var.business_division}-${var.environment}-${var.project_name}"
}
If the variables are:
business_division = infra
environment = dev
project_name = azlab2
Then the local value becomes:
infra-dev-azlab2
This prefix can then be reused everywhere.
Example:
name = "rg-${local.resource_name_prefix}"
Result:
rg-infra-dev-azlab2
This keeps naming consistent across all resources.
8. Variables vs Locals
At first, variables and locals can look similar, but they have different purposes.
| Concept | Purpose | Example |
|---|---|---|
| Variable | Value passed into Terraform | var.environment |
| Local | Value calculated inside Terraform | local.resource_name_prefix |
Simple explanation:
Variables are inputs.
Locals are internal calculations.
For example:
variable "environment" {
type = string
default = "dev"
}
locals {
resource_name_prefix = "infra-${var.environment}-azlab2"
}
Here, environment is an input.
resource_name_prefix is calculated from that input.
9. Common Tags in locals.tf
Tags are important in Azure because they help with governance, ownership, and cost visibility.
Instead of writing tags manually on every resource, I define common tags once in locals.tf.
Example:
locals {
common_tags = {
Project = "enterprise-azure-lab"
Environment = var.environment
Owner = var.owner
ManagedBy = "terraform"
CostControl = "destroy-after-validation"
}
}
Then resources can use:
tags = local.common_tags
This means all resources can receive the same standard tags.
Why this matters:
- Easier cost tracking
- Easier ownership identification
- Easier cleanup
- Better governance habit
- More professional lab design
10. Naming Pattern in This Lab
The lab uses a consistent naming pattern:
<resource-type>-<business-division>-<environment>-<project-name>
Example:
rg-infra-dev-azlab2
vnet-infra-dev-azlab2
nsg-infra-dev-azlab2
law-infra-dev-azlab2
asp-infra-dev-azlab2
This naming pattern is simple, but it is much better than random resource names.
Good naming helps answer:
- What is this resource?
- Which project does it belong to?
- Which environment is it for?
- Who manages it?
- Can it be safely destroyed?
In a real company, naming standards are even more strict, but this lab version is good enough to show the concept.
11. Why Storage Account Names Need Random Suffixes
Some Azure resource names must be globally unique.
Storage Account names are one example.
That means this name cannot already exist anywhere in Azure:
stdevazlab2
To avoid naming conflicts, the lab uses a random suffix.
Example concept:
resource "random_string" "storage_suffix" {
length = 6
upper = false
special = false
}
Then the Storage Account name can include that suffix:
name = "st${var.environment}${var.project_name}${random_string.storage_suffix.result}"
Example result:
stdevazlab2a1b2c3
This solves the global uniqueness problem.
12. How These Files Work Together
The relationship looks like this:
generic-input-variables.tf
↓
env/dev.tfvars
↓
locals.tf
↓
resource files
↓
Azure resources
More specifically:
Variables define what can be customized.
.tfvars provides real values for one environment.
Locals calculate reusable names and tags.
Resource files use variables and locals to create Azure resources.
Example flow:
var.business_division = "infra"
var.environment = "dev"
var.project_name = "azlab2"
local.resource_name_prefix = "infra-dev-azlab2"
Resource Group name = "rg-infra-dev-azlab2"
This is the reason generic-input-variables.tf and locals.tf are not optional decoration.
They are part of the control system of the whole lab.
13. Common Beginner Confusion
Confusion 1: Are Variables and Locals the Same?
No.
Variables are values provided to Terraform.
Locals are values calculated inside Terraform.
variable = input
local = calculated helper value
Confusion 2: Do File Names Matter?
Mostly no.
Terraform reads all .tf files in the same directory.
These two structures can both work:
main.tf
or:
resource-group.tf
network.tf
storage.tf
locals.tf
The second approach is better for readability.
Confusion 3: Why Not Hardcode Everything?
Hardcoding works for a quick test, but it becomes messy quickly.
For a portfolio project, reusable structure looks more professional.
Confusion 4: Why Use Tags?
Tags are useful for ownership, billing, governance, and cleanup.
Even in a lab, tags show enterprise-style thinking.
14. My Key Takeaway
The main lesson is:
Terraform structure matters before the infrastructure gets complicated.
generic-input-variables.tf defines the inputs.
dev.tfvars provides environment-specific values.
locals.tf builds consistent names and tags.
The resource files then use those values to deploy Azure infrastructure in a predictable way.
This is why these files are necessary.
They make the lab:
- Cleaner
- Safer
- More reusable
- Easier to explain
- More professional
- More suitable for interview discussion
In simple words:
Variables make Terraform flexible. Locals make Terraform clean.
That is the foundation before moving deeper into Resource Groups, networking, security, monitoring, App Service, RBAC, and cost control.