Terraform Testing Using Terratest
By Kyle Robertson, Software Engineer, Quicken Loans
Infrastructure as Code (IaC) has become a common pattern for provisioning and maintaining infrastructure. This is a very good thing, as codifying infrastructure provides many benefits such as ease of creation, automation and state management. One of the biggest players in this realm is Terraform, an IaC tool, which allows you to provision infrastructure from multiple cloud providers.
While the benefits of IaC abound, you’ll begin to run into problems as you expand and change your code. Things like typos, faulty logic and more may slip into your code and cause your infrastructure to not provision correctly. These problems can take time to fix, and they may cause your infrastructure to become impaired and potentially impede progress on creating new infrastructure. We need a way to catch these changes before they become issues. Luckily for us, there is a tool that can help us do exactly that: Terratest.
Terratest is library that allows for testing of infrastructure from multiple providers. It contains functions that you can use to quickly create tests to validate all parts of your infrastructure.
All tests using Terratest are written in Go. This makes Terratest widely accessible for anyone who may work with infrastructure in their day-to-day work.
To show how simple it is, here’s an example test straight from the Terratest repo:
Terratest provides a suite of built-in libraries, which make various test scenarios easy to set up, along with tools that make it easy to interact with the infrastructure you’re testing. In addition, since you’re working with a standard programming language when using Terratest, you have access to all the standard and community-created libraries available for the language. Using these in combination with Terratest is a powerful combination that allows you to create complex test scenarios and get creative with your testing.
Setting Up Terraform
To demonstrate how to create tests, I’ll take you through an example. Let’s imagine that you have your own start-up. You’ve spent months creating your great new application, but now you need to run it somewhere! You’ve settled on using Amazon Web Services (AWS) as your cloud provider. After analyzing your application, you’ve determined you’ll need the following to host it:
· EC2 instances
· An Elastic Load Balancer (ELB) to load balance between multiple EC2 instances
· A Route 53 zone and alias record to point to your load balancer, allowing you to reach the app with a friendly URL
Being the savvy engineer that you are, you decide to use Terraform to codify your infrastructure configuration. You tell your fellow engineer friends about your plan, and they recommend using Terratest to test your infrastructure code. You believe that your set-up is so simple, it doesn’t need any tests. Really, how hard could it be?
For the sake of simplicity, we’ll assume some of the prerequisite setup — such as creating a virtual private cloud (VPC) and a Route 53 zone — have already been done.
The full example can be found here, but let’s take a look at a few specific points of interest:
The code below is creating our EC2 instances. We’re loading the Amazon machine image (AMI) to use for the EC2 and associating a security group with the EC2 instances that only allow traffic from within the VPC.
Next, we’re creating our load balancer here. In addition, we create a listener to send traffic on port 80 to a target group that will be associated with our EC2 instances.
With the Terraform created, you’re now ready to stand up your infrastructure. You run your terraform plan and terraform apply with glee, anxious to see your application running in production after months of work. Once the apply is complete, you hastily put the URL in your browser and …
You try again and again, hoping it might be some caching issue or a local connection issue, but nothing works. You’re completely perplexed.
As an engineer working for a start-up, you most likely have had many sleepless and caffeine-filled nights. Unfortunately, as a result, it’s easy to miss things due to exhaustion. If you look carefully at the example, you’ll see something important is missing.
The EC2 instances aren’t being attached to the target group.
You rapidly fix the mistake, but in frustration, wonder if you could have caught this before you tried to make your infrastructure.
Writing Your First Infrastructure Test with Terratest
You do some research online and decide you should write a test to validate that your Terraform works properly when you make changes. You decide to start simple and write a test that applies your Terraform based on a sample input.
You can view the simple test here. Let’s go over how this works:
You’ll see you’re importing one package to consume Terratest: github.com/gruntwork-io/terratest/modules/terraform. This package has all the libraries to create tests against Terraform.
Next, you’re configuring the options for your Terraform run. This defines that you’re running Terraform with an input of a tfvars file.
Once you do this, you’re also defining the Terraform ‘destroy’ action. By giving the ‘defer’ keyword, you’re instructing this to destroy the resources created by the test after the ‘apply’ and any additional testing are done — regardless of whether the test passes or fails.
Lastly, you’re actually running your ‘apply.’ If either the initialization or ‘apply’ fails, the test is considered a failure. Otherwise, the test is a success.
You know this is a good place to start, but this isn’t enough. Compared to writing a unit test for an application, this unit test is running the function it’s testing without performing any assertions of the results. It isn’t truly testing anything besides that the function runs. You know that you need to validate the infrastructure created by your test.
A More Complex Test: Validating Your Test Sample Site
Excited by the possibilities of Terratest, you decide to improve your test to validate the infrastructure created. But how can you do this? There are a few options:
- Check the output of the ‘terraform apply’ to assert if it matches the expected output.
- Use the AWS software development kit and retrieve the generated resources to validate they’re what you expect.
- In the case of a more complex system, actually reach out to the infrastructure (via HTTP request, DB connection, etc.) to validate it returns the expected response within the expected parameters.
You decide to use a combination of methods one and three to improve your test. Once the ‘apply’ is complete, you’ll check the output and ensure it matches what you expect. If those assertions pass, then you’ll perform an HTTP request against your created infrastructure and ensure it returns a ‘200’ response.
Let’s take a look at this more complex example:
Once we run the ‘apply,’ we’re loading the outputs, then comparing them against expected values to ensure they’re correct.
While checking the output is a valuable test, the next test is even more valuable. We perform an HTTP GET request to our site and ensure we get an HTTP status code ‘200’ back. By this test passing, we know that all of our infrastructure is working, and it’s all connected correctly. We can be confident in the state of our Terraform.
Through this example, we have seen the power and possibility of Terratest. With the tools it provides, we can create complex tests that validate our infrastructure code works as expected in many different scenarios.
I encourage you to take time to look at the other features that Terratest provides and start writing tests for your own infrastructure. Not only will you find that it’s fun and rewarding, you’ll also ensure the quality of your IaC.
We’re still hiring! Even in the face of uncertainty, we’re reshaping the fintech industry. Interested in joining us? Check out our technology openings.
These opinions are those of the author. Unless noted otherwise in this post, Quicken Loans is not affiliated with, nor is it endorsed by any of the companies mentioned. All trademarks and other intellectual property used or displayed are the ownership of their respective owners. This article is © 2020 Quicken Loans.