Testing your code, aligning to agreed standards and verifying compliance towards security guidelines is crucial for building high-quality infrastructures. You should apply these principles to infrastructure code to develop reliable and highly predictable code. In this article, we will go through tools for continuous integration and local development that we, at sysdogs, use daily and we highly recommend incorporating into your workflow.
Continuous integration is an essential part of code development. Terraform is not a special citizen - it should be validated and tested, like it was a regular programming language, although it is not. Below, you will find a list of useful tools we, at sysdogs, incorporate into all of our Terraform CI pipelines.
terraform fmt and
You should not undermine the importance of consistent formatting for code readability and efficient development among the team. You could leverage IDE integrations to perform auto-formatting for you, but additional verification in one of the pull request checks is also worth it. Do not forget to add -recursive option to make sure you are also checking files in child directories.
Another thing worth mentioning is terraform validate command. It will validate all Terraform files inside the directory, yet accessing no remote services, such as remote state or provider APIs.
terraform fmt -check -recursive
If you use GitHub, HashiCorp GitHub Action is worth looking at.
Intention of tfsec is to perform a static analysis of Terraform code to spot potential security issues. It contains best security practices for major cloud providers such as AWS, GCP and Azure. To give you an example, it can mark insecure security group rules such as wide open ingress, not encrypted S3 buckets, and many, many more.
We also highly recommend reviewdog tfsec GitHub Action that creates a comment with an error directly inside pull request.
At sysdogs, we are over-paranoid in terms of security. That is why we use another static code analysis tool for security and compliance misconfiguration called checkov. It is very similar to tfsec, however we also use it to validate Kubernetes manifests that we create.
checkov -d path_to_your_tf_codebase
Compliance is bliss. Every organization needs to have a set of compliance policies, and continuously test the code against them. We leverage terraform-compliance to perform negative testing and write our compliance policies using behavior-driven development. Terraform-compliance performs policy validation on the plan instead of static code analysis, and therefore, it can pinpoint violations with higher accuracy.
For everything else, that cannot be tracked down with terraform-compliance or if you just want to validate anything besides Terraform, you can use config-lint. It is a linter, supporting multiple formats, that checks the files against a set of rules written in YAML.
TFLint is a Terraform linter that focuses on finding errors, validating best practices and much, much more. It has over 700 rules built-in, which include both regular Terraform rules, and AWS specific ones with other major cloud providers in the experimental state. Its primary purpose is to find misconfiguration that will never flag as an error before running apply, for example wrong EC2 instance type. TFLint is created in Go and has a plugin system that makes it extendable by any person with Go knowledge.
Deep-inspection is something special for TFlint. As most tools apply static code analysis, deep inspection verifies the resource on provisioning level.
tflint --deep --module path_to_your_tf_codebase
If you are a happy GitHub user, we suggest looking at reviewdog TFLint GitHub Action that creates a comment with an error directly inside pull request.
Testing is a wide topic that deserves a separate article. At sysdogs, as I have already mentioned before, we believe that you must test ANY code and Terraform is no exception.. For our tests, we use a regular gotest testing suite with one special library. Terratest is a Go library that makes it easier to write automated tests. It contains helpers, functions and patterns for common infrastructure testing tasks. With it, you can ensure that the Terraform module you change is still behaving as expected, basing your certainty on unit tests that will create an actual infrastructure, run tests and destroy it afterwards.
Versioning your code is crucial. There are multiple ways to achieve this, but the easiest we have found so far is to utilize something that will automatically tag the code based on pull request labels. Our pick is Release on Push GitHub Action – it creates a new GitHub release after merging to the main branch. You just add a label to your pull request that marks the change as patch, minor or major, and the action creates a new GitHub release based on semantic versioning to reflect this. If there is something you wish to introduce into the main branch without release creation, “norelease” label is the way to go.
To limit the time you spend on waiting for continuous integration checks to fail, we also provide our suggestion of several tools that will improve your quality of life while working with Terraform.
Multiple Terraform versions
Sometimes you need multiple Terraform versions on your local machine. For example, you are migrating from 0.12 to 0.13 and you need both of them at the same time. Python has its pyenv, Ruby has its rbenv, Node has its nvm and Terraform has its tfenv! It is a simple Terraform version manager that allows you to switch between multiple versions without hassle.
It does not really matter which text editor you use daily to work with Terraform. We highly recommend an editor that understands HCL and can highlight errors before you even push your code to the repository. One of our favorites would be IntelliJ IDEA, which has excellent support for HCL, or Visual Studio Code with Terraform plugin recently taken over by HashiCorp. It is also a good idea to enable auto-formatting on save to ensure that the code you create is always in flawless form.
An alternative to using fully fledged IDE is usage of git hooks with pre-commit framework. Anton Babenko has created an excellent pre-commit hook for Terraform that will format your code, validate it using validate command, TFLint, tfsec, checkov, and generate docs using terraform-docs.
Properly leveraging tools and configuring your local environment with additional utilities will boost your efficiency, productivity and quality of the code that you produce. There are many, many more tools available, and we invite you to explore the possibilities and find the solution that suits you on your own. Let us know if you find something helpful or worth adding to this list, as we love to expand our arsenal.