Day 35: Mastering Terraform Modules

Adrian Rubico

|

Jun 22, 2025

01:38 AM GMT+8

Day 35: Mastering Terraform Modules

When you have Terraform projects on multiple resources or environments, repeating configurations can become inefficient and difficult to manage. This problem is solved by Terraform modules, which offer encapsulation and reuse of configurations. Using modules keeps your code clean and maintainable (DRY, Don't Repeat Yourself).

Understanding Module

What is a module in Terraform?

A module is a collection of .tf files grouped together to perform a specific function or define a particular set of infrastructure components. Terraform configurations set in folders can be considered modules in their simplest form.

There are two main types of modules:

  • Root module: The main configuration directory where you run terraform init, plan, and apply.
  • Child module: A module called from the root module or another module.

Why use modules?

Modules promote:

  • Reusability: Define a component once and use it multiple times.
  • Consistency: Enforce standards across environments.
  • Maintainability: Easier to update or fix issues in one place.
  • Collaboration: Enable teams to work on smaller, focused parts of infrastructure.

Understanding Child and Parent Modules in Terraform

A parent module is containers for multiple resources that are used together. The child module is simply any module that is called by another configuration.

Terraform Module Child and Parent.

💡Note:  Outputs from modules are not automatically visible in the parent configuration. You must explicitly define outputs in the child module and reference them from the parent module.

What is a module made of?

In Terraform module, configuration .tf files are organized into dedicated directories.

It is similar on this example directory structure below:

bash
├── modules
|   └── module_1
|       ├── main.tf
|       ├── variables.tf
|       └── outputs.tf
└── main.tf

A Terraform module usually consist of:

  • main.tf: This is where you defines all the infrastructure resources (like virtual machines or databases) and data sources (for fetching information about existing infrastructure) that the module will manage or interact with.
  • variables.tf: Declares the input variables that the module accepts from its calling configuration (the parent module or root module).
  • outputs.tf: Defines the output values that the module will expose to its parent module or to the user.
  • providers.tf (Optional but recommended): specifies the providers (e.g., aws, azurerm, google) that the module relies on.

Task

Let's now create and apply a Terraform configuration that uses modules. This task will guide you through a simple configuration into reusable module for better structure and scalability.

Step 1: Clone the Repository

Start by cloning the sample repository and navigate to the module directory.

bash
git clone https://github.com/git-adrianrubico/learn-terraform
cd learn-terraform/07-Module

Step 2: Review the Existing Module Folder

Here are the following files of the resource group module:

resource_group/main.tf 

hcl
resource "azurerm_resource_group" "rg-example" {
  name     = var.rgname
  location = var.azregion
  tags = {
    environment = local.env
  }
}

resource_group/variables.tf

hcl
variable "rgname" {
  default = "Name of Resource Group"
  type = string
}

variable "azregion" {
  description = "Location of Region"
  type        = string
  default     = "EAST US"
}

resource_group/outputs.tf

The purpose of this file is to expose values for use in other modules.

hcl
output "resouce_group_name" {
  value = azurerm_resource_group.rg-example.name
}

output "resource_group_location" {
  value = azurerm_resource_group.rg-example.location
}

output "resource_group_tag_env" {
  value = azurerm_resource_group.rg-example.tags
}

And here are the following files for the root module:

main.tf

As you can see, we can override the region even if a default region is specified in the resource group module's variables.

hcl
module "resource_group" {
  source   = "./resource_group"
  rgname   = "rg-module-example"
  azregion = "JAPAN EAST"
}

outputs.tf

hcl
output "resource_group_name" {
  value = module.resource_group.resouce_group_name
}

output "resouce_group_location" {
  value = module.resource_group.resource_group_location
}

output "resource_group_tag_env" {
  value = module.resource_group.resource_group_tag_env
}

Step 3: Initialize and Apply the Resource Group module

Apply to the infrastructure:

bash
terraform init
terraform plan
terraform apply
Terraform module resource group.

Verify in the Azure Portal:

Verify deploy resource group in Azure Portal.

Step 4: Create New Module for Storage Account

Create a new folder named storage_account and add the following .tf files:

storage_account/main.tf

hcl
resource "azurerm_storage_account" "sa-example" {
  name                     = var.stgname
  resource_group_name      = var.rgname
  location                 = var.rgloc
  account_tier             = var.acctier
  account_replication_type = var.reptype
  
  tags = {
    environment = var.tag
  }
}

storage_account/variables.tf

hcl
variable "stgname" {
  type = string
  description = "Name of Storage Account"
}

variable "rgname" {
  type = string
  description = "Name of Resource Group"
}

variable "rgloc" {
  type = string
  description = "Resource Group Location"
}

variable "acctier" {
  type = string
  description = "Storage Account Tier"
}

variable "reptype" {
  type = string
  description = "Storage Account Replication Type"
}

variable "tag" {
  type = map(string)
  description = "Tag Name"
}

storage_account/outputs.tf

hcl
output "storage_account_name" {
  value = azurerm_storage_account.sa-example.name
}

Step 5: Modify the main.tf from root module

Add storage_account module references:

hcl
module "storage_account" {
  source  = "./storage_account"
  stgname = "stgmoduletest01"
  rgname  = module.resource_group.resouce_group_name
  rgloc   = module.resource_group.resource_group_location
  acctier = "Standard"
  reptype = "LRS"
  tag     = module.resource_group.resource_group_tag_env
}

Step 6: Update outputs.tf from root module

hcl
output "storage_account_name" {
  value = module.storage_account.storage_account_name
}

Step 7: Initialize and Apply

Before applying changes, run:

bash
terraform init
Reinitialized storage account in Terraform Module.

This step reinitialized Terraform, downloading and preparing the modules you defined. It is required when you introduce new modules or change sources.

Then, proceed to:

bash
terraform plan
terraform apply

Step 8: Verify the created Storage Account in Azure Portal

Verify Storage Account in Azure Portal.

Conclusion

In this blog, we explored the fundamentals of Terraform modules. We learned how to separate configuration into reusable components by creating resource group a storage account module. This approach helps organize Terraform code, improve maintainability, and promote reuse across environments.

We also covered how to pass variables and expose outputs between child and parent modules. Understanding how to structure and reference modules is a key step in writing scalable and production-ready infrastructure code.

In the next blog, we will dive into Terraform Backends to understand how to manage and secure the state file remotely and support team collaboration.

Discussion