Secure Terraform Code With Checkov And TFLint
With this simple setup, you can incorporate industry best practices into your Terraform configurations and keep your repository organised

As a DevOps engineer building cloud infrastructure, it is top of mind for me to ensure that the infrastructure configurations do not expose any security vulnerabilities and violate compliance standards. Depending on the type of infrastructure being built, relevant industry standards and best practices need to be considered. For instance, when configuring VPC subnets, I refer to networking best practices on setting up security groups to control traffic flow to and from those subnets.
We engineers try our best to keep up-to-date with security standards and state-of-the-art tools but it is a challenging task. To add to that, when working in a team, if you don’t have a good system in place, maintaining a common set of standards and compliance could be challenging.
So, how to maintain compliance and code quality of infrastructure configurations and address vulnerabilities early in the development cycle?
Introducing security scanning tools early in the development phase can identify any misconfigurations, security vulnerabilities and compliance violations before they land in your environment. Most scanning tools have made integration so easy that you can add them as pre-commit hooks. This ensures any risks associated with infra configurations are detected and mitigated promptly in the Software Development Lifecycle (SDLC) - a shift-left approach. This also means you really don’t have any excuse to not integrate a suitable scanning tool on day 1.
For this example, I am going to show you how to integrate Checkov and TFLint tools as pre-commit hooks as well as in a CI/CD pipeline. Together, these tools help in maintaining security posture and keeping your infra code organised.
💡Tip: Spacelift has written a great article explaining the importance of IaC scanning and various tools you can use to secure your infra code. I recommend reading this article for a more broader understanding.
Implementation
For this example, I am going to integrate these tools into an existing Terraform repository which I created in a previous post. For more information on what this Terraform code builds, check out my previous post:
Also, if you are new to Terraform, I also wrote an article introducing Terraform and its main concepts. I encourage you to have a read to cover the basics of Terraform:
Now that we got the basics covered, let’s dive into the setup.
Setup TFLint As A Pre-Commit Hook
TFLint is an open-source linter and static analysis tool specifically designed for Terraform code that can help you maintain consistent code styling and identify any misconfigurations or errors (e.g. undefined variables). I prefer using this tool for its linting capabilities more so than the static analysis. More importantly, I also get the choice to integrate this tool at a repository level using pre-commit hooks or integrate at a CI/CD pipeline level. Broadly speaking, linting at a repository level lets you customize linting based on each project’s nature. If you think you need these patterns enforced across a group of projects, then integrating linting at CI/CD would make more sense. My personal preference would be at a repository level because that way, it is flexible enough for me to customize according to the requirements of the repository formatting and styling consistency before I commit the code.
💡Tip: If you want to learn more about git hooks and how to install pre-commit plugin, checkout this page: https://pre-commit.com/
Create a new file at the root level of the repository with the name .precommit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.2
hooks:
- id: terraform_tflint
args:
- '--args=--only=terraform_deprecated_interpolation'
- '--args=--only=terraform_deprecated_index'
- '--args=--only=terraform_unused_declarations'
- '--args=--only=terraform_comment_syntax'
- '--args=--only=terraform_documented_outputs'
- '--args=--only=terraform_documented_variables'
- '--args=--only=terraform_typed_variables'
- '--args=--only=terraform_module_pinned_source'
- '--args=--only=terraform_naming_convention'
- '--args=--only=terraform_required_version'
- '--args=--only=terraform_required_providers'
- '--args=--only=terraform_standard_module_structure'
- '--args=--only=terraform_workspace_remote'
- '--args=--only=terraform_unused_required_providers'
Once the configuration file is created, the next step is to ensure TFLint cli is installed on the local machine. I am on a Mac so for me this is as simple as running brew install tflint
but depending on your OS, you will have to follow the relevant installation instructions from here: https://github.com/terraform-linters/tflint (Note: I am also downloading the latest version of the tool that was available at the time).
✅ Checkpoint: Run
tflint -v
to ensure TFLint is running and the version of it can be obtained❯ tflint -v TFLint version 0.55.1 + ruleset.terraform (0.10.0-bundled)
Now that I have installed tflint on my local, let’s check whether the pre-commit hook actually works!
To check the functionality of the TFLint tool, I have intentionally created an unused variable named “unused_variable” in my variables.tf file.
# ./variables.tf
...
variable "instance_type" {
type = string
description = "Instance type"
default = "t2.large"
}
variable "unused_variable" {
type = string
description = "This is an unused variables"
}
variable "enable_monitoring" {
type = bool
description = "Enable detailed monitoring"
default = false
}
...
To trigger pre-commit hook without actually committing the code, run pre-commit run -a
command and see the outcome!
And as expected, the TFLint scan failed with an error indicating that there is an unused variable present in the configuration. Perfect!!
Now the unused variable was highlighted and subsequently the pre-commit failed because I explicitly added the flag terraform_unused_declarations into the rule set argument in the pre-commit configuration. If I didn’t include it, TFLint would have passed instead with a warning and not exiting with error code 1.
💡Hint: For more information on TFLint rule set, check this page: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/main/docs/rules/README.md
Setup Checkov As A Pre-Commit Hook
Checkov is a static code analysis tool that can identify misconfigurations and compliance violations. It includes over 750 predefined policies to detect common misconfigurations. Furthermore, Checkov scans for industry common standards like Center for Internet Security (CIS) and Amazon Web Services (AWS) Foundations so it is super helpful for anyone building cloud infrastructure to have peace of mind that their IaC setup aligns with industry standards.
Checkov too has a pre-commit hook integration so let’s start by updating the previously created pre-commit configuration file.
Let’s start with the most basic setup and now the pre-commit configuration file should be like below:
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.97.3
hooks:
- id: terraform_tflint
args:
- '--args=--only=terraform_deprecated_interpolation'
- '--args=--only=terraform_deprecated_index'
# - '--args=--only=terraform_unused_declarations'
- '--args=--only=terraform_comment_syntax'
- '--args=--only=terraform_documented_outputs'
- '--args=--only=terraform_documented_variables'
- '--args=--only=terraform_typed_variables'
- '--args=--only=terraform_module_pinned_source'
- '--args=--only=terraform_naming_convention'
- '--args=--only=terraform_required_version'
- '--args=--only=terraform_required_providers'
- '--args=--only=terraform_standard_module_structure'
- '--args=--only=terraform_workspace_remote'
- '--args=--only=terraform_unused_required_providers'
- id: terraform_checkov
First and foremost, install Checkov on the local machine by running brew install checkov
.
✅ Checkpoint: Checkov installed and can check version
❯ checkov -v 3.2.370
Now that Checkov is installed, let’s run the scan on the repository with command pre-commit run -a
again.
Below is a snippet from the logs showing the outcome of the scan:
As shown in the two screenshots above, Checkov identified two issues in the Terraform configuration.
(1) The first issue is flagged because git tags can be repointed. This could lead to instability in Terraform code and or introduce risks if the tags were to be repointed as part of a malicious attack. A common example is when you use 3rd party modules.
(2) The second issue is related to assigning public IPs to subnets which could unintentionally expose resources deployed to those subnets to the public internet.
Run IaC Scanning on CI/CD Pipeline
While it is super easy to set up IaC scanners as pre-commit hooks at a repository level, scaling that setup across a team of developers working on a multi-repository project could get tricky. Generally, while linters are configured at a repository level, policy scanning and compliance checks need to be integrated across the board at a consistent manner. Reason being that these policies and standards are common to the domain and don’t generally require any further customization. These tools also provide the ability to add your own custom policies as well. Therefore, it makes sense to have security scanning tools integrated into the CI/CD pipelines and trigger scanning jobs upon pushing code to remote branch.
Below is a sample setup where I have configured TFLint at the repository level and integrated Checkov scanning job in the GitHub actions (ie Github pipeline) to be triggered upon a code push.
To trigger a scan job when new code is pushed to the branch, add below config at ./github/workflows/github-actions.yml in the repository.
# ./github/workflows/github-actions.yml
name: GitHub Actions Demo Pipeline
run-name: ${{ github.actor }} is testing out GitHub Actions
on: [push]
jobs:
scan:
permissions:
contents: read
runs-on: self-hosted
steps:
- uses: actions/checkout@v3
- name: Checkov Github Action
uses: bridgecrewio/checkov-action@v12
with:
output_format: cli, sarif
output_file_path: console, results.sarif
The above example is a simplified version from the GitHub reference: https://github.com/marketplace/actions/checkov-github-action.
With this setup, we should be expecting the pipeline to fail with similar errors we observed when running Checkov as a pre-commit hook. Shown below is the outcome:
That is literally it! We have successfully set up TFLint and Checkov that will scan your infra code and highlight issues as early as possible in your infra code development cycle.
Thanks for reading…Until next time!