{"id":79430,"date":"2026-10-20T04:58:39","date_gmt":"2026-10-20T04:58:39","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=79430"},"modified":"2026-10-20T04:59:00","modified_gmt":"2026-10-20T04:59:00","slug":"how-to-configure-pre-commit-for-terraform-complete-devops-guide-with-15-practical-use-cases-commands-security-checks-and-best-practices","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/how-to-configure-pre-commit-for-terraform-complete-devops-guide-with-15-practical-use-cases-commands-security-checks-and-best-practices\/","title":{"rendered":"How to Configure pre-commit for Terraform: Complete DevOps Guide with 15 Practical Use Cases, Commands, Security Checks, and Best Practices"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Terraform pre-commit Hooks: Installation, Configuration, TFLint, Trivy, Checkov &amp; CI\/CD Guide<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Learn to configure pre-commit for Terraform with step-by-step commands, terraform fmt, validate, TFLint, Trivy, Checkov, terraform-docs, provider locks, multi-environment projects, and GitHub Actions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform pre-commit, pre-commit Terraform configuration, terraform_fmt, terraform_validate, terraform_tflint, terraform_trivy, terraform_docs, Terraform security scanning, Terraform Git hooks, Terraform CI\/CD best practices<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform is widely used to provision and manage cloud infrastructure through Infrastructure as Code (IaC).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As Terraform repositories grow, maintaining consistent formatting, validating configurations, enforcing security standards, and keeping documentation updated become increasingly important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider a development team managing Terraform resources for AWS, Azure, Google Cloud, Kubernetes, or MongoDB Atlas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Without automated checks, developers may accidentally commit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Incorrectly formatted Terraform configurations.<\/li>\n\n\n\n<li>Invalid provider or module references.<\/li>\n\n\n\n<li>Deprecated or unsupported arguments.<\/li>\n\n\n\n<li>Unrestricted network security rules.<\/li>\n\n\n\n<li>Unencrypted cloud resources.<\/li>\n\n\n\n<li>Hardcoded credentials.<\/li>\n\n\n\n<li>Terraform modules with outdated documentation.<\/li>\n\n\n\n<li>Inconsistent provider lock files.<\/li>\n\n\n\n<li>Changes that fail validation in CI\/CD.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Many of these problems can be detected before changes reach the main branch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>pre-commit framework<\/strong>, combined with the community-maintained <strong>pre-commit-terraform<\/strong> hook collection, provides a practical solution.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of manually executing multiple Terraform commands before every commit, developers can automate these checks through Git hooks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this tutorial, we will configure a complete Terraform quality-control workflow, starting with formatting and expanding into validation, linting, security scanning, documentation, and CI\/CD enforcement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. How pre-commit Works with Terraform<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">pre-commit is a framework that executes configured checks when Git operations occur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform-specific hooks can call tools including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Terraform CLI<\/li>\n\n\n\n<li>TFLint<\/li>\n\n\n\n<li>Trivy<\/li>\n\n\n\n<li>Checkov<\/li>\n\n\n\n<li>terraform-docs<\/li>\n\n\n\n<li>Infracost<\/li>\n\n\n\n<li>Terragrunt<\/li>\n\n\n\n<li>OpenTofu<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The checks are configured in a file named:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.pre-commit-config<\/span><span class=\"hljs-selector-class\">.yaml<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Terraform Development Workflow<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Developer modifies Terraform files\n                |\n                v\n         git add *.tf\n                |\n                v\n           git commit\n                |\n                v\n         pre-commit hooks\n                |\n        +-------+-------+\n        |               |\n        v               v\n   terraform fmt       TFLint\n        |               |\n        +-------+-------+\n                |\n                v\n         Generate Docs\n                |\n                v\n        Commit permitted?\n           \/        \\\n         Yes         No\n          |           |\n          v           v\n      Git commit    Fix issues\n          |           |\n          |      Stage and retry\n          v\n        git push\n          |\n          v\n      pre-push hooks\n          |\n     +----+-------+\n     |            |\n     v            v\n  Validate      Trivy\n     |            |\n     +-----+------+\n           |\n           v\n       CI\/CD checks\n           |\n           v\n      Pull request\n           |\n           v\n       Code review\n           |\n           v\n      Terraform plan\n           |\n           v\n     Approved apply<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The goal is not to run every possible check on every commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A more efficient workflow divides checks according to their execution cost.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Stage<\/th><th>Checks<\/th><th>Typical cost<\/th><\/tr><tr><td>Pre-commit<\/td><td>Terraform formatting, whitespace, basic linting<\/td><td>Low<\/td><\/tr><tr><td>Pre-commit<\/td><td>Documentation generation<\/td><td>Low\u2013Medium<\/td><\/tr><tr><td>Pre-push<\/td><td>Terraform validation<\/td><td>Medium\u2013High<\/td><\/tr><tr><td>Pre-push<\/td><td>Security scanning<\/td><td>Medium\u2013High<\/td><\/tr><tr><td>CI\/CD<\/td><td>Complete validation and security checks<\/td><td>Variable<\/td><\/tr><tr><td>Deployment pipeline<\/td><td>Terraform plan and policy evaluation<\/td><td>Potentially expensive<\/td><\/tr><tr><td>Approved deployment<\/td><td>Terraform apply<\/td><td>Infrastructure-changing<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommended approach:<\/strong> Keep local commits fast and enforce all required checks again in CI\/CD.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. Prerequisites<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Before configuring pre-commit for Terraform, install the required tools.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Tool<\/th><th>Purpose<\/th><th>Required?<\/th><\/tr><tr><td>Git<\/td><td>Source control and Git hooks<\/td><td>Yes<\/td><\/tr><tr><td>Python 3.11+<\/td><td>Runtime for pre-commit 4.7.0<\/td><td>Yes<\/td><\/tr><tr><td>pre-commit<\/td><td>Git hook framework<\/td><td>Yes<\/td><\/tr><tr><td>Terraform CLI<\/td><td>Terraform formatting and validation<\/td><td>Yes<\/td><\/tr><tr><td>Bash<\/td><td>Required by pre-commit-terraform shell hooks<\/td><td>Yes<\/td><\/tr><tr><td>TFLint<\/td><td>Advanced Terraform linting<\/td><td>Optional<\/td><\/tr><tr><td>terraform-docs<\/td><td>Generate module documentation<\/td><td>Optional<\/td><\/tr><tr><td>Trivy<\/td><td>Detect Terraform security misconfigurations<\/td><td>Optional<\/td><\/tr><tr><td>Checkov<\/td><td>Additional IaC policy scanning<\/td><td>Optional<\/td><\/tr><tr><td>jq<\/td><td>Certain hook recovery and cost workflows<\/td><td>Optional<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Check installed versions:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git --version\npython3 --version\npre-commit --version\nterraform version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For additional tools:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">tflint --version\nterraform-docs --version\ntrivy --version\ncheckov --version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install only the additional tools used by your configured hooks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. Install Terraform pre-commit Dependencies on macOS<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The following commands work on supported Intel and Apple Silicon Macs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Check Homebrew<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew --version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install Homebrew from its official website if necessary:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/brew.sh\n<\/div><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Install pre-commit<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install pre-commit<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 3: Install Terraform<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add HashiCorp&#8217;s official tap:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew tap hashicorp\/tap<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install Terraform:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install hashicorp\/tap\/terraform<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 4: Install TFLint<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install terraform-linters\/tap\/tflint<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 5: Install terraform-docs<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install terraform-docs<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 6: Install Trivy<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install trivy<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 7: Install Checkov (Optional)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use pipx:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">brew install pipx\npipx ensurepath\npipx install checkov<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Open a new terminal if PATH changes require it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 8: Verify Installation<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit --version\nterraform version\ntflint --version\nterraform-docs --version\ntrivy --version\ncheckov --version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">You now have the principal tools used in this tutorial.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. Install Terraform pre-commit Dependencies on Linux<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">These instructions apply to Ubuntu and Debian. RHEL and other Linux systems can use the corresponding package managers and official binary distributions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Install Git, Python, and pipx<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo apt update\n\nsudo apt install -y \\\n  git \\\n  python3 \\\n  python3-pip \\\n  python3-venv \\\n  pipx \\\n  curl \\\n  unzip \\\n  jq<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Ensure the selected Python version is 3.11 or newer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Install pre-commit<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pipx ensurepath<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Open a new shell if required.<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pipx install pre-commit<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 3: Install Terraform<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Install Terraform using HashiCorp&#8217;s official Linux installation instructions:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/developer.hashicorp.com\/terraform\/install\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For supported Ubuntu\/Debian systems, HashiCorp provides an APT repository.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After configuring that repository:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo apt update\nsudo apt install terraform<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 4: Install TFLint<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Download the appropriate Linux package from:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/github.com\/terraform-linters\/tflint\/releases\/latest\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Choose the binary matching your CPU architecture, verify the checksum or published artifact attestation, and install the binary in a directory on PATH.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, after extracting the downloaded Linux binary:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo install -m 0755 tflint \/usr\/local\/bin\/tflint<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 5: Install terraform-docs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Download the appropriate package from:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/github.com\/terraform-docs\/terraform-docs\/releases\/latest\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After extraction:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo install -m 0755 terraform-docs \/usr\/local\/bin\/terraform-docs<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 6: Install Trivy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use the official installation instructions:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/trivy.dev\/latest\/getting-started\/installation\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After configuring Trivy&#8217;s official APT repository:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">sudo apt update\nsudo apt install trivy<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 7: Install Checkov (Optional)<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pipx install checkov<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 8: Verify<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit --version\nterraform version\ntflint --version\nterraform-docs --version\ntrivy --version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For RHEL, CentOS Stream, Rocky Linux, and Fedora, use compatible RPM packages or official binary releases rather than assuming Debian packages will work.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. Configure Terraform pre-commit on Windows<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Windows deserves special attention.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Although pre-commit itself supports native Windows, the <code>pre-commit-terraform<\/code> hook collection executes Bash scripts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The maintainers strongly recommend <strong>WSL2 with Ubuntu or Docker<\/strong> for Windows users.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended: WSL2<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Open PowerShell as Administrator.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Install WSL:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">wsl --install -d Ubuntu<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Restart Windows if prompted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open the Ubuntu terminal and install the Linux dependencies using the instructions in Section 5.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For better compatibility and performance, keep active Terraform repositories inside the WSL Linux filesystem, such as:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">~<span class=\"hljs-regexp\">\/projects\/<\/span>terraform-infrastructure<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Rather than placing the working repository under <code>\/mnt\/c<\/code>, unless your workflow requires Windows filesystem access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Alternative: Docker<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The community-maintained hook collection provides a Docker image containing Terraform-related tools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For an initial evaluation:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">docker pull ghcr.io\/antonbabenko\/pre-commit-terraform:latest<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Run the checks from a Git repository on Linux or WSL:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">docker run --rm \\\n  -e <span class=\"hljs-string\">\"USERID=$(id -u):$(id -g)\"<\/span> \\\n  -v <span class=\"hljs-string\">\"$(pwd):\/lint\"<\/span> \\\n  -w \/lint \\\n  ghcr.io\/antonbabenko\/pre-commit-terraform:latest \\\n  run --all-files<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The Docker image&#8217;s <code>latest<\/code> tag is convenient for testing but is not a reproducible production dependency. Pin a reviewed image version or digest for CI\/CD.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Native Windows using Git Bash is possible, but has additional PATH, Bash, and tool compatibility requirements.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. Create a Terraform Repository Structure<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A well-organized Terraform repository makes hook configuration simpler.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider the following example:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform-infrastructure\/\n\u2502\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .pre-commit-config.yaml\n\u251c\u2500\u2500 .tflint.hcl\n\u2502\n\u251c\u2500\u2500 modules\/\n\u2502   \u251c\u2500\u2500 vpc\/\n\u2502   \u2502   \u251c\u2500\u2500 main.tf\n\u2502   \u2502   \u251c\u2500\u2500 variables.tf\n\u2502   \u2502   \u251c\u2500\u2500 outputs.tf\n\u2502   \u2502   \u2514\u2500\u2500 README.md\n\u2502   \u2502\n\u2502   \u2514\u2500\u2500 eks\/\n\u2502       \u251c\u2500\u2500 main.tf\n\u2502       \u251c\u2500\u2500 variables.tf\n\u2502       \u251c\u2500\u2500 outputs.tf\n\u2502       \u2514\u2500\u2500 README.md\n\u2502\n\u251c\u2500\u2500 environments\/\n\u2502   \u251c\u2500\u2500 development\/\n\u2502   \u2502   \u251c\u2500\u2500 main.tf\n\u2502   \u2502   \u251c\u2500\u2500 versions.tf\n\u2502   \u2502   \u2514\u2500\u2500 terraform.tfvars.example\n\u2502   \u2502\n\u2502   \u251c\u2500\u2500 staging\/\n\u2502   \u2502   \u251c\u2500\u2500 main.tf\n\u2502   \u2502   \u2514\u2500\u2500 versions.tf\n\u2502   \u2502\n\u2502   \u2514\u2500\u2500 production\/\n\u2502       \u251c\u2500\u2500 main.tf\n\u2502       \u2514\u2500\u2500 versions.tf\n\u2502\n\u2514\u2500\u2500 .github\/\n    \u2514\u2500\u2500 workflows\/\n        \u2514\u2500\u2500 terraform-quality.yml<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This structure separates reusable modules from environment-specific root configurations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The paths are examples. Adapt the directory filters in subsequent sections to match your actual repository.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. Use Case 1: Automatically Format Terraform Code<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Formatting is the first and most useful Terraform pre-commit use case.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform provides:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform fmt<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To format all Terraform files under the current directory:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform fmt -recursive<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To check formatting without modifying files:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform fmt -check -recursive<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To display formatting differences:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform fmt -check -diff -recursive<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Configure terraform_fmt in pre-commit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create <code>.pre-commit-config.yaml<\/code> in the repository root:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">repos:\n  - repo: https:<span class=\"hljs-comment\">\/\/github.com\/antonbabenko\/pre-commit-terraform<\/span>\n    rev: v1<span class=\"hljs-number\">.109<\/span><span class=\"hljs-number\">.2<\/span>\n    <span class=\"hljs-attr\">hooks<\/span>:\n      - id: terraform_fmt<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Install the Git Hook<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Run the Hook<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_fmt --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Test Automatic Formatting<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create an intentionally poorly formatted Terraform file:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">variable <span class=\"hljs-string\">\"environment\"<\/span>{\ntype=string\n<span class=\"hljs-keyword\">default<\/span>=<span class=\"hljs-string\">\"development\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_fmt --all-files<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Terraform rewrites it into canonical formatting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">variable <span class=\"hljs-string\">\"environment\"<\/span> {\n  type    = string\n  <span class=\"hljs-keyword\">default<\/span> = <span class=\"hljs-string\">\"development\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If files are modified, pre-commit may report a failure for that run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Review the changes:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git diff<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Stage the corrected files and retry.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">When to Use<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Enable this hook in practically every Terraform repository.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is fast, deterministic for a given Terraform version, and prevents formatting-only review comments.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">9. Use Case 2: Validate Terraform Configuration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Formatting confirms stylistic consistency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Validation checks whether a Terraform configuration is internally consistent and satisfies supported Terraform language and provider-schema requirements.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform provides:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform validate<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">However, validation usually requires initialization first.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Initialize for Validation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a root module:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">terraform init -backend=<span class=\"hljs-literal\">false<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The <code>-backend=false<\/code> option is useful for validation without connecting to a configured remote backend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It does not eliminate all initialization requirements. Terraform may still need to download providers and child modules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Validate<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform validate<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Example successful output:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Success! The configuration is valid.<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Configure terraform_validate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add this hook under the existing <code>pre-commit-terraform<\/code> repository:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_validate\n        args:\n          - --tf-init-args=-backend=false\n          - --args=-no-color<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_validate --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Why This Matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The hook helps detect problems such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Invalid Terraform configuration structures.<\/li>\n\n\n\n<li>Missing required resource arguments.<\/li>\n\n\n\n<li>Incorrect references.<\/li>\n\n\n\n<li>Unsupported arguments.<\/li>\n\n\n\n<li>Certain provider configuration errors.<\/li>\n\n\n\n<li>Invalid relationships between configuration blocks.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Best Practice<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>terraform validate<\/code> is not equivalent to <code>terraform plan<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It does not verify the complete impact of infrastructure changes against real remote infrastructure or state.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use validation for static correctness and plan for infrastructure-change evaluation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommendation:<\/strong> In large repositories, put validation in the pre-push stage or CI\/CD because initialization and provider downloads may be expensive.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. Use Case 3: Automatically Lint Terraform Using TFLint<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform validation does not catch every code-quality or provider-specific issue.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">TFLint is designed to complement Terraform validation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It can identify:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Deprecated Terraform syntax.<\/li>\n\n\n\n<li>Unused declarations.<\/li>\n\n\n\n<li>Undocumented variables or outputs when corresponding rules are enabled.<\/li>\n\n\n\n<li>Cloud-provider-specific issues.<\/li>\n\n\n\n<li>Unsupported or suspicious configuration patterns covered by installed rules.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Create a TFLint Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create <code>.tflint.hcl<\/code> in your repository root.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">plugin <span class=\"hljs-string\">\"terraform\"<\/span> {\n  enabled = <span class=\"hljs-literal\">true<\/span>\n  preset  = <span class=\"hljs-string\">\"recommended\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This enables the bundled Terraform language ruleset with its recommended preset.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Initialize TFLint<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">tflint --init<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 3: Run TFLint Manually<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Inside a Terraform directory:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">tflint<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For a repository containing multiple Terraform directories, use the corresponding hook to select affected directories automatically.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Configure the pre-commit Hook<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_tflint\n        args:\n          - --args=--config=__GIT_WORKING_DIR__\/.tflint.hcl<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The special placeholder <code>__GIT_WORKING_DIR__<\/code> is replaced by the repository root by the pre-commit-terraform hook collection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This allows multiple Terraform directories to share the same TFLint configuration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Run the Hook<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_tflint --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Enable Additional Rules<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_tflint\n        args:\n          - --args=--config=__GIT_WORKING_DIR__\/.tflint.hcl\n          - --args=--enable-rule=terraform_documented_variables<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This enables the rule checking whether variables are documented.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cloud-Specific Linting<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">TFLint can also load provider-specific rulesets for AWS, Azure, and Google Cloud.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These plugins must be configured and installed separately.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For production use, pin reviewed plugin versions, review the applicable rules, and execute <code>tflint --init<\/code> in an environment where the configuration and plugin sources are available.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">When to Use<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">TFLint is particularly useful for teams maintaining cloud infrastructure across many modules and environments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It catches issues that Terraform formatting and validation alone may not identify.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. Use Case 4: Scan Terraform Security Misconfigurations with Trivy<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform can create security-sensitive infrastructure resources.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Publicly exposed network services.<\/li>\n\n\n\n<li>Unencrypted storage.<\/li>\n\n\n\n<li>Broad IAM permissions.<\/li>\n\n\n\n<li>Insecure database configurations.<\/li>\n\n\n\n<li>Missing security controls.<\/li>\n\n\n\n<li>Weak infrastructure defaults.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Trivy provides Infrastructure-as-Code misconfiguration scanning.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Test Trivy Manually<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">trivy config .<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To show only high and critical findings:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">trivy config \\\n  --severity HIGH,CRITICAL \\\n  .<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To make the command fail when matching findings are detected:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">trivy config \\\n  --severity HIGH,CRITICAL \\\n  --<span class=\"hljs-keyword\">exit<\/span>-code <span class=\"hljs-number\">1<\/span> \\\n  .<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The nonzero exit status can be used to block a pipeline or Git hook.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Configure terraform_trivy<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_trivy\n        args:\n          - --args=--severity=HIGH,CRITICAL\n          - --args=--exit-code=1<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Run the Hook<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_trivy --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Example: Detect Public Exposure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Consider this Terraform resource:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">resource <span class=\"hljs-string\">\"aws_security_group\"<\/span> <span class=\"hljs-string\">\"example\"<\/span> {\n  name = <span class=\"hljs-string\">\"example-security-group\"<\/span>\n\n  ingress {\n    from_port   = <span class=\"hljs-number\">22<\/span>\n    to_port     = <span class=\"hljs-number\">22<\/span>\n    protocol    = <span class=\"hljs-string\">\"tcp\"<\/span>\n    cidr_blocks = &#91;<span class=\"hljs-string\">\"0.0.0.0\/0\"<\/span>]\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This configuration allows SSH access from any IPv4 address.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trivy can detect such exposures when an enabled rule matches the configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The exact finding, severity, and rule ID depend on the installed Trivy version and its policy database.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">When to Use<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Security scanning should be mandatory in production Terraform workflows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For smaller projects, running Trivy locally may be sufficient.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For larger monorepos, put full Trivy scans in pre-push and CI\/CD while keeping local commits fast.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. Use Case 5: Scan Terraform with Checkov<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Checkov is another Infrastructure-as-Code scanning tool.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It performs policy and security checks against Terraform and other supported infrastructure configuration formats.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Checkov and Trivy overlap in some capabilities, but their rulesets and reporting workflows differ.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Run Checkov Manually<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Scan the current directory:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">checkov -d .<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To scan Terraform specifically:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">checkov -d . --framework terraform<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 2: Configure Checkov with pre-commit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add the following under the <code>pre-commit-terraform<\/code> repository:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_checkov\n        args:\n          - --args=--quiet<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_checkov --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 3: Exclude a Specific Rule When Justified<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_checkov\n        args:\n          - --args=--quiet\n          - --args=--skip-check CKV2_AWS_8<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Rule suppression should always have a documented justification and approval process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not disable a security rule simply to make the build green.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Trivy vs. Checkov<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Criteria<\/th><th>Trivy<\/th><th>Checkov<\/th><\/tr><tr><td>Terraform misconfiguration scanning<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>IaC policy coverage<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Supports additional IaC formats<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>CI\/CD integration<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Security reporting<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Policy model<\/td><td>Trivy rules and policies<\/td><td>Checkov policies<\/td><\/tr><tr><td>Terraform pre-commit hook<\/td><td><code>terraform_trivy<\/code><\/td><td><code>terraform_checkov<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For many organizations, one primary scanner with a well-maintained policy baseline is better than enabling multiple scanners without a plan for handling duplicate findings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use both where distinct policy coverage justifies the additional complexity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. Use Case 6: Automatically Generate Terraform Module Documentation<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform modules frequently contain variables, outputs, requirements, and provider details.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keeping a module&#8217;s README synchronized with its configuration can be tedious.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>terraform-docs<\/code> can automatically generate this documentation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Generate Documentation Manually<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a module:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform-docs markdown table .\/modules\/vpc<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To write documentation into a README:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform-docs markdown table \\\n  --output-file README.md \\\n  --output-mode inject \\\n  .\/modules\/vpc<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 2: Configure terraform_docs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_docs\n        files: ^modules\/.*\\.tf$\n        args:\n          - --hook-config=--add-to-existing-file=true\n          - --hook-config=--create-file-if-not-exist=true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This scopes documentation generation to Terraform files inside the <code>modules\/<\/code> directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It avoids unexpectedly generating documentation for every environment root module.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Add Documentation Markers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In a module&#8217;s README:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"># VPC Terraform Module\n\nThis module creates the required network infrastructure.\n\n## Usage\n\nAdd usage examples here.\n\n<span class=\"hljs-comment\">&lt;!-- BEGIN_TF_DOCS --&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- END_TF_DOCS --&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 4: Run the Hook<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_docs --all-files<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The hook updates the generated documentation section.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Commit Documentation Changes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Review the changes:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git diff<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Stage the module files and updated README:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git add modules\/vpc\/<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Benefits<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Consistent documentation.<\/li>\n\n\n\n<li>Less manual maintenance.<\/li>\n\n\n\n<li>Easier module onboarding.<\/li>\n\n\n\n<li>More complete variable and output documentation.<\/li>\n\n\n\n<li>Documentation changes reviewed alongside code.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Recommendation:<\/strong> Enable this hook for reusable Terraform modules rather than indiscriminately across every Terraform directory.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. Use Case 7: Maintain Terraform Provider Lock Files<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform uses <code>.terraform.lock.hcl<\/code> to record selected dependency provider versions and checksums.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This file helps teams use consistent provider versions across development and automation environments.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Initialize Terraform<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform init<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Terraform can generate or update <code>.terraform.lock.hcl<\/code> during initialization.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Generate Checksums for Multiple Platforms<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a particular root module:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform providers lock \\\n  -platform=darwin_amd64 \\\n  -platform=darwin_arm64 \\\n  -platform=linux_amd64 \\\n  -platform=linux_arm64 \\\n  -platform=windows_amd64<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is useful when developers use macOS or Windows while CI\/CD executes on Linux.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Only include platforms required by your organization and supported by the selected providers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Configure the Provider Lock Hook<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Optional hook:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_providers_lock\n        args:\n          - --hook-config=--mode=regenerate-lockfile-if-some-platform-missed\n          - --args=-platform=darwin_arm64\n          - --args=-platform=linux_amd64\n          - --args=-platform=windows_amd64<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_providers_lock --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Important Considerations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Provider lock operations may require network access.<\/li>\n\n\n\n<li>Lockfile regeneration can be slow.<\/li>\n\n\n\n<li>Some providers do not support every platform.<\/li>\n\n\n\n<li>Lockfiles should normally be committed for root configurations.<\/li>\n\n\n\n<li>Do not blindly update providers on every commit.<\/li>\n\n\n\n<li>Private provider registries may require authentication.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The provider-lock hook is optional. Many teams prefer explicit lockfile maintenance during dependency upgrades rather than running it automatically on every commit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. Use Case 8: Detect Private Keys and Common File Problems<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform repositories can contain files such as:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">variables<\/span><span class=\"hljs-selector-class\">.tf<\/span>\n<span class=\"hljs-selector-tag\">terraform<\/span><span class=\"hljs-selector-class\">.tfvars<\/span><span class=\"hljs-selector-class\">.example<\/span>\n<span class=\"hljs-selector-tag\">backend<\/span><span class=\"hljs-selector-class\">.hcl<\/span>\n<span class=\"hljs-selector-tag\">provider<\/span><span class=\"hljs-selector-class\">.tf<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">While infrastructure parameters often belong in version control, credentials and local state files usually do not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add the standard pre-commit hooks collection:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code> - repo: https:\/\/github.com\/pre-commit\/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: trailing-whitespace\n      - id: end-of-file-fixer\n      - id: check-yaml\n      - id: check-json\n      - id: check-merge-conflict\n      - id: detect-private-key\n      - id: check-added-large-files\n        args: [--maxkb=1024]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These hooks provide additional file hygiene and basic safety checks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Test Private-Key Detection<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">pre-commit run detect-<span class=\"hljs-keyword\">private<\/span>-key --all-files<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Validate YAML Configuration<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run check-yaml --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Check for Merge Conflicts<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run check-merge-conflict --all-files<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Recommended Terraform .gitignore<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># Terraform working directories<\/span>\n**\/.terraform<span class=\"hljs-comment\">\/*\n\n# Local Terraform state files\n*.tfstate\n*.tfstate.*\n\n# Terraform crash logs\ncrash.log\ncrash.*.log\n\n# Saved plan artifacts\n*.tfplan\ntfplan\ntfplan.*\n\n# Local overrides\noverride.tf\noverride.tf.json\n*_override.tf\n*_override.tf.json\n\n# Sensitive local variable files\n*.auto.tfvars\n*.auto.tfvars.json\n\n# Local credentials\n.env\n.env.*\n!.env.example\n\n# Terraform CLI configuration\n.terraformrc\nterraform.rc<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Do not ignore <code>.terraform.lock.hcl<\/code> for normal root-module workflows where provider dependency locking is required.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also, do not assume <code>.gitignore<\/code> prevents secret leakage. It does not remove files already tracked by Git, and Terraform state may contain sensitive data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use secret scanning and appropriate state-storage security controls in addition to Git hooks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. Use Case 9: Separate Fast and Slow Hooks<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A common mistake is enabling every hook during <code>git commit<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This can make commits frustratingly slow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A better approach is to separate quick checks from resource-intensive checks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fast Checks \u2014 Pre-commit<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">terraform_fmt\nterraform_tflint\nterraform_docs\ntrailing-whitespace\nend-of-file-fixer\ncheck-yaml\ndetect-<span class=\"hljs-keyword\">private<\/span>-key<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Heavier Checks \u2014 Pre-push<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform_validate\nterraform_trivy<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Additional complete scans and policy gates can run in CI\/CD.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Configure Git Hook Stages<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">default_install_hook_types:\n  - pre-commit\n  - pre-push\n\n<span class=\"hljs-attr\">repos<\/span>:\n  - repo: https:<span class=\"hljs-comment\">\/\/github.com\/antonbabenko\/pre-commit-terraform<\/span>\n    rev: v1<span class=\"hljs-number\">.109<\/span><span class=\"hljs-number\">.2<\/span>\n    <span class=\"hljs-attr\">hooks<\/span>:\n      - id: terraform_fmt\n        <span class=\"hljs-attr\">stages<\/span>: &#91;pre-commit]\n\n      - id: terraform_validate\n        <span class=\"hljs-attr\">stages<\/span>: &#91;pre-push]\n        <span class=\"hljs-attr\">args<\/span>:\n          - --tf-init-args=-backend=<span class=\"hljs-literal\">false<\/span>\n\n      - id: terraform_trivy\n        <span class=\"hljs-attr\">stages<\/span>: &#91;pre-push]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install both hook launchers:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install \\\n  --hook-type pre-commit \\\n  --hook-type pre-push<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Run Pre-commit Stage Manually<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-commit<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Run Pre-push Stage Manually<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-push<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Benefits<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This approach keeps routine commits fast while still checking more expensive conditions before code is pushed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, developers can bypass local hooks. Required CI\/CD checks remain essential.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. Use Case 10: Configure pre-commit for Terraform Monorepos<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform monorepos commonly contain many independent working directories.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">infrastructure\/\n\u251c\u2500\u2500 modules\/\n\u2502   \u251c\u2500\u2500 vpc\/\n\u2502   \u2514\u2500\u2500 eks\/\n\u251c\u2500\u2500 environments\/\n\u2502   \u251c\u2500\u2500 development\/\n\u2502   \u251c\u2500\u2500 staging\/\n\u2502   \u2514\u2500\u2500 production\/\n\u2514\u2500\u2500 .pre-commit-config.yaml<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Each environment may use different:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Providers.<\/li>\n\n\n\n<li>Provider versions.<\/li>\n\n\n\n<li>Backend configurations.<\/li>\n\n\n\n<li>Modules.<\/li>\n\n\n\n<li>Credentials.<\/li>\n\n\n\n<li>Terraform CLI versions.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform operations are normally scoped to working directories, whereas pre-commit selects files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The pre-commit-terraform hooks handle much of the working-directory selection automatically.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Run Checks Only for Development<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run \\\n  --files environments\/development\/main.tf<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Run Checks Only for Production<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run \\\n  --files environments\/production\/main.tf<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The relevant hooks determine the Terraform working directories based on the files supplied.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Limit Security Scans to Production<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: terraform_trivy\n        files: ^environments\/production\/.*\\.tf$<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Exclude Generated Files<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">At the top of <code>.pre-commit-config.yaml<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">exclude: |\n  (?x)^(\n    \\.terraform\/|\n    .*<span class=\"hljs-regexp\">\/\\.terraform\/<\/span>|\n    generated\/|\n    vendor\/\n  )<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Important Limitation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">pre-commit is file-oriented.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the only change in a directory is deletion of a Terraform file, directory-oriented checks may be skipped because no remaining matching file is passed to the hook.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For that reason, CI\/CD should also run complete validation for critical Terraform working directories.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. Use Case 11: Scan Terraform Infrastructure Cost<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Infrastructure cost awareness can be incorporated into Terraform development using Infracost.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Infracost estimates costs for supported Terraform resources.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Install Infracost<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Follow:<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/www.infracost.io\/docs\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Authenticate using an appropriately scoped Infracost API key.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Run Manually<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For an example root module:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">infracost breakdown \\\n  --path environments\/development<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Add the Hook<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>     - id: infracost_breakdown\n        args:\n          - --args=--path=environments\/development<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The hook can evaluate estimated infrastructure costs according to the configured Infracost settings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The hook also supports jq-compatible cost conditions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended Usage<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Cost analysis is usually better suited to CI\/CD or pull-request reporting than every local commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cost estimation may require remote API access, consumes additional time, and does not cover every Terraform resource.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. Use Case 12: Configure Terraform pre-commit for Terragrunt<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terragrunt projects use additional HCL configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The pre-commit-terraform repository provides hooks such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terragrunt_fmt\nterragrunt_validate\nterragrunt_validate_inputs\nterragrunt_providers_lock<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code> - repo: https:\/\/github.com\/antonbabenko\/pre-commit-terraform\n    rev: v1.109.2\n    hooks:\n      - id: terragrunt_fmt\n\n      - id: terragrunt_validate\n\n      - id: terragrunt_validate_inputs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These hooks require a compatible Terragrunt installation and may need access to providers, modules, or cloud authentication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For large Terragrunt deployments, start with formatting and add validation in a controlled stage after testing initialization and dependency behavior.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. Use Case 13: Support OpenTofu with pre-commit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The community hook collection also supports OpenTofu.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you use OpenTofu instead of Terraform, you can explicitly configure the hook to use it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code> - repo: https:\/\/github.com\/antonbabenko\/pre-commit-terraform\n    rev: v1.109.2\n    hooks:\n      - id: terraform_fmt\n        args:\n          - --hook-config=--tf-path=opentofu\n\n      - id: terraform_validate\n        args:\n          - --hook-config=--tf-path=opentofu<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ensure OpenTofu is installed and accessible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can verify it using:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">tofu version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The hook&#8217;s <code>--tf-path=opentofu<\/code> setting explicitly selects OpenTofu through the hook collection&#8217;s supported tool-resolution mechanism.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not assume Terraform and OpenTofu versions are interchangeable. Test formatting, validation, and provider compatibility against the versions your organization actually uses.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. Use Case 14: Automate Terraform Quality Checks in GitHub Actions<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Local Git hooks alone do not guarantee quality.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Developers can skip hooks, and automated changes may bypass local workstation controls entirely.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CI\/CD provides centrally executed checks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">.github\/workflows\/terraform-quality.yml<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For a basic Terraform formatting and validation workflow:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">name: Terraform Quality Checks\n\n<span class=\"hljs-attr\">on<\/span>:\n  pull_request:\n  push:\n    branches:\n      - main\n\n<span class=\"hljs-attr\">permissions<\/span>:\n  contents: read\n\n<span class=\"hljs-attr\">jobs<\/span>:\n  terraform-quality:\n    runs-on: ubuntu-latest\n\n    <span class=\"hljs-attr\">steps<\/span>:\n      - name: Checkout Repository\n        <span class=\"hljs-attr\">uses<\/span>: actions\/checkout@v4\n\n      - name: Setup Python\n        <span class=\"hljs-attr\">uses<\/span>: actions\/setup-python@v6\n        <span class=\"hljs-attr\">with<\/span>:\n          python-version: <span class=\"hljs-string\">\"3.13\"<\/span>\n\n      - name: Setup Terraform\n        <span class=\"hljs-attr\">uses<\/span>: hashicorp\/setup-terraform@v4\n        <span class=\"hljs-attr\">with<\/span>:\n          terraform_version: <span class=\"hljs-string\">\"1.14.6\"<\/span>\n          <span class=\"hljs-attr\">terraform_wrapper<\/span>: <span class=\"hljs-literal\">false<\/span>\n\n      - name: Install pre-commit\n        <span class=\"hljs-attr\">run<\/span>: |\n          python -m pip install pre-commit==<span class=\"hljs-number\">4.7<\/span><span class=\"hljs-number\">.0<\/span>\n\n      - name: Validate pre-commit Configuration\n        <span class=\"hljs-attr\">run<\/span>: |\n          pre-commit validate-config\n\n      - name: Check Terraform Formatting\n        <span class=\"hljs-attr\">run<\/span>: |\n          pre-commit run terraform_fmt --all-files\n\n      - name: Validate Terraform\n        <span class=\"hljs-attr\">run<\/span>: |\n          pre-commit run terraform_validate \\\n            --all-files \\\n            --hook-stage pre-push<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This example assumes the corresponding hooks are present in <code>.pre-commit-config.yaml<\/code> and that validation does not require unavailable private credentials.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is a <strong>basic pipeline<\/strong>, not a complete installation of every optional scanner in this article.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To enforce TFLint, Trivy, Checkov, or terraform-docs in GitHub Actions, install the corresponding tools or use an approved, pinned container image containing them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Complete Hook Execution Using a Docker Image<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The pre-commit-terraform project publishes an image containing Terraform-related tooling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An example CI\/CD job can run:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\"><span class=\"hljs-attribute\">name<\/span>: Terraform Complete Checks\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n\njobs:\n  terraform-checks:\n    runs-on: ubuntu-latest\n\n    container:\n      image: ghcr.io\/antonbabenko\/pre-commit-terraform:latest\n\n    defaults:\n      run:\n        shell: bash\n\n    steps:\n      - uses: actions\/checkout@v4\n\n      - name: Validate Configuration\n        run: pre-commit validate-config\n\n      - name: Run Pre-commit Checks\n        run: |\n          pre-commit run \\\n            --all-files \\\n            --hook-stage pre-commit\n\n      - name: Run Pre-push Checks\n        run: |\n          pre-commit run \\\n            --all-files \\\n            --hook-stage pre-push<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Production warning:<\/strong> The <code>latest<\/code> tag above is illustrative. Pin a verified image digest or reviewed version for real CI\/CD pipelines. Review the image&#8217;s included tool versions, dependencies, security posture, and access to private Terraform modules.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CI should fail when a hook finds a policy violation or modifies a file that should already have been formatted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not automatically commit hook-generated changes from untrusted pull-request workflows.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. Use Case 15: Enforce Terraform pre-commit Across a Team<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">An effective team rollout requires more than creating a YAML file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The repository configuration should be version-controlled and referenced in the developer onboarding process.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Commit the Configuration<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">git<\/span> <span class=\"hljs-selector-tag\">add<\/span> <span class=\"hljs-selector-class\">.pre-commit-config<\/span><span class=\"hljs-selector-class\">.yaml<\/span>\n<span class=\"hljs-selector-tag\">git<\/span> <span class=\"hljs-selector-tag\">commit<\/span> <span class=\"hljs-selector-tag\">-m<\/span> \"<span class=\"hljs-selector-tag\">Add<\/span> <span class=\"hljs-selector-tag\">Terraform<\/span> <span class=\"hljs-selector-tag\">pre-commit<\/span> <span class=\"hljs-selector-tag\">hooks<\/span>\"<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 2: Every Developer Installs the Hook<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After cloning the repository:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install \\\n  --hook-type pre-commit \\\n  --hook-type pre-push<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 3: Initialize Hook Environments<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install-hooks<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 4: Validate the Complete Repository<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-commit<\/code><\/span><\/pre>\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-push<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 5: Enforce Quality in CI\/CD<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add required CI checks to your repository&#8217;s branch protection or rulesets.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For critical infrastructure repositories, a useful quality policy is:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Control<\/th><th>Local<\/th><th>CI\/CD<\/th><\/tr><tr><td>Terraform formatting<\/td><td>Required<\/td><td>Required<\/td><\/tr><tr><td>Terraform linting<\/td><td>Recommended<\/td><td>Required<\/td><\/tr><tr><td>Terraform validation<\/td><td>Pre-push<\/td><td>Required<\/td><\/tr><tr><td>Terraform security scanning<\/td><td>Recommended<\/td><td>Required<\/td><\/tr><tr><td>Terraform documentation<\/td><td>Required for modules<\/td><td>Verified<\/td><\/tr><tr><td>Provider lock consistency<\/td><td>When changed<\/td><td>Verified<\/td><\/tr><tr><td>Terraform plan<\/td><td>Optional for local testing<\/td><td>Required before deployment<\/td><\/tr><tr><td>Code review<\/td><td>Pull request<\/td><td>Required<\/td><\/tr><tr><td>Terraform apply<\/td><td>No automatic commit-time apply<\/td><td>Separate authorized deployment<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Never make infrastructure-changing operations such as <code>terraform apply<\/code> part of a standard pre-commit hook.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">23. Complete Production-Ready .pre-commit-config.yaml<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The following is a recommended starting configuration for a Terraform repository with reusable modules and multiple environments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It combines formatting, file hygiene, linting, module documentation, validation, and security scanning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>File:<\/strong> <code>.pre-commit-config.yaml<\/code><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># Terraform pre-commit configuration<\/span>\n\nminimum_pre_commit_version: <span class=\"hljs-string\">\"4.0.0\"<\/span>\n\ndefault_install_hook_types:\n  - pre-commit\n  - pre-push\n\nexclude: |\n  (?x)^(\n    \\.terraform\/|\n    .*\/\\.terraform\/|\n    generated\/|\n    vendor\/\n  )\n\nrepos:\n\n  <span class=\"hljs-comment\"># General file hygiene<\/span>\n  - repo: https:<span class=\"hljs-comment\">\/\/github.com\/pre-commit\/pre-commit-hooks<\/span>\n    rev: v6<span class=\"hljs-number\">.0<\/span><span class=\"hljs-number\">.0<\/span>\n    hooks:\n\n      - id: trailing-whitespace\n        stages: &#91;pre-commit]\n\n      - id: end-of-file-fixer\n        stages: &#91;pre-commit]\n\n      - id: check-yaml\n        stages: &#91;pre-commit]\n\n      - id: check-json\n        stages: &#91;pre-commit]\n\n      - id: check-merge-conflict\n        stages: &#91;pre-commit]\n\n      - id: detect-<span class=\"hljs-keyword\">private<\/span>-key\n        stages: &#91;pre-commit]\n\n      - id: check-added-large-files\n        stages: &#91;pre-commit]\n        args:\n          - --maxkb=<span class=\"hljs-number\">1024<\/span>\n\n  <span class=\"hljs-comment\"># Terraform-specific checks<\/span>\n  - repo: https:<span class=\"hljs-comment\">\/\/github.com\/antonbabenko\/pre-commit-terraform<\/span>\n    rev: v1<span class=\"hljs-number\">.109<\/span><span class=\"hljs-number\">.2<\/span>\n    hooks:\n\n      <span class=\"hljs-comment\"># 1. Terraform formatting<\/span>\n      - id: terraform_fmt\n        stages: &#91;pre-commit]\n\n      <span class=\"hljs-comment\"># 2. Terraform linting<\/span>\n      - id: terraform_tflint\n        stages: &#91;pre-commit]\n        args:\n          - --args=--config=__GIT_WORKING_DIR__\/.tflint.hcl\n\n      <span class=\"hljs-comment\"># 3. Terraform module documentation<\/span>\n      - id: terraform_docs\n        stages: &#91;pre-commit]\n        files: ^modules\/.*\\.tf$\n        args:\n          - --hook-config=--add-to-existing-file=<span class=\"hljs-keyword\">true<\/span>\n          - --hook-config=--create-file-<span class=\"hljs-keyword\">if<\/span>-not-exist=<span class=\"hljs-keyword\">true<\/span>\n\n      <span class=\"hljs-comment\"># 4. Terraform validation<\/span>\n      - id: terraform_validate\n        stages: &#91;pre-push]\n        args:\n          - --tf-init-args=-backend=<span class=\"hljs-keyword\">false<\/span>\n          - --args=-no-color\n          - --hook-config=--parallelism-limit=<span class=\"hljs-number\">1<\/span>\n\n      <span class=\"hljs-comment\"># 5. Terraform security scanning<\/span>\n      - id: terraform_trivy\n        stages: &#91;pre-push]\n        args:\n          - --args=--severity=HIGH,CRITICAL\n          - --args=--<span class=\"hljs-keyword\">exit<\/span>-code=<span class=\"hljs-number\">1<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Additional .tflint.hcl<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create a file named <code>.tflint.hcl<\/code> in the repository root:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">plugin <span class=\"hljs-string\">\"terraform\"<\/span> {\n  enabled = <span class=\"hljs-literal\">true<\/span>\n  preset  = <span class=\"hljs-string\">\"recommended\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Activate the Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit validate-config<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install the hooks:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install \\\n  --hook-type pre-commit \\\n  --hook-type pre-push<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Prepare hook environments:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit install-hooks<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Run quick checks:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-commit<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Run heavier checks:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run --all-files --hook-stage pre-push<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Important Setup Notes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This full configuration assumes that Terraform, TFLint, terraform-docs, and Trivy are installed and accessible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The shared <code>.tflint.hcl<\/code> example must exist before enabling the TFLint hook.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>terraform_docs<\/code> hook assumes reusable modules are stored under <code>modules\/<\/code>. Change or remove that path filter for different repository structures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The validation hook uses Terraform initialization without a backend. This is appropriate for many repositories, but remote module dependencies and private registries may still require authentication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, run the full configuration against a representative repository and review the output before enforcing it across a team.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. Terraform pre-commit Command Cheat Sheet<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Use case<\/th><th>Command<\/th><\/tr><tr><td>Check pre-commit version<\/td><td><code>pre-commit --version<\/code><\/td><\/tr><tr><td>Validate hook configuration<\/td><td><code>pre-commit validate-config<\/code><\/td><\/tr><tr><td>Install default hook<\/td><td><code>pre-commit install<\/code><\/td><\/tr><tr><td>Prepare hook environments<\/td><td><code>pre-commit install-hooks<\/code><\/td><\/tr><tr><td>Run configured pre-commit hooks<\/td><td><code>pre-commit run<\/code><\/td><\/tr><tr><td>Run all pre-commit hooks against tracked files<\/td><td><code>pre-commit run --all-files<\/code><\/td><\/tr><tr><td>Run pre-push-stage hooks<\/td><td><code>pre-commit run --all-files --hook-stage pre-push<\/code><\/td><\/tr><tr><td>Format Terraform<\/td><td><code>terraform fmt -recursive<\/code><\/td><\/tr><tr><td>Check Terraform formatting<\/td><td><code>terraform fmt -check -recursive<\/code><\/td><\/tr><tr><td>Run terraform_fmt hook<\/td><td><code>pre-commit run terraform_fmt --all-files<\/code><\/td><\/tr><tr><td>Validate Terraform manually<\/td><td><code>terraform validate<\/code><\/td><\/tr><tr><td>Run Terraform validation hook<\/td><td><code>pre-commit run terraform_validate --all-files<\/code><\/td><\/tr><tr><td>Run TFLint<\/td><td><code>tflint<\/code><\/td><\/tr><tr><td>Run TFLint hook<\/td><td><code>pre-commit run terraform_tflint --all-files<\/code><\/td><\/tr><tr><td>Run Trivy<\/td><td><code>trivy config .<\/code><\/td><\/tr><tr><td>Run Trivy hook<\/td><td><code>pre-commit run terraform_trivy --all-files<\/code><\/td><\/tr><tr><td>Run Checkov<\/td><td><code>checkov -d . --framework terraform<\/code><\/td><\/tr><tr><td>Run Checkov hook<\/td><td><code>pre-commit run terraform_checkov --all-files<\/code><\/td><\/tr><tr><td>Generate module documentation<\/td><td><code>terraform-docs markdown table .\/modules\/vpc<\/code><\/td><\/tr><tr><td>Run terraform_docs hook<\/td><td><code>pre-commit run terraform_docs --all-files<\/code><\/td><\/tr><tr><td>Update hook revisions<\/td><td><code>pre-commit autoupdate<\/code><\/td><\/tr><tr><td>Clear cached hook environments<\/td><td><code>pre-commit clean<\/code><\/td><\/tr><tr><td>Remove hooks<\/td><td><code>pre-commit uninstall<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For hooks configured exclusively for the pre-push stage, explicitly add <code>--hook-stage pre-push<\/code> when running them manually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. Troubleshooting Terraform pre-commit Problems<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Error 1: terraform: command not found<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Possible cause:<\/strong> Terraform is not installed or is missing from PATH.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verify:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Find the executable:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">command -v terraform<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Install the compatible Terraform version required by the project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 2: terraform_fmt Modified Files<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">Terraform fmt........................Failed\n- files were modified by <span class=\"hljs-keyword\">this<\/span> hook<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This usually indicates that formatting corrections were applied.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Review:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git diff<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Stage the corrected files and rerun the hook.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 3: terraform_validate Fails During Initialization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Possible causes:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Required provider or module cannot be downloaded.<\/li>\n\n\n\n<li>Private module authentication is missing.<\/li>\n\n\n\n<li>Terraform or provider version mismatch.<\/li>\n\n\n\n<li>Invalid dependency lock file.<\/li>\n\n\n\n<li>Unsupported initialization or backend configuration.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Run the commands manually in the failing working directory:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">terraform init -backend=<span class=\"hljs-literal\">false<\/span>\nterraform validate<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Review the actual error message before changing the hook configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 4: TFLint Plugin Initialization Failed<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Try:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">tflint --init<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Review <code>.tflint.hcl<\/code> and any configured external plugins.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Confirm the selected plugin version is compatible with the installed TFLint version.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 5: Trivy Reports Security Violations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">trivy config .<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Inspect each finding and determine whether it represents a real security risk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Correct the Terraform configuration or document a justified, approved exception.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid suppressing rules globally merely to allow the pipeline to pass.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 6: terraform-docs Modified README.md<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is expected when generated documentation needs updating.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Review:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git diff<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Stage the README changes alongside the Terraform code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 7: Hooks Are Very Slow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Possible causes include provider downloads, private module initialization, large security scans, or too many Terraform directories.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Moving validation to pre-push or CI\/CD.<\/li>\n\n\n\n<li>Using provider\/plugin caches carefully.<\/li>\n\n\n\n<li>Limiting parallelism for conflicting initialization workloads.<\/li>\n\n\n\n<li>Scoping hooks to relevant directories.<\/li>\n\n\n\n<li>Avoiding unnecessary provider upgrades.<\/li>\n\n\n\n<li>Pinning tool versions.<\/li>\n\n\n\n<li>Splitting large monorepo checks into independent CI jobs.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The hook collection supports options such as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">args:\n  - --hook-config=--parallelism-limit=1<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This can be particularly useful when Terraform validation shares plugin caches or accesses constrained private registries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 8: Windows Hook Fails with Bash Error<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Terraform hook collection relies on Bash.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use WSL2 or Docker when Windows shell compatibility becomes problematic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verify Bash availability:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">bash --version<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For native Windows setups, be aware that different installations of Bash can produce incompatible behavior.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error 9: No Terraform Files Found<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">pre-commit runs hooks only when the hook&#8217;s file filters match relevant files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git status<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Inspect which files are tracked:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">git ls-files<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Then run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit run terraform_fmt --all-files --verbose<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A hook reporting <code>Skipped<\/code> can be normal when no applicable files exist.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. Terraform pre-commit Best Practices for Production<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A mature Terraform workflow should balance reliability, security, and developer experience.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Always Enable Terraform Formatting<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Make <code>terraform_fmt<\/code> mandatory in all Terraform repositories.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Use TFLint for Additional Code Quality<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform validation and TFLint complement each other.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Run Security Scans Before Merging<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use Trivy, Checkov, or another approved IaC scanning tool.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Avoid Expensive Operations During Every Commit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Keep the pre-commit stage fast.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Move expensive operations to pre-push or CI\/CD.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Keep Module Documentation Updated<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use terraform-docs for reusable modules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6. Maintain Provider Lock Files<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Commit appropriate <code>.terraform.lock.hcl<\/code> files for root modules.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Review provider version changes deliberately.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Never Run terraform apply in pre-commit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Git commit must not unexpectedly create, change, or destroy infrastructure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8. Enforce CI\/CD Checks<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Local hooks can be skipped, so CI must independently run required validations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">9. Pin Versions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Pin the pre-commit framework, hook repositories, Terraform CLI, TFLint, scanner versions, and relevant CI dependencies.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">10. Minimize Permissions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Formatting and validation should not require highly privileged production credentials.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">11. Avoid Secrets in Terraform Repositories<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Protect local variable files, state files, credentials, and backend configurations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use secure credential delivery and dedicated secrets management.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">12. Keep Terraform Plans Separate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform plan may require access to remote state, cloud APIs, and sensitive variables.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Execute plans in an appropriately controlled environment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">13. Configure Environment Isolation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use separate credentials and appropriate access controls for development, staging, and production.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">14. Review Hook Upgrades<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">pre-commit autoupdate<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Then review the dependency changes and rerun all checks before committing them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">15. Optimize Before Enforcing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Measure how long hooks take across representative modules before making them mandatory for every developer.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. Frequently Asked Questions<\/h1>\n\n\n\n<h3 class=\"wp-block-heading\">Q1. Is pre-commit mandatory for Terraform?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Terraform works without pre-commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, pre-commit can improve consistency and catch common errors earlier.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q2. Should terraform fmt run before terraform validate?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Formatting is typically faster, so it is efficient to run formatting before more expensive validation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q3. Is terraform validate enough for Terraform security?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Terraform validation checks configuration correctness, not comprehensive security compliance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use security scanners and appropriate policy controls.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q4. Which is better for Terraform: Checkov or Trivy?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Both support Terraform misconfiguration scanning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Choose based on policy coverage, existing integrations, reporting requirements, and your team&#8217;s security standards.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q5. Should TFLint run on every commit?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For smaller repositories, yes, if execution remains fast.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For large monorepos, selective execution or CI-based linting may improve developer experience.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q6. Should terraform-docs run automatically?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, particularly for reusable Terraform modules.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It helps keep documentation aligned with code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q7. Can pre-commit work with HCP Terraform or Terraform Cloud?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, local validation, private module downloads, and remote execution workflows may have different authentication and initialization requirements.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid configuring ordinary developer hooks to depend on privileged production credentials.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q8. Can pre-commit work with Terraform modules stored in private Git repositories?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, provided the environment can authenticate to those repositories.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hooks that initialize Terraform may need access to the private module sources.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q9. Can pre-commit be used in a Terraform monorepo?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Terraform hook collection supports directory-aware operations, and pre-commit supports file filters and exclusions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For deleted-file-only changes or cross-directory dependencies, supplement file-based checks with CI validation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q10. Does pre-commit automatically fix Terraform security issues?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Formatting and documentation hooks may automatically modify files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Security scanners typically report problems rather than safely rewriting infrastructure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Security findings usually require deliberate review and changes.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. Final Recommended Terraform Quality Workflow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For most professional Terraform teams, the following staged workflow is a strong starting point.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><th>Check<\/th><th>Pre-commit<\/th><th>Pre-push<\/th><th>CI\/CD<\/th><\/tr><tr><td>Terraform formatting<\/td><td>Yes<\/td><td>\u2014<\/td><td>Yes<\/td><\/tr><tr><td>Whitespace\/file checks<\/td><td>Yes<\/td><td>\u2014<\/td><td>Yes<\/td><\/tr><tr><td>YAML\/JSON validation<\/td><td>Yes<\/td><td>\u2014<\/td><td>Yes<\/td><\/tr><tr><td>TFLint<\/td><td>Yes<\/td><td>Optional<\/td><td>Yes<\/td><\/tr><tr><td>terraform-docs<\/td><td>Yes<\/td><td>\u2014<\/td><td>Yes<\/td><\/tr><tr><td>Terraform validate<\/td><td>\u2014<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Trivy<\/td><td>\u2014<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Checkov<\/td><td>\u2014<\/td><td>Optional<\/td><td>Optional<\/td><\/tr><tr><td>Provider lock checks<\/td><td>On dependency change<\/td><td>Optional<\/td><td>Yes<\/td><\/tr><tr><td>Infracost<\/td><td>\u2014<\/td><td>\u2014<\/td><td>Optional<\/td><\/tr><tr><td>Terraform plan<\/td><td>\u2014<\/td><td>\u2014<\/td><td>Yes<\/td><\/tr><tr><td>Terraform apply<\/td><td>Never<\/td><td>Never<\/td><td>Approved deployment only<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The exact gates should reflect repository size, team requirements, security policy, and infrastructure criticality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">pre-commit can make Terraform development more consistent by automatically running formatting, validation, linting, documentation, and security checks before infrastructure code is merged.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The most important hooks are:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">terraform_fmt\nterraform_validate\nterraform_tflint\nterraform_trivy\nterraform_docs<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Additional hooks for Checkov, provider locks, cost estimation, and Terragrunt can be introduced when required.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A practical rollout begins with formatting and linting, adds validation and security scanning, and then enforces the same standards through CI\/CD.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the right configuration, Terraform teams can reduce avoidable mistakes, improve code reviews, strengthen security practices, and maintain more reliable infrastructure repositories without unnecessarily slowing down daily development.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Official Documentation and References<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/pre-commit.com\/\">pre-commit Official Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/pre-commit\/pre-commit\/releases\/latest\">pre-commit Releases<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/antonbabenko\/pre-commit-terraform\">pre-commit-terraform Repository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/antonbabenko\/pre-commit-terraform\/releases\/latest\">pre-commit-terraform Releases<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/pre-commit\/pre-commit-hooks\">Official pre-commit Hooks<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.hashicorp.com\/terraform\/cli\">Terraform CLI Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.hashicorp.com\/terraform\/cli\/commands\/validate\">Terraform Validation Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/terraform-linters\/tflint\">TFLint Repository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/terraform-docs.io\/\">terraform-docs Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/trivy.dev\/\">Trivy Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/bridgecrewio\/checkov\">Checkov Repository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.infracost.io\/docs\/\">Infracost Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\">GitHub Actions Documentation<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Last reviewed: October 20, 2026. Hook behavior, command-line options, tool releases, and integration requirements may change. Validate the complete configuration against a representative project before enforcing it in production.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Terraform pre-commit Hooks: Installation, Configuration, TFLint, Trivy, Checkov &amp; CI\/CD Guide Learn to configure pre-commit for Terraform with step-by-step commands, terraform fmt, validate, TFLint, Trivy, Checkov, terraform-docs,&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-79430","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79430","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=79430"}],"version-history":[{"count":2,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79430\/revisions"}],"predecessor-version":[{"id":79432,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79430\/revisions\/79432"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=79430"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=79430"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=79430"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}