What is a Terraform module, and why should you modularize configuration?

4 minbeginnerterraformmodulesfundamentals

Quick Answer

A module is simply a directory of .tf files. Every Terraform configuration is a module, and any module can be called by another via a module block. You modularize to encapsulate a reusable piece of infrastructure (e.g., "a standard VPC" or "an ECS service") behind a small set of input variables and outputs, so consumers don't need to know its internals. This reduces duplication across environments/teams, enforces organizational standards (tagging, security groups) in one place, and makes changes safer since they're tested once and reused everywhere.

Detailed Answer

A key insight for interviews: every Terraform configuration is a module, even a plain directory of .tf files with no module blocks anywhere. It's technically "the root module." What people usually mean by "a module" is a reusable unit designed to be called from elsewhere.

Calling a module

module "vpc" {
  source = "./modules/vpc"

  cidr_block  = "10.0.0.0/16"
  environment = "prod"
}

Inside modules/vpc/, you'd have .tf files declaring variable blocks (cidr_block, environment) as its inputs, resources it creates internally, and output blocks exposing values like the created VPC's ID back to the caller.

Why modularize

  1. Encapsulation. A module hides implementation details behind a small, deliberate interface (its variables and outputs). Consumers don't need to know it internally creates a VPC, three subnets, a route table, and an internet gateway; they just pass cidr_block and get back vpc_id.
  2. Reuse across environments/teams. The same modules/vpc can be called from dev, staging, and prod root configurations with different variable values, guaranteeing structural consistency instead of three hand-written, slowly-diverging copies of near-identical VPC code.
  3. Enforcing standards centrally. If your org requires every resource to have specific tags, or every S3 bucket to have encryption enabled, baking that into a shared module means every consumer gets it automatically. Fixing an issue in one place fixes it everywhere the module is used (after consumers bump their version pin).
  4. Safer, smaller blast radius for changes. A well-tested, versioned module can be changed and validated once, rather than every team member independently copy-pasting and modifying raw resource blocks with slightly different mistakes.

The tradeoff

Modules add a layer of indirection. Over-modularizing trivial, single-use configuration can make it harder to see what's actually being created without digging into module internals. The rule of thumb: modularize genuinely repeated patterns (a standard VPC, a standard microservice deployment), not every single resource.

Related Resources