Skip to main content

Open source tools for Terraform code testing

Artikel von:
Stephane Jourdan
Stephane Jourdan
wordpress-sync/feature-synk-iac-terraform-purple

14. Mai 2020

0 Min. Lesezeit

Editor's note: This post originally appeared on CloudSkiff.com. CloudSkiff joined Snyk in October 2021.

Terraform code testing is a recurring subject that we keep hearing about. People are wondering a lot about infrastructure code testing, even if we don't see much adoption of testing in practice. First of all, should you test your code, and what are the different strategies to automate tests, and what are the best open source tools to do so?

Terraform code testing is key and yes, you should be testing it. There are a lot of ways you can do it. It is very similar to standard software engineering processes. It comes from the same culture, so there is no surprise here.

Terraform code testing: Start by using linters

Linters are a tool you can execute directly on your laptop or on your CI/CD system because you want to ensure others respects some coding conventions or standardization.

One linter in Terraform that I really like is named TFLint. It is open source and available on Github. It’s a linter “the infrastructure way”, which means that it lints your code in a way terraform validate wouldn’t. terraform validate is a subcommand in Terraform. Basically, it just checks for the validity of the structure, and not the validity of the contents. It means that if you request a wrong type of AWS machine, terraform validate will not detect it as bogus, while TFLint will.

Here’s how a faulty commit would look like:

wordpress-sync/blog-terraform-code-testing-faulty

And here’s how it would be caught and appear in your logs:

wordpress-sync/blog-terraform-code-testing-error
wordpress-sync/blog-terraform-code-testing-cli

This linter also has a “deep linting” ability that goes beyond simple linting.

Imagine that you need to request an existing correct VM, let’s say a T3 large instance. If your account is not allowed any more capacity (for example you already launched all the VMs you were allowed to request), the linter will send a request to the AWS API for the correctness of what you request plus the ability of your request to be executed within your account limits. It does all the things that a proper linter in development will do, quality, tabs, etc., and, it goes beyond the validator and the formatter that you can find directly on Terraform.

One common use case would be that you made all your tests in a staging environment and the AWS limits of this environment are very high because you made dozens of requests over time, each time you needed to test something. So maybe you have hundreds or maybe thousands of allowed instances on each region.

But then you decide to promote a new change in production, and maybe your production environment doesn’t have the same rules or the same AWS account limits. Then you will promote something that won’t work in your production environment.

The deep linter here will ensure that what you are about to promote or to launch from staging or from development, or any environment to production, really can run as per the rights of your account.

Perform unit testing on your Terraform code with TDD‹

A proper Agile engineering workflow would begin with test-driven development (TDD).

When you start doing some TDD, you write unit tests, like: “well I want to create my VPC and my VPC is supposed to have this address space.” The next step is to write your function (in Terraform it is called a resource) and then to execute that. As we’ll see later, I would use any RSpec derivative tools for that: Serverspec, InSpec…  Your TDD steps would look something like:

Start by writing your test: it fails

Write the resource and it doesn’t fail because it will launch in an isolated environment (especially that VPC).

Through this process, you are going to test what you wanted and then destroy it. You can do exactly what you would do in engineering, like in your CI: execute all the tests on pull requests. By the way, choosing how much security you want once you get started on testing is a sensitive subject. I personally execute all the tests prior to merging anything, but I’m much lighter during the pull request phase because launching all the tests, all the time, at each commit can be very long.

Do integration tests

Another thing you can do, and which is quite common in QA processes, is to randomize things such as variables. Terratest is another really cool tool for integration tests. It’s probably the most complete, but also the most complex.

Let’s dig a bit into it.

Terratest is a Golang library. It requires skills to master (well it’s Go…) but allows you to test anything that has an API like AWS, Azure, Google Cloud, Kubernetes, Docker images, Packer builds… (Packer is the tool from Hashicorp to build AMIs: virtual machine disk image) and even Helm charts as well.

Basically, you just need to create the name of your resource_test.go, you import your libraries and you just execute this, but it requires a certain level of skills in Golang and a deep understanding of what you want to test.

Randomize stuff

During this phase of integration, I would randomize items like regions names. You usually always test your code in the same region. By randomizing it, you will have different results, and you will ensure that it works.

Maybe your names are unique, and the names of your resources are as well. For example, your Amazon S3 bucket needs to be unique. It is the same process as an engineering project:

Use the tools

  • Lint your code

  • Perform unit test on your files

  • Randomize that

  • Put the results in your CI

  • Add that to your pull requests

  • Tag your code and your releases, (like master)

  • Use feature branches and then merge to master and then release a code version

Other tools we recommend to test your infrastructure code

I mentioned all the RSpec derivative tools for that: Serverspec, Inspec… InSpec is made by Chef, while Serverspec is more community-based, I think.

Serverspec is made for unit testing but it’s not only dedicated to Terraform. It’s more general. You can test anything from infrastructure to Docker images, to processes running on a server or that kind of thing, but it’s readable — it’s close to English. It’s really generic because it does not test directly the code, or it does not directly match directly the Terraform code in your infrastructure repository, but it does test for the reality of what was launched. So for example, if you add a new feature, maybe a database on Google Cloud and you want to check that the port “5.4.3.2” is open, then you can just write this test, execute it and then you will be sure until the end of the times that something is answering on this port. Serverspec can help you do this quite quickly and easily.

There is another tool that I really like called GOSS. It is open source and really cool. It is very simple, based on YAML. It is not meant at all to test Terraform code but rather designed to test results. For example, you created a security group and you want to ensure that port 22 for SSH is closed. You can test that, plus processes, packages, DNS resolver, users… or check if your http endpoint on kubernetes is answering, that kind of things…

I think it can even output results of the tests as a Nagios-compatible output so you are even able to launch it as a /health point in your microservice to ensure that things are running properly. It is really simple and lightweight. I really like this tool. It works very well for Docker files etc …

Testing your Terraform code

GOSS for very high level, easy stuff with simple YAML, simple binary, very easy, straightforward. Serverspec and InSpec for all the unit testing, and it’s alll open source.

And Terratest obviously for integration. It is very complete. Integrate all that in your CI/CD system, or at least anywhere before shipping/releasing it and into the wild or you are going to have issues if you don’t ?

For testing your Terraform for security vulnerabilities, Snyk IaC can scan your Terraform configs (and Kubernetes, CloudFormation, and ARM templates!) as you code, with guided fixes so you can merge and move on. You can test as you write, monitor for changes in your git repositories, and automate testing in your build pipelines before deployment. Getting started with a Free plan takes minutes, while a breach from an IaC misconfiguration can cause damage to last a lifetime. Sign up and start securing your configs below.