A Simple Guide to Terraform Modules
In the second part of our series on Terraform, we will be discussing how to create abstractions over our Terraform resources.
We will be utilizing a feature in Terraform called modules.
But before we head into the technical details, let's discuss why we would want to abstract things.
This article assumes you already know the basics of Terraform. If not, please check out my introductory article on Terraform here.
Abstracting resources
Let's say you want to provision a Kubernetes cluster in AWS that is ready to host your services using Terraform.
To do that, you wouldn't just need a single EKS resource. You would need to set up at least five or six other resources.
Imagine that you have multiple environments, then you'd have to copy the code for all your environments.
But what if we could abstract all these multiple resources into one?
So instead of considering individual resources such as EKS, VPC, EC2, etc...
You would combine them all into whatever abstraction you would want. You could deploy an abstraction called "app," which would represent an application deployed on AWS and accessible by the public internet.
This way, you'd define your abstraction in one place and be able to use it as a module in multiple different environments. You could have an app for staging and production.
Another benefit of this approach is that you could allow self-service. In a DevOps culture, you'd want your developers to be able to provision their own resources in a controlled environment.
If you abstract things, you could have some sane defaults that would minimize blast radius and lower the cognitive load, as they don't have to know all the complexities behind a module.
Terraform modules
I kinda spoiled it out when I said modules. Nevertheless, Terraform has the ability to create abstractions over resources, and they are called modules.
Before heading to the technical details of modules, let's learn more about when we use modules and considerations before adopting them.
PS. Technically speaking, every Terraform configuration you write, even if it's just a single .tf
file is considered the "root module". If you use any other modules, then those are considered child modules. So, in summary, every Terraform configuration is essentially a module.
When to use modules?
- Reusable Components – If you have a need for a common set of resources over multiple projects/environments, then abstracting things with modules would be a good idea.
- Code Organization – Modules break down your big Terraform files into smaller, easier-to-handle pieces. This makes your code clearer and easier to keep up to date.
- Collaboration and Sharing – You can share modules with your team or others using Terraform. This helps everyone work together better and keeps your setups consistent.
- Versioning and Iteration – With modules, you can make and track changes in your code without impacting everything at once. This lets you try out changes safely before applying them widely.
- Compliance and Standards – If your organization needs to follow certain rules or standards, modules help make sure these are met throughout your infrastructure.
Things to Think About Before Using Modules
- Complexity – Overusing modules or creating very complex modules can make understanding and maintaining the code harder, especially for new team members.
- Dependency Management – Managing dependencies between modules can become challenging, especially in large-scale infrastructures with many interdependent modules.
- Versioning Issues – If not managed carefully, different versions of modules can lead to inconsistencies and deployment issues.
- Limited Flexibility – Sometimes, the general nature of modules might not fit all use cases, requiring either customization (which can increase complexity) or abandonment of the module approach for specific cases.
- Overhead –Creating and maintaining modules requires initial effort and overhead. For smaller projects, this might be more effort than it's worth.
- Learning Curve – For those new to Terraform, understanding how to use modules effectively can add to the learning curve.
Using an existing Terraform module
You can find existing Terraform modules on the Terraform Registry or directly on GitHub.
For this example, I've chosen the AWS VPC module.
To use the module, you'll need to include it in your Terraform configuration files. This typically involves specifying the module source and version and any required input variables. Here's a basic example:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.5.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
This block tells Terraform to use the AWS VPC module from the Terraform Registry, specifically version 5.5.0. We then have input variables that are specific to the module itself, so we have to read the documentation and figure out what it needs.
After adding the module to your configuration, run terraform init
.
This command initializes the Terraform configuration, downloading and installing the specified module. Once initialization is complete, you can apply your configuration with terraform apply
, which will prompt Terraform to provision the resources as defined in the module.