In part one of the Into to Terraform series, we introduced the high-level concepts of Terraform and Infrastructure-as-Code in general. In part two, we looked at the basic building blocks of Terraform and how to construct resources. We saw how variables could be used to circumvent hardcoding individual resource details like IP addresses directly into Terraform code, making code more reusable and easy to edit.
While using variable parameters are a great step, the heart and soul of Infrastructure-as-Code lies in efficiency and reusability. A Terraform template is still not very easy to repurpose if you need to go through every variable and update resource details – such manual parsing takes a lot of time.
A famous phrase attributed to multiple scholars and philosophers from antiquity says that we “stand on the shoulders of giants” in order to further scientific and technological progress. In the course of Terraform, you can stand on the shoulders of your own templates by leveraging what are called Modules – defined by Hashicorp this way:
“A module is a container for multiple resources that are used together. Modules can be used to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects.”
Using modules allows for orchestration of resources in a much less manually taxing fashion, deploying resources by providing a simple list of variables within the module template which are then piped into the underlying resources to achieve any number of flexible deployment schematics – whatever suits your purpose. As the definition from Hashicorp says, modules make your template less about painstaking code maintenance and more about outlining an abstraction of what you need.
Let’s hop into a Terraform template for a simple AWS VPC deployment and check it out. I have a simple VPC terraform template in a public Github repo here: https://github.com/itprosource/aws_tf_example-vpc
Very simple deployment – we have a main.tf file holding all of our resources, and a variables.tf file declaring all of our variables. In main.tf, I’ve declared a VPC with a public/private subnet, internet gateway, and route tables for the public and private subnets.
As you can see, all of our key attributes are delineated in variables. Note that the defaults are empty – if we tried to run the template as-is, the Terraform engine would prompt us to provide values for these variables in the Cli. But we could also input those values directly into the file. Either way, we need to pipe in these values via variables.
Yes, we can go through our variable file and modify values. But what if – for example – we wanted to use this template multiple times, maybe as part of a leveraged offering for clients in which we quickly and efficiently stand up and maintain a Terraform-managed VPC? This means we’d need to maintain multiple different sets of variable files – and anyone with repo management experience understands why this would become unnecessarily difficult very quickly.
Thankfully, Terraform offers us “modules” to handle this very problem. To build a module, let’s first add a modules folder to our repo (and if you want to specify certain modules, create another folder with a name – which I have done below with “simple-vpc”):
Within our modules directory, let’s add another main.tf file (we’ll discuss some of these other files – like terraform.tfstate – in a later article):
In the module file, I’ll create a very simple module for deploying the resources seen in the base template:
Let’s break this down piece-by-piece.
Provider – The provider block is what tells the Terraform engine which cloud providers, SaaS platforms, or other APIs we intend to work with. The possibilites of what could go in your provider block are very open-ended, with some stretching on for many multiple lines – but as you can see in my Provider block here, very little info is needed. We use “aws” to specify that we’re connecting to AWS, and insert a region from AWS so that Terraform knows where to deploy resources (if I didn’t specify it here, the Cli would prompt us to specify a region at runtime – which might actually be how you want it done, depending on your use-case.)
Module “simple_vpc” – This is the start of our module block, where we declare our module along with a name – which is “simple_vpc” in this case. Each name must be unique – so if you deploy multiple modules in the same template (which is definitely possible and in-fact a key feature of modules), then you need to make sure the name value for each module in that file is different. Final note: Everything between the brackets constitutes our module.
source = “../..” – The source block tells the module where to look for it’s resources. Without it, the module wouldn’t have any clue what to deploy. In this case, I have my module nested 2 folders down: “modules/simple-vpc”. The two sets of dots indicate that the module needs to look two folders down to find it’s base resources.
…and the remainder of lines in the module (name, cidr, az, etc) are all simply stand-ins for our variables. We drop the value here, and the Terraform engine plugs those into the variables on the back-end. If we run a Terraform deployment here, it will give us the complete VPC deployment with precisely what we specified in the module.
The value here is obvious: updating the module is easy as pie. No need to parse multiple lines of code – just drop your values into a few key lines and you’re off to the races. If you think back to our initial problem of needing to use this base template for multiple VPCs, one possible and efficient solution is to use multiple module files. We can leave our source code alone (both resources and variables) and just build / manage different module files.
Modules are deeply flexible and we’ve just barely scratched the surface of how they can be used to optimize your code repo and deployment processes. I will eventually write an article looking at advanced module techniques – but this will get you started for now.
Come back for part IV of the Intro the Terraform series where we’ll look at a few various key concepts – like cli commands and Terraform state files. Check back soon!