Static Analysis of Terraform code with Checkov

Towards high-quality infrastructure-as-code part 2

Photo by Ivan Bandura on Unsplash

In part 1 of this blog series, I make a case for testing your Terraform code with Go and Terratest.

In part 2 of this blog series, I’ll be making a case for static analysis tools. Static analysis tools for Terraform are a powerful mechanism to help your team follow industry best-practices. Conversely, your organization’s infrastructure team can leverage static analysis tools and custom checks to document and enforce company-wide policies.

These tools operate on the Terraform code or in the Terraform plan. Hence they are faster to run than an end-to-end test in Terratest. Instead of working on the Terraform infrastructure as a whole, static analysis tools focus on each resource individually.

Checkov

The software I recommend for static analysis of Terraform is Checkov. Checkov provides a comprehensive set of built-in policies for the most common pitfalls and an easy way to create custom rules.

From all the tools I’ve seen, Chekov seems to be the most mature one. Checkov has the largest number of built-in policies, has excellent support for custom policies, and integrates very well with most CICD services.

Writing custom rules is done in python. Depending on where you stand, this may be an advantage or a problem. If your team already has to worry about Terraform and Go, learning yet another language might not be the best choice. In those cases, you may use Tfsec or Semgrep, which implement custom tools as YAML/JSON configuration files. The latter is trending and may become the de facto tool for static analysis of any language, but Terraform support is still in the alpha stage.

Example

The following is an example deployment with some checks. Like the Terratest blog post, this example does not represent a real workload nor follows best practices. We’ll be using the Azure cloud for this example.

Say you’d like to deploy a storage account in your organization’s subscription.

According to your organization’s naming scheme, one must prefix every resource group’s name with the environment targeted (either “dev” or “prod”).

We’ll organize the code with the following structure:

example_storage_account
├── policies
│ ├── custom_policies.py
│ └── __init__.py
└── src
└── main.tf

Our initial iteration src/main.tf will be as such:

Now let’s run the Terraform code through Checkov. Install the tool with:

$ pip install checkov

Now, in the directory example_storage_account, run Checkov with:

$ checkov --directory src

Surprisingly, Checkov failed our deployment! Here’s the abridged output:

Check: CKV_AZURE_33: "Ensure Storage logging is enabled for Queue service for read, write and delete requests"
FAILED for resource: azurerm_storage_account.this
File: /main.tf:15-21
Guide: https://docs.bridgecrew.io/docs/enable-requests-on-storage-logging-for-queue-service
Check: CKV_AZURE_3: "Ensure that 'Secure transfer required' is set to 'Enabled'"
FAILED for resource: azurerm_storage_account.this
File: /main.tf:15-21
Guide: https://docs.bridgecrew.io/docs/ensure-secure-transfer-required-is-enabled
Check: CKV_AZURE_44: "Ensure Storage Account is using the latest version of TLS encryption"
FAILED for resource: azurerm_storage_account.this
File: /main.tf:15-21
Guide: https://docs.bridgecrew.io/docs/bc_azr_storage_2
Check: CKV_AZURE_35: "Ensure default network access rule for Storage Accounts is set to deny"
FAILED for resource: azurerm_storage_account.this
File: /main.tf:15-21
Guide: https://docs.bridgecrew.io/docs/set-default-network-access-rule-for-storage-accounts-to-deny

From the output, it looks like we are not following best-practices on our storage account. We are not enforcing HTTPS, and our default firewall rules are too permissive. Since we will not be using the queue service, we can ignore that check. Each check provides a link to a web page explaining the error and how to fix it.

Following the documentation, we refactor our storage account like this:

We changed our firewall rules to deny by default and only accept our public IP. Moreover, we now enforce HTTPS and a higher TLS version. Note the comment that skips the queue check. Checkov now allows our infrastructure.

Now let’s create a custom policy to enforce the naming scheme. Here’s our custom_policies.py:

Don’t forget to also include an empty __init__.py file to the policies folder. As we pass the environment as an input, Checkov has no way to check if the Terraform code respects the resource group naming conventions. For this case, we’ll need to perform our checks on the Terraform plan (tfplan). In the src folder, we create the tfplan with the input variable environment set to dev.

$ terraform init && terraform plan --var environment=dev --out terraform.tfplan.binary

The tfplan is in a binary format. For Checkov to understand the plan, we must first convert it to JSON:

$ terraform show --json terraform.tfplan.binary | jq ‘.’ > terraform.tfplan.json

Now we can run Checkov with the JSON tfplan and custom policy. On the example_storage_account folder, execute the following command:

$ checkov --file src/terraform.tfplan.json --external-checks-dir policies

Checkov will check the custom policy and evaluate it as PASSED. Here’s the abridged output:

Passed checks: 7, Failed checks: 0, Skipped checks: 0Check: MYORG_RG_001: "Ensure resource group is prefixed by the environment name"
PASSED for resource: azurerm_resource_group.this
File: /src/terraform.tfplan.json:18-23

I’ve hosted the complete example here.

Conclusion

As a DevOps team, static analysis is yet another tool to deploy high-quality infrastructure-as-code. You can use Checkov alongside your existing Terratest code to provide an extra layer of safety. With Checkov, you can ensure your code follows best practices and is compliant with your organization’s policies.

The same caveats apply as Terratest. Using Checkov means another programming language to learn on top of Terraform, and a tradeoff between doing Terraform code and testing code.

My recommendation is to start by using Checkov’s built-in policies on your local development. As you saw in the example, they provide valuable feedback and guide your development towards a more secure and reliable infrastructure.

Happy coding!

Founder and Software Engineer at Unicoeding. I help organizations with their cloud and data infrastructure. You can find us at www.unicoeding.com.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store