Terraform: Repeatable Infrastructure Basics

Katoria Henry
Geek Culture
Published in
8 min readFeb 13, 2023

--

It’s another week of Terraform magic and we’re going to keep it super simple for this tutorial. In my previous article, we talked about some of the gems of Terraform, such as Terraform taking the headache away by managing and scaling your deployments for high availability, along with knowing that your dependencies and resources will all be kept in one place. For this tutorial, we’re going to dive into creating modules for AWS resources, such as EC2 Instances, Security Groups, VPCs, etc. The assumption is that you’re familiar with what these resources are and the purpose they serve, so I won’t dive into detail about these things. It’s important to understand why modules are typically used compared to single scripts/files in Terraform, so let’s learn a little more about Modules and why they’re essential for your deployments.

If you understand what a general module is, adding Terraform to the mix only simplifies that even more. Modules are unique to use in Terraform in that you’re basically able to bundle/group together (containerize) a set of resources that generally consists of individual .tf files for your deployment configuration. This bundle of files is typically stored in a separate directory and are generally phrased as “reusable or repeatable infrastructure” as you’re able to reuse these files throughout your entire configuration (within other modules). Modules help to eliminate redundancies in code and provide Engineers with the ability to work on different parts of the code simultaneously, thereby improving overall collaboration.

It’s important to note that when creating your configuration files, you must pass any identified resources to the module as input variables or outputs as you’re not able to reference these in submodules or the parent module. Though there is some degree of isolation when using modules, you’re the deciding factor as to when you should use modules. For Production workloads, modules are typically the top choice versus individual .tf files.

Using Terraform Parent & Child Modules, we will execute the following:

  • Create a custom module for ec2 out of the resource block that you can use as repeatable infrastructure. Recommended to use an Amazon Linux 2 AMI
  • Create a variables.tf file for your ec2 module
  • Push the newly created module and the ec2.tf file to your repo
  • Create a Security Group module that allows for HTTP web access
  • Assign the security group to the EC2 module
  • Add a bootstrap script to your EC2 module to launch a webserver of your choice (Apache, Nginx, etc).
  • Add another module for a VPC
  • Add a public Subnet
  • Make sure to modify the EC2 module and the Security Group module to use the new VPC ID.

To see the full list of modules and files we will be creating, feel free to fork my repo found here:

Resources/PreReqs:

  • As always, Confidence to get it done!
  • AWS Account
  • GitHub Account integrated with VSCode
  • Terraform Installed
  • Knowledge of basic Linux commands
  • Source Code Editor (I use Visual Studio Code (VS Code)) w/ the Terraform Extension Installed
  • AWS CLI installed locally

Part 1

The first step that one should take before jumping into file creation, is to ensure you have a new directory in which your files will be stored. Be sure to create a NEW git repository, clone it, and start working in your new directory with the cloned repo. To kick things off, let’s follow the steps below (this will start of with some basic Linux commands):

  • In your terminal, enter the command below to create a new directory:
mkdir <directoryname>
  • You’ll then need to change into the newly created directory, by running the command below:
cd <directoryname>
  • Because we’ll be using a parent and child modules, let’s create our first terraform monolith file, which will include the terraform required providers, the provider, and a single resource for an AWS EC2 instance, which should look like this after running the commands below:
touch <filename>
vi <filename>
  • Next, we’re going to break down this file into individual files (I know, we could have done that to begin with, but this is a learning process for those that are new to Terraform 😊)
  • Let’s proceed with creating the files needed to breakdown our monolith file, which will serve as our root module. Run the command below:
touch main.tf variables.tf outputs.tf providers.tf
  • Before we actually begin updating the files in our root module, let’s create a new folder within our current directory for the child modules, and place them in a separate folder for our compute, security groups, and networking, by running the commands below:
mkdir Modules 
cd Modules
mkdir Compute
mkdir Security
mkdir Networking

Part 2

We now have our file structure set up accordingly for the parent and child modules, and we can now begin creating all remaining files. Let’s begin the process of shifting around the configuration, starting with our providers.tf file in the parent module. Start running the commands below to ensure we’re in the correct directory:

cd ..
ls
vi <rootmodulename>

*Remove the text for the providers, copy it, and then paste into the providers.tf file*

  • Head back to your new modules directory you created, navigate to the compute folder created in Part 1, and create the required files by following these commands:
cd Modules
ls
cd Compute
touch main.tf variables.tf output.tf
  • Open the “main.tf” file, paste the contents from above, and your resource for main.tf should look like this after adding the “sample instance” resource from the root main.tf file:
  • Create the same files in your networking and security folder as they will be needed for the VPC and Security Group we will create. As an example, your VPC resource should look similar to this:
  • Your root main.tf file (which includes the actual modules), should look similar to this:
  • When you have created all files, the tree should resemble this:

Part 3

In the previous lesson, we walked through the use of basic terraform commands that are required for your deployments. You would normally start with the terraform init command, but I want to ensure that things will go smoothly with all of the files created, and so I will start things off with the commands below:

terraform plan
  • You’ll notice that there are 12 things to add here, so let’s proceed with our next command to make sure all formatting is accurate within the entire directory, followed by our initiation:
terraform fmt -recursive
terraform init
  • Okay, it’s looking good so far, so let’s run the ‘plan’ command below, and then attempt to apply the configuration changes:
terraform plan
terraform apply --auto-approve
  • You’ll notice from above that we received a “Warning” for an undeclared value, along with an Error for the security group. To resolve the error, head into your AWS Console, navigate to VPC, and copy the newly created VPC ID. Once copied, add this ID to the main.tf file for the Security module, as the value for “VPC ID”. Save the changes and run the commands below:
terraform validate
terraform apply --auto-approve

*It’s important to note that errors can be easily resolved if you pay close attention to the message that Terraform provides. It essentially tells you exactly what needs to be done to resolve the issue. You should always keep the Terraform documentation handy for small issues such as what we encountered above.*

Part 4

So now that we have all of our resources created, let’s head into the AWS Console to take a few screenshots to serve as confirmation:

VPC:

EC2 Instance (You’ll notice that it is in a running state):

Security Group:

Custom HTML Webpage

Note: This turned out to be a server error, not an error with the TF configuration

After several refreshes and research, with no change to the script required, I realized that it would just not let me be great. Security groups are spot on, but I was unable to reach the Apache server unfortunately.

Part 5

Okay, so we’ve learned how to create parent and child modules in earlier steps, but what’s the use of having this good info if we can’t share it with the world? Let’s get ready to push our files to Github, starting with the flow below:

  • If you created your repo earlier and cloned it as required in Part 1, within your IDE, you should be able to stage your changes, and push to your repo. It should look like this for all files you intend to commit:
  • If your commit was successful, head over to your Github repo, and you should have a new message for the branch with the commits:
  • With your .gitignore file in place, your backend and state files should NOT have been pushed over to your repo, along with your tfvars file, as shown here:
  • Now, let’s not forget about all of the resources that we’ve created for this walkthrough. If you’ve been patiently waiting to see that command to kill all of your resources, proceed with it below:
terraform destroy --auto-approve 🙂

This was a fun one for sure! Be sure to stop by next week for more Terraform!

Image: Free Giphy

Follow me on LinkedIn, and @theCaptN21 on GitHub!

--

--

Katoria Henry
Geek Culture

Platform Engineering | DevOps | Chaos Engineering | Cloud Engineering | High Availability | Cloud Security | 👉🏽 https://www.linkedin.com/in/katoria-henry-2018