LAB 02 – Variables, validations, locals and outputs¶
Story: GreenGrid needs a dev and a prod environment from the same code – with the company naming and tagging standard enforced, and wrong input rejected before anything reaches Azure.
| Duration | ~40 min |
| You edit | variables.tf, new locals.tf, main.tf, outputs.tf, new env/*.tfvars |
| Solution | solutions/LAB02/ (or ./bin/catchup 2) |
| Block | Purpose |
|---|---|
variable |
input – differs per environment or user |
locals |
derived values and standards – computed once, used everywhere |
output |
results for people, pipelines and other configurations |
*.tfvars |
the values for one environment |
Step 1 – Variables with validation¶
Replace variables.tf:
variable "prefix" {
type = string
description = "Your personal prefix (2-8 lowercase letters/digits) - unique in the shared subscription"
validation {
condition = can(regex("^[a-z0-9]{2,8}$", var.prefix))
error_message = "prefix must be 2-8 lowercase letters or digits."
}
}
variable "environment" {
type = string
description = "Environment name"
validation {
condition = contains(["dev", "test", "prod"], var.environment)
error_message = "environment must be dev, test or prod."
}
}
variable "location" {
type = string
description = "Azure region"
default = "westeurope"
validation {
condition = contains(["westeurope", "northeurope", "swedencentral", "polandcentral"], var.location)
error_message = "Use an EU region approved by GreenGrid: westeurope, northeurope, swedencentral or polandcentral."
}
}
variable "owner" {
type = string
description = "Team or person responsible for the resources"
default = "platform-team"
}
variable "extra_tags" {
type = map(string)
description = "Additional tags merged into the standard tags"
default = {}
}
Step 2 – Locals: naming and tagging standard¶
locals.tf:
locals {
name_suffix = "${var.prefix}-greengrid-${var.environment}"
# Storage account: 3-24 characters, lowercase letters and digits only
storage_name = substr(lower(replace("st${var.prefix}gg${var.environment}${random_string.suffix.result}", "-", "")), 0, 24)
common_tags = merge(
{
project = "greengrid"
environment = var.environment
owner = var.owner
managed_by = "terraform"
},
var.extra_tags,
)
}
Use them in main.tf:
| Resource | Change |
|---|---|
| resource group | name = "rg-${local.name_suffix}", tags = local.common_tags |
| storage account | name = local.storage_name, tags = local.common_tags |
| storage account | account_replication_type = var.environment == "prod" ? "ZRS" : "LRS" |
| storage account | add blob_properties { versioning_enabled = true } |
Step 3 – One file per environment¶
mkdir -p env
cat > env/dev.tfvars <<'EOF'
prefix = "jk" # <- your own prefix
environment = "dev"
location = "westeurope"
EOF
cat > env/prod.tfvars <<'EOF'
prefix = "jk" # <- your own prefix
environment = "prod"
location = "westeurope"
owner = "operations"
EOF
rm terraform.tfvars
Add outputs (outputs.tf) for storage_blob_endpoint (azurerm_storage_account.data.primary_blob_endpoint) and tags (local.common_tags), each with a description.
✅ Checkpoint: the plan replaces the resource group and the storage account – their names changed (-greengrid → -greengrid-dev). Fine in a lab, a disaster in production (LAB04 shows moved blocks for renames that are only addresses). Apply.
Step 4 – Break the validations¶
Try each one and read the error:
terraform plan -var-file=env/dev.tfvars -var="environment=production"
terraform plan -var-file=env/dev.tfvars -var="prefix=Jan_K"
terraform plan -var-file=env/dev.tfvars -var="location=eastus"
✅ Checkpoint: three clear error messages – and not a single API call to Azure.
Variable precedence (highest wins): -var / -var-file on the command line → *.auto.tfvars → terraform.tfvars → TF_VAR_name environment variables → default.
Step 5 – terraform console¶
local.name_suffix
local.common_tags
lower("GreenGrid Meter-42")
replace("tf_train", "_", "-")
substr("abcdefghijklmnopqrstuvwxyz0123", 0, 24)
merge({ a = "1" }, { b = "2", a = "3" })
cidrsubnet("10.20.0.0/16", 8, 1)
Leave with exit.
Bonus¶
sensitive = trueon a variable – what changes in the plan output?- Add an
extra_tags = { costcenter = "4711" }line toenv/prod.tfvarsand plan prod. (Do not apply prod – it would be a second copy of everything.) terraform plan -var-file=env/dev.tfvars -out=dev.tfplanandterraform show dev.tfplan– a plan file applies exactly what you reviewed.
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
No value for required variable "environment" |
forgot -var-file=env/dev.tfvars |
Reference to undeclared local value |
locals.tf not saved, or a typo in local.<name> |
| validation passes although the value is wrong | condition uses another variable – only var.<self> is allowed in older Terraform; since 1.9 other variables work too |
Files for this lab: Think-Cube/terraform-azure-labs/labs/LAB02-variables-locals