
This post is part of a series on Terraform Modules Explained.
Modules are a foundational feature of Terraform and one of the most widely used features at that, this does not mean however that when they are used their use is well thought out. During my professional time working with Terraform I have seen some pretty interesting things when it comes to Terraform, whilst most of these things are generally benign I tend to find the extreme overuse of Terraform modules to be one of the most potentially dangerous. This behavior led me to write a post in August of 2020: Terraform; to module, or not to module with the sole intent of guiding engineers on when is a good time to create a module, the most important thing I wanted from that post however was to increase thought around the process of creating modules and not just doing it for the sake of it.
In today's post we will dive into a few important areas:
There are a few ways we can host our Terraform modules, in some cases we may be restricted to a particular method due to security or governance requirements.
module "awesome_module" {
source = "../modules/awesome-module"
...
}// Generic non-versioned git
module "awesome_module" {
source = "git::https://github.com/BrendanThompson/awesome-module.git"
...
}
// GitHub explicit version
module "awesome_github_module" {
source = "github.com/BrendanThompson/awesome-module?ref=v1.0.0"
...
}Using the ?ref= URL parameter at the end of our git endpoint we can specify a git tag, a branch, or even a particular commit.
module "awesome_module" {
source = "https://terraform.brendanthompson.com/modules/awesome-module"
...
}The above would simply do a redirect to our GitHub (or another git provider) endpoint as per the Remote Module with Git. With the HTTP endpoint, it is also possible to utilize an endpoint that returns a zip/tar.bz2/tar.gz/tar.xz archive, like the below:
module "awesome_module" {
source = "https://terraform.brendanthompson.com/modules/awesome-module.zip"
...
}Alternatively, it can use the archive query parameter:
module "awesome_module" {
source = "https://terraform.brendanthompson.com/modules/awesome-module?archive=zip"
}// AWS
module "awesome_module" {
source = "s3::https://s3.ap-southeast-2.amazonaws.com/brendanthompson/modules/awesome-module.zip"
...
}
// GCP
module "awesome_module" {
source = "gcs::https://www.googleapis.com/storage/v1/brendanthompson/modules/awesome-module.zip"
...
}// HashiCorp Public Repository
module "awesome_module_public" {
source = "ministry-of-magic/awesome-module/azurerm"
version = ">= 1.0.0, < 2.0.0"
...
}
// Terraform Cloud
module "awesome_module_tfc" {
source "app.terraform.io/ministry-of-magic/awesome-module/azurerm"
version = "1.0.0"
...
}
// Scalr
module "awesome_module_scalr" {
source = "ministry-of-magic.scalr.io/env-XXX/awesome-module/azurerm"
version = "~> 1.0"
}As we all know naming is a very contentious topic in the realms of IT, that is, unless it is a Terraform module (and you want to host it in a registry). Whilst local modules, or modules that you are going to be sourcing from most remote services can have any name at all if your module is going to be hosted on any registry that implements the Module Registry Protocol will require that your module be named in the terraform-
If you do not name your repository in the above format you are going to receive the following error:
{
"errors": [
{
"status": "422",
"title": "unprocessable entity",
"detail": "Validation failed: Name is invalid"
}
]
}If you're going to be moving to a Terraform Cloud/Enterprise, or any other solution that uses the Module Registry Protocol such as Scalr then you should think about setting your modules up with the above naming from the get-go.
The file structure of a module is almost identical to the structure of any other Terraform code, there is a small exception in that you should not provide a providers.tf file with your module code as that will come from the caller.
If we were going to create a simple module for an Azure Virtual Machine that could enable Private Link we would structure the repository like so:
.
├── README.md
├── examples/
├── linux.tf
├── network.tf
├── outputs.tf
├── private-link.tf
├── terraform.tf
├── tests/
├── variables.tf
└── windows.tfWe could name the repository in two ways:
You might consider option two if it was wrapping a few providers.
Let's go through each of these files and talk about what their function is.
As you can see there are a few files there even for a module that could be considered rather simple and adheres to Phase 3 - The Domain Files (My Terraform Development Workflow). It is exceptionally important when developing modules to be asking yourself these questions:
If you have a positive answer for all of the above then you meet the criteria for creating a module then you should be going for it. Remember your module should always make someone's life easier otherwise you might want to reconsider.
As an exercise let's answer the above three questions for our Virtual Machine module:
Hopefully, this post has given you some insight into getting started with Terraform modules, enough so that you can go and write and host your own!
You can follow Brendan @BrendanLiamT on Twitter.
