Overview

3 Terraform Variables and Modules

This chapter explains how Terraform enables reuse and composability through modules and a clear variable model. Modules encapsulate infrastructure patterns as reusable HCL components that consume inputs and return outputs, letting teams standardize best practices and compose higher-level platforms from lower-level resources. The chapter also introduces Terraform’s three “variable” concepts—input, output, and local—along with typing, input validation, and handling of sensitive data, all geared toward writing robust, maintainable, and secure Infrastructure as Code for Terraform and OpenTofu.

Modules come in several forms: root modules (entry points that configure providers), shared modules (versioned and distributed, often via registries), and submodules (internals that organize complex functionality). The chapter covers how to call modules using source, version, and provider aliasing; common file structure conventions; and best practices for consuming public or private registries with attention to versioning and code quality. Outputs are highlighted as the glue for composition, enabling one module’s results (such as IDs or IPs) to feed another, while optional dependencies can force ordering when needed. Provider configuration is reserved for root modules, and submodules help break large designs into smaller, testable pieces.

On variables, inputs define a module’s API, outputs expose results, and locals support internal logic; all values are immutable during a run. Strong typing and type constraints (string, number, bool, list, set, tuple, object, map, plus null and any) improve clarity and error messages, while the sensitive flag for inputs and outputs masks secrets in logs (though state must still be protected). The validation subblock adds guardrails, and recent improvements allow richer, cross-variable checks. The chapter culminates in a hands-on refactor of an EC2 example into a reusable module: defining inputs with types and regex validation, returning useful outputs, testing from a separate root workspace, and publishing for reuse—illustrating how thoughtful inputs, outputs, typing, and validation produce reliable, composable modules.

Module Scope for Inputs, Outputs, and Locals.
Module File Structure

Summary

  • Modules are the primary way that code is shared amongst developers using Terraform.
  • Input variables are used to customize module behavior by passing data into a module.
  • Output variables allow module users to pull information out of a module for reuse.
  • Local variables exist only inside modules and are used for internal data and logic processing.
  • All values have a type, and Inputs can be restricted to a specific type.
  • Terraform has many types, such as Strings, Numbers, Booleans, Lists, Sets, Tuples, Objects, and Maps.
  • Input variables can have additional validation checks using custom logic.

FAQ

What is a Terraform module and how is it different from a provider?A module is reusable Terraform code (pure HCL) that composes resources and data sources into higher‑level building blocks. A provider is a plugin that exposes low‑level resources and data sources (usually backed by an API). Modules can’t define new resource types; they use what providers offer, can combine multiple providers, and act like functions with inputs and outputs.
What kinds of modules exist: root, shared, and submodules?- Root module: Your working directory’s .tf files; configures providers and calls other modules. Running terraform init here creates a workspace.
- Shared module: A reusable module versioned and pulled from a registry or VCS (public or private).
- Submodule: A module shipped inside another module (commonly under modules/), used to organize logic but not published separately.
How do I use a module in configuration?Use a module block and set at least source (required) and optionally version (for registries). You pass variables as arguments on the block, and you can wire provider aliases via the providers argument. Example concepts: source can be a registry address, a Git URL, or a local path; version pins an allowed range so upgrades are controlled.
What file and folder structure is recommended for a module?Common layout (community convention):
- variables.tf: inputs
- outputs.tf: outputs
- main.tf and additional *.tf files: resources, data sources, logic
- modules/: submodules (each with its own structure)
- templates/: template files (if used)
- examples/: usage examples
- README.md: documentation (used by registries)
What are input, output, and local variables, and how do they scope?- Inputs: Parameters passed into a module (variable blocks), referenced as var.name.
- Outputs: Values a module returns (output blocks), consumed as module.module_name.output_name.
- Locals: Internal-only values (locals blocks), referenced as local.name.
Scopes: Only inputs cross into a module; only outputs leave it; locals never leave. All Terraform variables are immutable during a run (constants).
How do I define input variables and constrain their values?Define with variable "name" { ... } and optionally set:
- type: Type constraint (string, number, bool, list(...), map(...), object(...), tuple(...), set(...), any).
- default: Fallback value if not provided.
- sensitive: Mask value in plan/apply output.
- nullable: Whether null is allowed.
- validation blocks: Add condition and error_message for custom checks (e.g., regex, ranges). Reference in code as var.name.
How should I handle sensitive values in inputs and outputs?Mark inputs/outputs with sensitive = true to prevent CLI display and logging. Sensitivity propagates through expressions. Important: sensitive values can still be written to state; protect state storage accordingly. If an output derives from a sensitive value, mark the output as sensitive to avoid errors and accidental exposure.
How do I define and consume outputs?Use output "name" { value = ... } and optionally description, sensitive, and depends_on. Outputs commonly expose resource attributes for composition across modules. Consumers access them as attributes on the module block (module.my_mod.output_name). depends_on can force output evaluation after specific resources (rarely needed).
What are locals and when should I use them?locals { key = expression } defines internal constants for reuse and intermediate logic (e.g., common tags, computed strings). You can have multiple locals blocks; names must be unique across the module. Reference with local.name. Locals can reference other locals (no cycles) and inputs/expressions, but are not exposed outside the module except via outputs you define.
What are module registries and how do I choose/publish modules?- Sources: Public Terraform Registry, private registries (e.g., Terraform Cloud, Spacelift), Git hosts, or local paths.
- Versioning: Registries tie releases to VCS tags; use version constraints to control upgrades.
- Choosing: Check maintenance cadence, backward-compatibility policies, code quality, docs, and ownership (org vs individual).
- Private registries: Authenticate with terraform login; publishing usually triggers on repository tags.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free