Terraform Fundamentals · Module 03 of 12

Variables, locals, outputs and sensitive values

Parameterize configurations cleanly, derive reusable expressions with locals, expose intentional interfaces through outputs, and understand what sensitive actually protects.

Learning objectives

By the end of this module, you should be able to:

  • Define typed input variables with validation.
  • Use locals to avoid repeating derived expressions.
  • Expose outputs intentionally and explain the limits of sensitive markings.

Variables are the module input interface

Input variables accept values from callers, variable files, environment variables and CLI flags. Type constraints and validation move errors earlier in the workflow.

Locals name derived values

locals blocks calculate reusable expressions inside a module. They reduce repetition but are not externally configurable inputs.

Sensitive is a display control, not encryption

Marking a value sensitive suppresses it in many CLI/UI displays, but sensitive values can still be stored in state. Backend protection and secret-management practices remain necessary.

Worked examples

See the idea in practice.

Typed variable with validation

variable "environment" {
  type = string
  validation {
    condition     = contains(["dev", "test", "prod"], var.environment)
    error_message = "environment must be dev, test or prod"
  }
}

locals { name = "app-${var.environment}" }

output "name" { value = local.name }
  • Validation fails during planning when the input violates the contract.
  • The output creates an intentional root-module interface.
Hands-on lab

Lab — Build a typed module interface

Goal: Use validation, locals and outputs to create a predictable configuration interface.

Before you start

  • Terraform CLI only.
STEP 1

Write configuration

mkdir -p ~/gnu-terraform-labs/module03 && cd ~/gnu-terraform-labs/module03
cat > main.tf <<'EOF'
variable "environment" {
  type = string
  validation {
    condition     = contains(["dev", "test", "prod"], var.environment)
    error_message = "environment must be dev, test or prod"
  }
}

locals {
  service_name = "payments-${var.environment}"
}

resource "terraform_data" "service" {
  input = local.service_name
}

output "service_name" {
  value = terraform_data.service.output
}
EOF
STEP 2

Initialize and apply valid input

terraform init
terraform apply -auto-approve -var=environment=dev
STEP 3

Read the output

terraform output -raw service_name
STEP 4

Prove validation rejects invalid input

terraform plan -var=environment=production

Verify

  • The valid output is payments-dev.
  • The invalid value fails with the variable validation message.

Expected outcome

  • Configuration consumers receive a clear input contract and stable output.

If it fails

  • Use -var-file for larger value sets rather than long command lines.
  • Remember shell quoting when values contain spaces or special characters.
Real-world connection

Well-designed module inputs and outputs are API contracts; changing them deserves compatibility review.

Avoid these traps

Common mistakes

  • Using locals when callers need to configure a value.
  • Assuming sensitive values are encrypted automatically.
  • Leaving variables untyped when a stable module interface is intended.
Knowledge check

Can you explain it without looking back?

Open each answer only after you have tried to answer the question yourself.

1What are locals for?

Naming reusable derived expressions inside a module.

2What does sensitive = true guarantee?

Primarily display redaction; it does not encrypt state.

3Why add variable validation?

To reject invalid inputs early with a useful message.

My Learning

Record your progress for this module.

Progress is saved in this browser immediately. When you sign in to My Learning, it can also sync across devices.

Not started

Attempt the core lab and knowledge checks before marking the module complete.