✨ Sponsored Content ✨

Getting Started with Terraform Modules

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 lead 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 the 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:

Hosting Options#

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.

  // 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"
  }

Naming Modules#

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-<provider>-<name> format. Because of this requirement, your hosting repository must also be named in the same way.

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"
    }
  ]
}

File Structure#

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.tf

We could name the repository in two ways:

  1. With the provider: terraform-azurerm-virtual-machine
  2. With our organization: terraform-mom-virtual-machine

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:

  1. What does this module provide over using the provider resource?
  2. How does this module make my consumer's life easier?
  3. How does this module make an engineer's life easier to continue development on the module?

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:

  1. This module provides a simpler interface that can create either a Linux or Windows VM with the ability to enable Private Link in a single module block.
  2. As a consumer of the module you do not need to understand the inner workings of how Private Link works only that you require it to be enabled.
  3. As the module is split out into concise domain files modifying and extending the module is simple, because there is a module we can assume this is a standard within the organization thus if the standard needs to be changed it only has to occur in a single place to propagate out to the consumers.

Closing out#

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!


Brendan Thompson

Principal Cloud Engineer

Discuss on Twitter