Getty Images/iStockphoto

Learn how to use Terraform modules with examples

Running modules can benefit Terraform usability. Explore what a module in Terraform is, when you should use them, and run through some Terraform module examples.

Infrastructure as code is critical in modern DevOps toolchains. One popular tool, Terraform, can benefit from the use of modules to simplify configuration maintenance.

In Terraform, modules can group resources into logical units, letting you break down a complex configuration into smaller, reusable components. Doing so will make a Terraform configuration easier to maintain and can reduce redundancy between multiple Terraform projects. By writing and leveraging modules, you can simplify Terraform configurations, improve readability and increase code reusability.

To utilize modules, first develop a basic understanding of Terraform, such as what Terraform resources are and how to use them. Then, you can evaluate the types of resources you typically deploy in groups or multiple times and consider converting them to modules.

When to use Terraform modules

When you're deploying a resource or group of resources multiple times and possibly using a similar set of configurations, it might be time to consider combining them into a module. For example, say you deploy a set of virtual machines, each with a disk, a network interface controller and a static IP address. Combining these resources creates a logical unit called a virtual machine for Azure or an ec2-instance for AWS.

With that single unit, a module, you can create parameters to specify things such as VM size, disk size or IP address -- each with a smart default value. Then when you want to create a virtual machine and all the supporting resources, you can call the module once and it creates everything for you.

Module structure

Much like the main Terraform configuration root module, a child module should contain at least a variables.tf, a main.tf and an outputs.tf file in the same folder.

Screenshot of example module showing main.tf, output.tf and variables.tf files.
A child module should contain a main.tf, output.tf and variables.tf file.

The variables.tf file defines all input parameters. The main.tf file is used for the main Terraform script. The output.tf file is used to define all outputs. You can also optionally split out other parts of the module into their own files, such as the providers block.

Azure Virtual Network example

It is pointless to deploy a virtual network in Azure without a subnet. This makes Azure a good candidate to write a simple module that will deploy both a virtual network and a subnet. To build a module, create the three necessary files: main.tf, variables.tf and outputs.tf.

variables.tf

To create the variables.tf file, define variables using the following syntax.

variable "vnet_name" {
  type        = string
  description = "Name of the vNet"
}

Next, add the following additional parameters:

  • resource_group_name. Name of the resource group where the vNet will be placed.
  • Location. The Azure region where the resources will be placed.
  • vnet_address_space. Address space to assign to the vNet.
  • subnet_name. Name of the subnet.
  • subnet_address_space. Address space to assign to the subnet.

With these input variables defined, you can reference them inside of the Terraform script using the syntax var.var_name.

main.tf

Inside of the main.tf, place the resources that you want to create. This example simply creates a virtual network and subnet, so there are the following two resources.

resource "azurerm_virtual_network" "vnet" {
  resource_group_name = var.resource_group_name
  location            = var.location
  name                = var.vnet_name
  address_space       = [var.vnet_address_space]
}

resource "azurerm_subnet" "subnet" {
  resource_group_name  = var.resource_group_name
  name                 = var.subnet_name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = [var.subnet_address_space]
}

outputs.tf

Defining outputs for a Terraform module can be complicated, but the general idea is to output properties that you might need in a parent module but can't get from the input parameters. For example, you won't know the ID of the virtual network until after the virtual network is deployed, so you can output it with the following commands.

output "vnet_id" {
  description = "The ID of the vNet."
  value       = azurerm_virtual_network.vnet.id
}

You can create an output for any resource output in your Terraform module. This example sticks with just the virtual network ID.

Using a module

Now that you have written a module, the next step is to use the module. Take all three files (variables.tf, main.tf and outputs.tf) and place them into a folder named terraform-azurerm-vnet. Then in the parent folder, create the main.tf file for the parent module.

Screenshot of child module with added parent folders.
Create a terraform-azurerm-vnet parent folder to start using your module.

Modules in Terraform have their own keyword, so you can call the module using module and specify the location of the module using the source parameter.

module "vnet" {
  source = "./terraform-azurerm-vnet"
}

Terraform supports several different source types, including Git, the Terraform Registry and HTTP URLs. This example uses a local path.

Inside the module block, define the input parameters for the module with the following commands.

module "vnet" {
  source               = "./terraform-azurerm-vnet"
  resource_group_name  = "test-rg"
  location             = "westus"
  vnet_name            = "new-vnet"
  vnet_address_space   = "10.10.0.0/16"
  subnet_name          = "subnet01"
  subnet_address_space = "10.10.10.0/24"
}

Each input parameter matches up to a variable from the variables.tf file. If you also need to create the resource group using Terraform, you can do so with an additional resource in the same main.tf file.

resource "azurerm_resource_group" "rg" {
  name     = "test-rg"
  location = "westus"
}

Now add a provider block, and you can run through a plan.

Screenshot showing a running module in Terraform.
Terraform modules can run through plans with ease.

This is a simple example of how to use a Terraform module; therefore, it has limitations. If you want to create multiple subnets, you'll have to do that outside of the module. There are several options to extend this module to accept multiple subnets.

When working with complex Terraform configurations, it is convenient to specify a group of resources as a single unit using a module. While the virtual network example in this script is simplistic and only saves a few lines of code, it does make the code more readable. More complex modules could easily save 100+ lines of code and group all the VM-specific parameters in one module block.

Next Steps

Dive into Terraform basics

Deploy Azure landing zones using Terraform

Dig Deeper on Systems automation and orchestration