In Chapter 1: Terraform Environment Configuration, we explored how to define reusable blueprints for various environments such as dev and prod. Then, in Chapter 2: CI/CD Pipeline (GitHub Actions), we introduced automation via GitHub Actions, enabling consistent and reliable deployment of those blueprints.

Now, in this chapter, we shift our focus to the building blocks behind those blueprints: Terraform Modules. Modules allow us to encapsulate, reuse, and standardize infrastructure components—making our codebase more maintainable, scalable, and modular.

What Problem Are We Solving?

Consider the analogy of building with LEGO® bricks: rather than molding each brick from raw plastic, you use pre-made, reusable components like a 2x4 block, a window frame, or a wheelbase. Now imagine if you had to construct each of those pieces from scratch—every time you wanted to build a model. It would be inefficient, time-consuming, and prone to inconsistencies.

This is what managing cloud infrastructure without modules can feel like.

If you need to provision 10 similar servers, writing their configuration from scratch—or copying and pasting similar blocks—quickly leads to duplication. If a security policy changes, you'd need to update it in 10 separate places. This approach is not only inefficient but also introduces risk through inconsistency and human error.

Terraform Modules address this challenge by acting as reusable, composable building blocks for infrastructure—similar to LEGO bricks. Each module encapsulates a specific component (e.g., a VPC, an EKS cluster, or an IAM role) in a self-contained and standardized way.

In our project, rather than defining every detail of a VPC or EKS cluster directly in files like environments/dev/main.tf or environments/prod/main.tf, we leverage modules. This approach offers several benefits:

What Is a Terraform Module?

A Terraform module is a self-contained directory that houses one or more Terraform configuration files. At a minimum, a module typically includes:

Modules follow the same structure as root-level Terraform configurations but are explicitly designed for reuse and composition. Instead of executing them directly, you call a module from another configuration—such as the main.tf in your environment folder—passing in environment-specific inputs as needed.

How to Use a Module: The "Calling" Part

Let's look at how our main environment blueprints (like environments/prod/main.tf) use these modules. They "call" a module, passing in specific instructions (inputs) and receiving results (outputs).

Consider our environments/prod/main.tf file. It's designed to build an entire production environment. It does this by calling several modules: