Terraform Modules - Tutorials and Examples

Explore the power and flexibility of Terraform modules in infrastructure as code (IaC). Learn how modules enhance reusability, abstraction, and scalability in managing cloud resources, complete with practical examples for AWS and Google Cloud.

Terraform has quickly become a go-to infrastructure as code (IaC) tool due to its powerful declarative syntax for provisioning and managing infrastructure efficiently. One key feature that distinguishes Terraform from its rivals is its module system.

In this blog, we will explore the Terraform module in detail with some practical examples.

What are Terraform Modules?

Terraform modules are reusable and encapsulated collections of Terraform configurations. They simplify managing resources, making your Terraform code more manageable and scalable. Modules make defining, configuring, and organizing resources modular and consistent while abstracting away their complexity to make Terraform code more scalable and maintainable.

To read more about Terraform, check out our blog on Terraform Template, and Terraform vs Cloud Formation.

Imagine building a web application comprising multiple microservices, each relying on its own resources (virtual machines, databases, and networking components). Instead of writing duplicate code across each microservice instance, why not create modules for individual microservices to reuse across instances instead? By doing this, you reduce duplication while maintaining consistency and keeping code-base management overhead to a minimum.

Try the Terraform Modules Lab for free

Terraform Modules Lab
Terraform Modules Lab

Benefits of Using Terraform Modules

Using Terraform modules brings several advantages to your infrastructure provisioning process:

Reusability: Modules allow you to organize infrastructure resources and configurations into containers you can repurpose across projects and environments. This reuse saves effort and reduces errors significantly.

Abstraction: Modules simplify resource creation and configuration processes for Terraform configuration files, making them more concise and understandable.

Encapsulation: Modules isolate resources and their dependencies, making it more straightforward for you to manage or modify individual pieces of your infrastructure without impacting others or hindering modularity in its codebase. This improves its modularity.

Versioning: Terraform modules can be versioned, making it easier to track changes and update dependencies in an orderly manner. This ensures that changes made do not cause unintended problems in your infrastructure.

Collaboration: Modules allow your team and the wider community to work more collaboratively by sharing them via Terraform Registry or private module repositories - encouraging best practices and standardizing infrastructure configurations.

Anatomy of a Terraform Module

Before we dive into practical examples, let's understand the structure of a typical Terraform module.

Module Directory: A module is organized within a directory with a specific structure. This directory should contain at least one .tf file that defines the module's resources.

Input Variables: Modules often define input variables that allow customization of resource configurations. These variables are declared in the module and can be set when the module is used.

Output Values: Modules can also specify output values, which are computed by the module and can be used by the calling configuration. Outputs enable you to retrieve information from a module, such as IP addresses or resource IDs.

Resource Definitions: The core of a module is its resource definitions. These define the infrastructure resources you want to create, such as virtual machines, networks, or databases.

Variable Values: When you use a module, you pass values to its input variables to customize the resource configurations. These values can be hard-coded or derived from other sources.

How to write a Terraform module

To write a Terraform module, you need to create a directory that contains the following files:

  • main.tf: This file defines the resources that are created by the module.
  • variables.tf: This file defines the variables that can be used to customize the module.
  • outputs.tf: This file defines the outputs that are produced by the module.

You can also include other files in your module, such as data sources, providers, and custom scripts.

Here is an example of a simple Terraform module that creates an AWS S3 bucket:

module "s3_bucket" {
  source = "hashicorp/aws/modules/s3_bucket"
 
  bucket_name = "my-bucket"
  region = "us-east-1"
}

This module uses the hashicorp/aws/modules/s3_bucket module, which is a pre-built module that can be found in the Terraform registry. The source attribute specifies the name of the module to use.

The bucket_name and region variables are used to customize the module. The bucket_name variable specifies the name of the S3 bucket to create, and the region variable specifies the AWS region where the bucket should be created.

Below is the outputs.tf file for this module:

output "bucket_arn" {
  value = aws_s3_bucket.bucket.arn
}
 
output "bucket_name" {
  value = aws_s3_bucket.bucket.bucket_name
}

This file defines two outputs: bucket_arn and bucket_name. The bucket_arn output is the ARN (Amazon Resource Name) of the S3 bucket, and the bucket_name output is the name of the S3 bucket.

Now, let's explore some practical examples of using Terraform modules.

Terraform Module Examples

Below are three examples demonstrating how to use Terraform modules.

Example 1: AWS VPC Module

Creating an AWS Virtual Private Cloud (VPC) is fundamental for many infrastructure deployments. Instead of defining the VPC configuration repeatedly, we can create a Terraform module for it.

Module Directory Structure:

modules/
  vpc/
	main.tf
	variables.tf

VPC Module Code (modules/vpc/main.tf):

resource "aws_vpc" "example" {
  cidr_block = var.cidr_block
  tags   	= { Name = var.name }
}

In this module, we define an AWS VPC resource and allow customization of the VPC and name using input variables.

Input Variables (modules/vpc/variables.tf):

variable "cidr_block" {
  description = "The CIDR block for the VPC."
}
 
variable "name" {
  description = "The name of the VPC."
}

The variables.tf file declares the input variables that can be set when the module is used.

Using the VPC Module (main.tf):

module "my_vpc" {
  source 	= "./modules/vpc"
  cidr_block = "10.0.0.0/16"
  name   	= "my-vpc"
}

In the main Terraform configuration, we use the module block to include the VPC module. We specify the module's source directory and provide values for the input variables.

Now, you can easily create multiple VPCs with different configurations by reusing this module.

Example 2: AWS EC2 Instance Module

Creating Amazon Elastic Compute Cloud (EC2) instances is another common task in AWS. Let's create a Terraform module for EC2 instances.

Module Directory Structure:

modules/
  ec2/
	main.tf
	variables.tf

EC2 Instance Module Code (modules/ec2/main.tf):

resource "aws_instance" "example" {
  ami       	= var.ami
  instance_type = var.instance_type
  subnet_id 	= var.subnet_id
  key_name  	= var.key_name
 
  tags = {
	Name = var.name
  }
}

In this module, we define an AWS EC2 instance resource and allow customization of the AMI, instance type, subnet, key name, and name using input variables.

Input Variables (modules/ec2/variables.tf):

variable "ami" {
  description = "The AMI ID for the EC2 instance."
}
 
variable "instance_type" {
  description = "The instance type for the EC2 instance."
}
 
variable "subnet_id" {
  description = "The subnet ID for the EC2 instance."
}
 
variable "key_name" {
  description = "Key pair to associate with the EC2 instance."
}
 
variable "name" {
  description = "The name of the EC2 instance."
}

The variables.tf file declares the input variables that can be set when using the module.

Using the EC2 Instance Module (main.tf):

module "my_ec2" {
  source    	= "./modules/ec2"
  ami       	= "ami-12345678"
  instance_type = "t2.micro"
  subnet_id 	= "subnet-01234567"
  key_name  	= "my-key-pair"
  name      	= "my-ec2-instance"
}

In the main Terraform configuration, we use the module block to include the EC2 instance module. We specify the module's source directory and provide values for the input variables.

With this module, you can easily create EC2 instances with different configurations across your infrastructure.

These examples demonstrate how Terraform modules promote code reuse, abstraction, and encapsulation. By following similar patterns, you can create modules for various infrastructure components, including databases, load balancers, and networking resources.

Example 3: Google Cloud Storage (GCS) Bucket Module

Google Cloud Storage (GCS) is a popular choice for object storage. Let's create a Terraform module for GCS buckets.

Module Directory Structure:

modules/
  gcs_bucket/
	main.tf
	variables.tf

GCS Bucket Module Code (modules/gcs_bucket/main.tf):

resource "google_storage_bucket" "example" {
  name 	= var.bucket_name
  location = var.location
}
 
output "bucket_name" {
  value = google_storage_bucket.example.name
}

In this module, we define a GCS bucket resource and allow customization of the bucket's name and location using input variables. We also define an output value to retrieve the bucket name for potential use in other configurations.

Input Variables (modules/gcs_bucket/variables.tf):

variable "bucket_name" {
  description = "The name of the GCS bucket."
}
 
variable "location" {
  description = "The location of the GCS bucket."
}

The variables.tf file declares the input variables that can be set when using the module.

Using the GCS Bucket Module (main.tf):

module "my_gcs_bucket" {
  source  	= "./modules/gcs_bucket"
  bucket_name = "my-example-bucket"
  location	= "us-central1"
}

In the main Terraform configuration, we use the module block to include the GCS bucket module. We specify the module's source directory and provide values for the input variables.

This module allows you to create GCS buckets with different configurations across your Google Cloud projects.

Best practices for writing Terraform modules

Here are some best practices for writing Terraform modules:

Use a standard module structure: The standard module structure is a directory structure that follows a specific pattern. This makes it easier to find and understand your modules.

Adopt a naming convention: Use a consistent naming convention for your modules. This will make it easier to identify your modules and their purpose.

Use variables carefully: Variables can be used to customize your modules, but you should use them carefully to avoid errors.

Put static files in a separate directory: Static files, such as templates and images, should be in a separate directory. This will make it easier to manage and update your modules.

Test your modules: It is essential to test your modules by using the Terraform plan and apply commands before using them in production.

Document your modules: It is important to document your modules so that others can understand how to use them. You can use the Terraform docs command to generate documentation for your modules.

Expose outputs: Outputs are the values that are produced by your modules. You should expose the outputs that are necessary for other modules to use.

Use data sources: Data sources allow you to extract information from existing resources or data stores, making your Terraform modules more dynamic and flexible. By tapping into existing data stores, you can dynamically retrieve required attributes or information and pass them as variables to modules for processing.

Limit the use of custom scripts: Custom scripts can be used to perform complex tasks. However, you should limit their use to avoid making your modules difficult to understand and maintain.

Include helper scripts in a separate directory: Helper scripts can be used to perform everyday tasks. To avoid cluttering your modules, you should put these scripts in a separate directory.

Test your Terraform knowledge with our free Terraform Challenges. They will help in polishing your infrastructure provisioning and management skills!

Conclusion

Terraform modules are powerful for creating reusable, maintainable, and scalable infrastructure code. They allow you to encapsulate and abstract infrastructure components, making it easier to manage complex environments. In this blog post, we covered the benefits of using Terraform modules and explored practical examples for creating modules for AWS resources like VPCs, EC2 instances, and RDS instances.

As you continue your journey with Terraform, incorporating modules into your workflow will become increasingly valuable. By following best practices and creating well-structured modules, you can manage your infrastructure more efficiently, regardless of its complexity or scale. Happy Terraforming!

Looking to gain or polish your Terraform skills? Check out our Terraform for Beginners Course.

To gain other Infrastructure as Code (IaC) skills, check out our IaC Learning Path.