Introduction - Why Use Terraform Modules?

If you look back at my previous terraform blog post, you will see I have well over 100 lines of code in a single main.tf file. This was useful to me throughout the early learning phase; however, as Terraform users learn, all of them eventually come to realize that modules are the way of the future.

Let’s say you want to re-use bits of code, but change certain bits. For example, let’s say you want to create a webserver with the same userdata script every time the server is created, but for each server you create, you want the machine to be of a different instance type (say, of type t2.medium instead of t2.micro). Modules enable you the ability (kind of like classic c++ functions) to make attributes such as instance types in AWS into abstracted variables. This way, you can calll on the same module, but give the module different values so that the many instances you create can be varied, i.e, modularized.

The Structure of Terraform Modules

There’s not a huge difference between Terraform modules and your normal folder structure. Accompanying your main.tf file is always (or at least there should be) an outputs.tf file, your variables.tf file, and of course some variation of your main.tf file. The same can be said of your module files. Except with modules, variables are the name of the game. Sure, you could define all your code in a module to make your main.tf file more compact, but that doesn’t really take advantage of the full functionality of Terraform modules.

Ideally, modules should enable the user to make abstract as much of the small, minute details of provisioning resources as possible. Take a look at how a module’s main.tf file looks. In this example, I am provisioning an EC2 Instance with a userdata script - a basic Apache2 webserver that has useful modules installed at runtime.

EC2ModulePic

See the section I highlighted? That is one of the most important parts I want to talk about here: we should be focusing on abstracting as much of the code of our Terraform as we can. In a production environment, it is important for our code to not only be reusable, but also become flexible. Modules allow us to further deepen our code’s flexibility, as anyone who wants to use this module I created no longer has to be locked into specific AWS AMI’s, instance types, pem keys, availability zones, etc… They can enter all of the relevant dependency data manually themselves, so that regardless of their environment, developers can more quickly provision exactly the type of resources they need.

Calling Modules in Main.tf

How does main.tf provisioning look? To use the C++ comparison again, it’s really similar to passing variables itno a function when calling it. You will specify the module’s variables in the module’s version of main.tf, then you will define the variable types in variables.tf, and finally, you will define any outputs in outputs.tf (if any exist). The outputs discussion is a discussion for another day, though. Let’s take a look at our EC2 Instance’s module’s variables.tf file to understand how the variables are defined in the above picture.

The Variables.tf File:

variables.tf

Let’s be clear about something here. These variables, on their own, do not do ANYTHING for our module. They simply define the values that can/will be accepted when calling this module in your master main.tf file. You must make sure that the variable’s name (in orange quotes next to variable ) matches EXACTLY with the name as it is used in your module’s name. Look to the first picture in this blog for a look at the parity I have between the main.tf file of the EC2 Instance module and its variables.tf file, pictured directly above. These things are identical! The description, of course, can be whatever you want it to be.

Now that you have your module’s main.tf (or whatever else you named it) setup alongside your variables.tf, you must now actually make a module (or a function ;) ) call using these variable names with valid data to be passed into the module. Take a look below at our master main.tf file and its function calls.

MasterMain.TF

Besides the obvious benefit of our master main.tf file being only 40 lines of code, also take note of how we can specify specific module calls that are self-contained. What I mean by that is that you can make however many module calls you need to accomplish whatever it is you need to do. Let’s say you wanted to make a few different EC2 Instances with different availability zones and AMIs. You can do that with modules very quickly. And yes, it should be noted that the way I have setup this EC2 creation module is NOT the ideal method. There are ways to make one module call create however many instances from different AZs instead of having to make multiple instance calls. Perhaps I will cover how to use outputs.tf in the future, but for now, I think this gives you, dear reader, a fair bit to think about if this is your first foray into terraform modules.

Happy modulating!