How to Build a Terraform Reusable Module

A Terraform module is just a directory of .tf files with declared variables. When the same resource block appears three times with slightly different names, that's the signal to extract it - one module, three small calls, no more copy-paste.

DevOps Engineerterraformiacmodules

When to create a module

Copy-paste in Terraform is a common starting point: you need three services, so you write three identical resource blocks and swap the name. It works until you need to add a fourth, or change a setting in all three, or debug which paste is the canonical one.

The rule of thumb: if you have the same resource pattern repeated two or more times with only a few values changing, extract it into a module.

Module directory layout

A module is nothing more than a directory containing .tf files. By convention it has three files:

modules/service/
  variables.tf   # inputs the caller provides
  main.tf        # the actual resources, using var.*
  outputs.tf     # values the caller can read back

variables.tf - declare the inputs

variable "name" {
  description = "Service name (used in the config file path)"
  type        = string
}

variable "enabled" {
  description = "Whether the service is enabled"
  type        = bool
  default     = true
}

main.tf inside the module - use var.*

Inside the module every reference to the caller-provided values goes through var.:

resource "local_file" "config" {
  content  = "service=${var.name}\nenabled=${var.enabled}\n"
  filename = "/tmp/config-${var.name}.conf"
}

resource "null_resource" "marker" {
  triggers = {
    config = local_file.config.content
  }
}

outputs.tf - expose values to the root

output "config_path" {
  description = "Path to the generated config file"
  value       = local_file.config.filename
}

Root main.tf - call the module three times

The root module shrinks to three small module blocks. Each call passes a different name; everything else is encapsulated inside ./modules/service:

module "api" {
  source = "./modules/service"
  name   = "api"
}

module "worker" {
  source = "./modules/service"
  name   = "worker"
}

module "scheduler" {
  source = "./modules/service"
  name   = "scheduler"
}

Re-run terraform init after creating the module directory - Terraform caches module sources and won't pick up new ones without a fresh init. The plan output will show resources namespaced under the module call: module.api.local_file.config, module.worker.null_resource.marker, and so on. The resource count stays the same; only the addresses change.

Reading a module output from the root

output "api_config_path" {
  value = module.api.config_path
}

The root references module.<call_name>.<output_name>. This is how modules expose their internals selectively - the caller sees only what outputs.tf declares.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is a Terraform module?

A module is a directory of .tf files - variables.tf declares inputs, main.tf holds the resources, outputs.tf exposes values to the caller. The root configuration calls it with a module block and a source path. Any directory with .tf files is a valid module.

When should I extract a Terraform module?

When the same resource pattern appears two or more times with only a few values changing. Extract the repeated block into a module, declare the varying values as variables, and call the module once per instance with different inputs.

Do I need to run terraform init again after creating a module?

Yes. Terraform caches module sources during init. After adding or moving a module directory, remove .terraform/ and .terraform.lock.hcl, then run terraform init again so Terraform discovers the new module path.

Keep learning

Fix the Nginx 502 Bad GatewayDevOps projectRecover a Crashed Linux ServiceDevOps projectCorrect a Postgres Port MismatchDevOps projectDevOps roadmapStep by step to hiredDevOps interview questionsSTAR answersAll DevOps projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →