
Modules are one of the most widely used features in Terraform, but widely used does not mean well thought out. Over the years I've seen plenty of odd things in Terraform code. Most are harmless. The one that worries me is overusing modules, reaching for one when a plain resource would do. That's why I wrote a post in August of 2020, Terraform; to module, or not to module: I wanted engineers to think about when a module is actually worth creating, rather than building one out of habit.
This post covers a few important areas:
There are a few ways to host your Terraform modules. In some cases you're locked into a particular method because of 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 just redirects to our GitHub (or another git provider) endpoint, the same as Remote Module with Git. You can also point the HTTP endpoint at one 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 contentious topic in IT. The exception is a Terraform module you want to host in a registry. Local modules, and modules you source from most remote services, can have any name at all. But if your module is going to live on any registry that implements the Module Registry Protocol, it has to be named in the terraform-
If you don't name your repository in that format, you'll get 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 Terraform Cloud alternative 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.
A module's file structure is almost identical to any other Terraform code. There's one small exception: you shouldn't ship a providers.tf file with your module, since that comes from the caller.
Say we were creating a simple module for an Azure Virtual Machine that could enable Private Link. We'd 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.
Here's what each of these files does.
As you can see, that's a fair few files even for a module you'd call rather simple, and it follows Phase 3 - The Domain Files (My Terraform Development Workflow). When you're developing modules, it's really important to keep asking yourself these questions:
If you've got a positive answer to all of the above, you meet the criteria for creating a module and you should go for it. Remember, your module should always make someone's life easier. If it doesn't, you might want to reconsider.
As an exercise, here are the answers to 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.
