Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

pre-commit git hooks to take care of Terraform configurations 🇺🇦

License

NotificationsYou must be signed in to change notification settings

antonbabenko/pre-commit-terraform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Latest Github tagMaintenance statusGHA Tests CI/CD BadgeCodecov pytest BadgeOpenSSF Scorecard BadgeOpenSSF Best Practices BadgeCodetriage - Help Contribute to Open Source Badge

StandWithUkraine Banner

pre-commit-terraform logo

pre-commit-terraform provides a collection ofGit Hooks for Terraform and related tools and is driven by thepre-commit framework. It helps ensure that Terraform, OpenTofu, and Terragrunt configurations are kept in good shape by automatically running various checks and formatting code before committing changes to version control system. This helps maintain code quality and consistency across the project.

It can be run:

  • Locally and in CI
  • As standalone Git hooks or as a Docker image
  • For the entire repository or just for change-related files (e.g., local git stash, last commit, or all changes in a Pull Request)

Want to contribute?Checkopen issuesandcontributing notes.

Sponsors

If you want to support the development ofpre-commit-terraform andmany other open-source projects, please become aGitHub Sponsor!

Table of content

How to install

1. Install dependencies

Docker

Pull docker image with all hooks:

TAG=latestdocker pull ghcr.io/antonbabenko/pre-commit-terraform:$TAG

All available tagshere.

CheckAbout Docker image security section to learn more about possible security issues and why you probably want to build and maintain your own image.

Build from scratch:

IMPORTANT
To build image you need to havedocker buildx enabled as default builder.
Otherwise - provideTARGETOS andTARGETARCH as additional--build-arg's todocker build.

When hooks-related--build-args are not specified, only the latest version ofpre-commit andterraform will be installed.

git clone git@github.com:antonbabenko/pre-commit-terraform.gitcd pre-commit-terraform# Install the latest versions of all the toolsdocker build -t pre-commit-terraform --build-arg INSTALL_ALL=true.

To install a specific version of individual tools, define it using--build-arg arguments or set it tolatest:

docker build -t pre-commit-terraform \    --build-arg PRE_COMMIT_VERSION=latest \    --build-arg OPENTOFU_VERSION=latest \    --build-arg TERRAFORM_VERSION=1.5.7 \    --build-arg CHECKOV_VERSION=2.0.405 \    --build-arg HCLEDIT_VERSION=latest \    --build-arg INFRACOST_VERSION=latest \    --build-arg TERRAFORM_DOCS_VERSION=0.15.0 \    --build-arg TERRAGRUNT_VERSION=latest \    --build-arg TERRASCAN_VERSION=1.10.0 \    --build-arg TFLINT_VERSION=0.31.0 \    --build-arg TFSEC_VERSION=latest \    --build-arg TFUPDATE_VERSION=latest \    --build-arg TRIVY_VERSION=latest \.

Set-e PRE_COMMIT_COLOR=never to disable the color output inpre-commit.

NOTEThe build install scripts are calling the GitHub API to resolve the release URL. If you need to authenticate those calls, you can pass a GitHub token (theGITHUB_TOKEN environment variable is expected to be set with anaccess token):

docker build -t pre-commit-terraform --build-arg GITHUB_TOKEN.
MacOS
brew install pre-commit terraform-docs tflint tfsec trivy checkov terrascan infracost tfupdate minamijoyo/hcledit/hcledit jq
Ubuntu 18.04
sudo apt updatesudo apt install -y unzip software-properties-commonsudo add-apt-repository ppa:deadsnakes/ppasudo apt install -y python3.7 python3-pippython3 -m pip install --upgrade pippip3 install --no-cache-dir pre-commitpython3.7 -m pip install -U checkovcurl -L"$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest| grep -o -E -m 1"https://.+?-linux-amd64.tar.gz")"> terraform-docs.tgz&& tar -xzf terraform-docs.tgz&& rm terraform-docs.tgz&& chmod +x terraform-docs&& sudo mv terraform-docs /usr/bin/curl -L"$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.zip")"> tflint.zip&& unzip tflint.zip&& rm tflint.zip&& sudo mv tflint /usr/bin/curl -L"$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest| grep -o -E -m 1"https://.+?tfsec-linux-amd64")"> tfsec&& chmod +x tfsec&& sudo mv tfsec /usr/bin/curl -L"$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest| grep -o -E -i -m 1"https://.+?/trivy_.+?_Linux-64bit.tar.gz")"> trivy.tar.gz&& tar -xzf trivy.tar.gz trivy&& rm trivy.tar.gz&& sudo mv trivy /usr/bincurl -L"$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest| grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")"> terrascan.tar.gz&& tar -xzf terrascan.tar.gz terrascan&& rm terrascan.tar.gz&& sudo mv terrascan /usr/bin/&& terrascan initsudo apt install -y jq&& \curl -L"$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest| grep -o -E -m 1"https://.+?-linux-amd64.tar.gz")"> infracost.tgz&& tar -xzf infracost.tgz&& rm infracost.tgz&& sudo mv infracost-linux-amd64 /usr/bin/infracost&& infracost auth logincurl -L"$(curl -s https://api.github.com/repos/minamijoyo/tfupdate/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.tar.gz")"> tfupdate.tar.gz&& tar -xzf tfupdate.tar.gz tfupdate&& rm tfupdate.tar.gz&& sudo mv tfupdate /usr/bin/curl -L"$(curl -s https://api.github.com/repos/minamijoyo/hcledit/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.tar.gz")"> hcledit.tar.gz&& tar -xzf hcledit.tar.gz hcledit&& rm hcledit.tar.gz&& sudo mv hcledit /usr/bin/
Ubuntu 20.04+
sudo apt updatesudo apt install -y unzip software-properties-common python3 python3-pip python-is-python3python3 -m pip install --upgrade pippip3 install --no-cache-dir pre-commitpip3 install --no-cache-dir checkovcurl -L"$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest| grep -o -E -m 1"https://.+?-linux-amd64.tar.gz")"> terraform-docs.tgz&& tar -xzf terraform-docs.tgz terraform-docs&& rm terraform-docs.tgz&& chmod +x terraform-docs&& sudo mv terraform-docs /usr/bin/curl -L"$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest| grep -o -E -m 1"https://.+?_Linux_x86_64.tar.gz")"> terrascan.tar.gz&& tar -xzf terrascan.tar.gz terrascan&& rm terrascan.tar.gz&& sudo mv terrascan /usr/bin/&& terrascan initcurl -L"$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.zip")"> tflint.zip&& unzip tflint.zip&& rm tflint.zip&& sudo mv tflint /usr/bin/curl -L"$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/latest| grep -o -E -m 1"https://.+?tfsec-linux-amd64")"> tfsec&& chmod +x tfsec&& sudo mv tfsec /usr/bin/curl -L"$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest| grep -o -E -i -m 1"https://.+?/trivy_.+?_Linux-64bit.tar.gz")"> trivy.tar.gz&& tar -xzf trivy.tar.gz trivy&& rm trivy.tar.gz&& sudo mv trivy /usr/binsudo apt install -y jq&& \curl -L"$(curl -s https://api.github.com/repos/infracost/infracost/releases/latest| grep -o -E -m 1"https://.+?-linux-amd64.tar.gz")"> infracost.tgz&& tar -xzf infracost.tgz&& rm infracost.tgz&& sudo mv infracost-linux-amd64 /usr/bin/infracost&& infracost auth logincurl -L"$(curl -s https://api.github.com/repos/minamijoyo/tfupdate/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.tar.gz")"> tfupdate.tar.gz&& tar -xzf tfupdate.tar.gz tfupdate&& rm tfupdate.tar.gz&& sudo mv tfupdate /usr/bin/curl -L"$(curl -s https://api.github.com/repos/minamijoyo/hcledit/releases/latest| grep -o -E -m 1"https://.+?_linux_amd64.tar.gz")"> hcledit.tar.gz&& tar -xzf hcledit.tar.gz hcledit&& rm hcledit.tar.gz&& sudo mv hcledit /usr/bin/
Windows 10/11

We highly recommend usingWSL/WSL2 with Ubuntu and following the Ubuntu installation guide. Or use Docker.

IMPORTANT
We won't be able to help with issues that can't be reproduced in Linux/Mac.
So, try to find a working solution and send PR before open an issue.

Otherwise, you can followthis gist:

  1. Installgit andgitbash
  2. InstallPython 3
  3. Install all prerequisites needed (see above)

Ensure your PATH environment variable looks forbash.exe inC:\Program Files\Git\bin (the one present inC:\Windows\System32\bash.exe does not work withpre-commit.exe)

Forcheckov, you may need to also set yourPYTHONPATH environment variable with the path to your Python modules.
E.g.C:\Users\USERNAME\AppData\Local\Programs\Python\Python39\Lib\site-packages

Full list of dependencies and where they are used:

  • pre-commit,terraform oropentofu,git,BASH3.2.57 or newer,Internet connection (on first run),x86_64 or arm64 compatible operating system,Some hardware where this OS will run,Electricity for hardware and internet connection,Some basic physical laws,Hope that it all will work.

  • checkov required forterraform_checkov hook
  • terraform-docs 0.12.0+ required forterraform_docs hook
  • terragrunt required forterragrunt_validate andterragrunt_valid_inputs hooks
  • terrascan required forterrascan hook
  • TFLint required forterraform_tflint hook
  • TFSec required forterraform_tfsec hook
  • Trivy required forterraform_trivy hook
  • infracost required forinfracost_breakdown hook
  • jq required forterraform_validate with--retry-once-with-cleanup flag, and forinfracost_breakdown hook
  • tfupdate required fortfupdate hook
  • hcledit required forterraform_wrapper_module_for_each hook

1.1 Custom Terraform binaries and OpenTofu support

It is possible to set custom path toterraform binary.
This makes it possible to useOpenTofu binary (tofu) instead ofterraform.

How binary discovery works and how you can redefine it (first matched takes precedence):

  1. Check if per hook configuration--hook-config=--tf-path=<path_to_binary_or_binary_name> is set
  2. Check ifPCT_TFPATH=<path_to_binary_or_binary_name> environment variable is set
  3. Check ifTERRAGRUNT_TFPATH=<path_to_binary_or_binary_name> environment variable is set
  4. Check ifterraform binary can be found in the user's$PATH
  5. Check iftofu binary can be found in the user's$PATH

2. Install the pre-commit hook globally

Note

Not needed if you use the Docker image

DIR=~/.git-templategit config --global init.templateDir${DIR}pre-commit init-templatedir -t pre-commit${DIR}

3. Add configs and hooks

Step into the repository you want to have the pre-commit hooks installed and run:

git initcat<<EOF > .pre-commit-config.yamlrepos:- repo: https://github.com/antonbabenko/pre-commit-terraform  rev: <VERSION> # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases  hooks:    - id: terraform_fmt    - id: terraform_docsEOF

If this repository was initialized locally viagit init orgit clonebeforeyou installed the pre-commit hook globally (step 2),you will need to run:

pre-commit install

4. Run

Execute this command to runpre-commit on all files in the repository (not only changed files):

pre-commit run -a

Or, using Docker (available tags):

Tip

This command uses your user id and group id for the docker container to use to access the local files. If the files are owned by another user, update theUSERID environment variable. SeeFile Permissions section for more information.

TAG=latestdocker run -e"USERID=$(id -u):$(id -g)" -v"$(pwd):/lint" -w"/lint""ghcr.io/antonbabenko/pre-commit-terraform:$TAG" run -a

Execute this command to list the versions of the tools in Docker:

TAG=latestdocker run --rm --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info

Available Hooks

There are severalpre-commit hooks to keep Terraform configurations (both*.tf and*.tfvars) and Terragrunt configurations (*.hcl) in a good shape:

Hook nameDescriptionDependencies
Install instructions here
checkov andterraform_checkovcheckov static analysis of terraform templates to spot potential security issues.Hook notescheckov
Ubuntu deps:python3,python3-pip
infracost_breakdownCheck how much your infra costs withinfracost.Hook notesinfracost,jq,Infracost API key
terraform_docsInserts input and output documentation intoREADME.md.Hook notesterraform-docs
terraform_docs_replaceRunsterraform-docs and pipes the output directly to README.md.DEPRECATED, see#248.Hook notespython3,terraform-docs
terraform_docs_without_
aggregate_type_defaults
Inserts input and output documentation intoREADME.md without aggregate type defaults. Hook notes same as forterraform_docsterraform-docs
terraform_fmtReformat all Terraform configuration files to a canonical format.Hook notes-
terraform_providers_lockUpdates provider signatures independency lock files.Hook notes-
terraform_tflintValidates all Terraform configuration files withTFLint.Available TFLint rules.Hook notes.tflint
terraform_tfsecTFSec static analysis of terraform templates to spot potential security issues.DEPRECATED, useterraform_trivy.Hook notestfsec
terraform_trivyTrivy static analysis of terraform templates to spot potential security issues.Hook notestrivy
terraform_validateValidates all Terraform configuration files.Hook notesjq, only for--retry-once-with-cleanup flag
terragrunt_fmtReformat allTerragrunt configuration files (*.hcl) to a canonical format.terragrunt
terragrunt_validateValidates allTerragrunt configuration files (*.hcl)terragrunt
terragrunt_validate_inputsValidatesTerragrunt unused and undefined inputs (*.hcl)
terragrunt_providers_lockGenerates.terraform.lock.hcl files usingTerragrunt.terragrunt
terraform_wrapper_module_for_eachGenerates Terraform wrappers withfor_each in module.Hook noteshcledit
terrascanterrascan Detect compliance and security violations.Hook notesterrascan
tfupdatetfupdate Update version constraints of Terraform core, providers, and modules.Hook notestfupdate

Check thesource file to know arguments used for each hook.

Hooks usage notes and examples

Known limitations

Terraform operates on a per-dir basis, whilepre-commit framework only supports files and files that exist. This means if you only remove the TF-related file without any other changes in the same dir, checks will be skipped. Example and detailshere.

All hooks: Usage of environment variables in--args

All, except deprecated hooks:checkov,terraform_docs_replace

You can use environment variables for the--args section.

Important

Youmust use the${ENV_VAR} definition,$ENV_VAR will not expand.

Config example:

-id:terraform_tflintargs:  ---args=--config=${CONFIG_NAME}.${CONFIG_EXT}  ---args=--call-module-type="all"

If for config above set upexport CONFIG_NAME=.tflint; export CONFIG_EXT=hcl beforepre-commit run, args will be expanded to--config=.tflint.hcl --call-module-type="all".

All hooks: Usage of__GIT_WORKING_DIR__ placeholder in--args

All, except deprecated hooks:checkov,terraform_docs_replace

You can use__GIT_WORKING_DIR__ placeholder in--args. It will be replacedby the Git working directory (repo root) at run time.

For instance, if you have multiple directories and want to runterraform_tflint in all of them while sharing a single config file — use the__GIT_WORKING_DIR__ placeholder in the file path. For example:

-id:terraform_tflintargs:    ---args=--config=__GIT_WORKING_DIR__/.tflint.hcl

All hooks: Set env vars inside hook at runtime

All, except deprecated hooks:checkov,terraform_docs_replace

You can specify environment variables that will be passed to the hook at runtime.

Important

Variable values are exportedverbatim:

  • No interpolation or expansion are applied
  • The enclosing double quotes are removed if they are provided

Config example:

-id:terraform_validateargs:    ---env-vars=AWS_DEFAULT_REGION="us-west-2"    ---env-vars=AWS_PROFILE="my-aws-cli-profile"

All hooks: Disable color output

All, except deprecated hooks:checkov,terraform_docs_replace

To disable color output for all hooks, setPRE_COMMIT_COLOR=never var. Eg:

PRE_COMMIT_COLOR=never pre-commit run

All hooks: Log levels

In case you need to debug hooks, you can setPCT_LOG=trace.

For example:

PCT_LOG=trace pre-commit run -a

Less verbose log levels will be implemented in#562.

Many hooks: Parallelism

All, except deprecated hooks:checkov,terraform_docs_replace and hooks which can't be paralleled this way:infracost_breakdown,terraform_wrapper_module_for_each.
Also, there's a chance that parallelism have no effect onterragrunt_fmt andterragrunt_validate hooks

By default, parallelism is set tonumber of logical CPUs - 1.
If you'd like to disable parallelism, set it to1

-id:terragrunt_validateargs:    ---hook-config=--parallelism-limit=1

In the same way you can set it to any positive integer.

If you'd like to set parallelism value relative to number of CPU logical cores - provide valid Bash arithmetic expression and useCPU as a reference to the number of CPU logical cores

-id:terraform_providers_lockargs:    ---hook-config=--parallelism-limit=CPU*4

Tip

Info useful for parallelism fine-tunning
Tests below were run on repo with 45 Terraform dirs on laptop with 16 CPUs, SSD and 1Gbit/s network. Laptop was slightly used in the process.

Observed results may vary greatly depending on your repo structure, machine characteristics and their usage.

If during fine-tuning you'll find that your results are very different from provided below and you think that this data could help someone else - feel free to send PR.

HookMost used resourceComparison of optimization results / Notes
terraform_checkovCPU heavy-
terraform_fmtCPU heavy-
terraform_providers_lock (3 platforms,
--mode=always-regenerate-lockfile)
Network & Disk heavydefaults (CPU-1) - 3m 39s;CPU*2 - 3m 19s;CPU*4 - 2m 56s
terraform_tflintCPU heavy-
terraform_tfsecCPU heavy-
terraform_trivyCPU moderatedefaults (CPU-1) - 32s;CPU*2 - 30s;CPU*4 - 31s
terraform_validate (t validate only)CPU heavy-
terraform_validate (t init + t validate)Network & Disk heavy, CPU moderatedefaults (CPU-1) - 1m 30s;CPU*2 - 1m 25s;CPU*4 - 1m 41s
terragrunt_fmtCPU heavyN/A? need more info from TG users
terragrunt_validateCPU heavyN/A? need more info from TG users
terrascanCPU moderate-heavydefaults (CPU-1) - 8s;CPU*2 - 6s
tfupdateDisk/Network?too quick in any settings. More info needed
args:  ---hook-config=--parallelism-ci-cpu-cores=N

If you don't see code above in yourpre-commit-config.yaml or logs - you don't need it.
--parallelism-ci-cpu-cores used only in edge cases and is ignored in other situations. Check out its usage inhooks/_common.sh

checkov (deprecated) and terraform_checkov

checkov hook is deprecated, please useterraform_checkov.

Note thatterraform_checkov runs recursively during-d . usage. That means, for example, if you change.tf file in repo root, all existing.tf files in the repo will be checked.

You can specify custom arguments. E.g.:

-id:terraform_checkovargs:    ---args=--quiet    ---args=--skip-check CKV2_AWS_8

Check all available argumentshere.

For deprecated hook you need to specify each argument separately:

-id:checkovargs:["-d", ".","--skip-check", "CKV2_AWS_8",]

infracost_breakdown

infracost_breakdown executesinfracost breakdown command and compare the estimated costs with those specified in the hook-config.infracost breakdown parses Terraform HCL code, and calls Infracost Cloud Pricing API (remote version orself-hosted version).

Unlike most other hooks, this hook triggers once if there are any changed files in the repository.

  1. infracost_breakdown supports allinfracost breakdown arguments (runinfracost breakdown --help to see them). The following example only shows costs:

    -id:infracost_breakdownargs:    ---args=--path=./env/devverbose:true# Always show costs
    Output
    Runningin"env/dev"Summary: {"unsupportedResourceCounts": {"aws_sns_topic_subscription": 1  }}Total Monthly Cost:        86.83 USDTotal Monthly Cost (diff): 86.83 USD
  2. Note that spaces are not allowed in--args, so you need to split it, like this:

    -id:infracost_breakdownargs:    ---args=--path=./env/dev    ---args=--terraform-var-file="terraform.tfvars"    ---args=--terraform-var-file="../terraform.tfvars"
  3. (Optionally) Definecost constraints the hook should evaluate successfully in order to pass:

    -id:infracost_breakdownargs:    ---args=--path=./env/dev    ---hook-config='.totalHourlyCost|tonumber > 0.1'    ---hook-config='.totalHourlyCost|tonumber > 1'    ---hook-config='.projects[].diff.totalMonthlyCost|tonumber != 10000'    ---hook-config='.currency == "USD"'
    Output
    Runningin"env/dev"Passed: .totalHourlyCost|tonumber> 0.1         0.11894520547945205>  0.1Failed: .totalHourlyCost|tonumber> 1           0.11894520547945205>  1Passed: .projects[].diff.totalMonthlyCost|tonumber!=10000              86.83!= 10000Passed: .currency =="USD""USD" =="USD"Summary: {"unsupportedResourceCounts": {"aws_sns_topic_subscription": 1  }}Total Monthly Cost:        86.83 USDTotal Monthly Cost (diff): 86.83 USD
    • Only one path per one hook (- id: infracost_breakdown) is allowed.
    • Setverbose: true to see cost even when the checks are passed.
    • Hook usesjq to process the cost estimation report returned byinfracost breakdown command
    • Expressions defined as--hook-config argument should be in a jq-compatible format (e.g..totalHourlyCost,.totalMonthlyCost)To study json output produced byinfracost, run the commandinfracost breakdown -p PATH_TO_TF_DIR --format json, and explore it onjqplay.org.
    • Supported comparison operators:<,<=,==,!=,>=,>.
    • Most useful paths and checks:
      • .totalHourlyCost (same as.projects[].breakdown.totalHourlyCost) - show total hourly infra cost
      • .totalMonthlyCost (same as.projects[].breakdown.totalMonthlyCost) - show total monthly infra cost
      • .projects[].diff.totalHourlyCost - show the difference in hourly cost for the existing infra and tf plan
      • .projects[].diff.totalMonthlyCost - show the difference in monthly cost for the existing infra and tf plan
      • .diffTotalHourlyCost (for Infracost version 0.9.12 or newer) or[.projects[].diff.totalMonthlyCost | select (.!=null) | tonumber] | add (for Infracost older than 0.9.12)
  4. Docker usage. Indocker build ordocker run command:

    • You need to provideInfracost API key via-e INFRACOST_API_KEY=<your token>. By default, it is saved in~/.config/infracost/credentials.yml
    • Set-e INFRACOST_SKIP_UPDATE_CHECK=true toskip the Infracost update check if you use this hook as part of your CI/CD pipeline.

terraform_docs

  1. terraform_docs andterraform_docs_without_aggregate_type_defaults will insert/update documentation generated byterraform-docs framed by markers:

    <!-- BEGIN_TF_DOCS --><!-- END_TF_DOCS -->

    if they are present inREADME.md.

  2. It is possible to pass additional arguments to shell scripts when usingterraform_docs andterraform_docs_without_aggregate_type_defaults.

  3. It is possible to automatically:

    • create a documentation file

    • extend existing documentation file by appending markers to the end of the file (see item 1 above)

    • use different filename for the documentation (default isREADME.md)

    • use the same insertion markers asterraform-docs. It's default starting fromv1.93.
      To migrate everything toterraform-docs insertion markers, run in repo root:

      sed --version&> /dev/null&& SED_CMD=(sed -i)|| SED_CMD=(sed -i'')grep -rl --null'BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK'.| xargs -0"${SED_CMD[@]}" -e's/BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK/BEGIN_TF_DOCS/'grep -rl --null'END OF PRE-COMMIT-TERRAFORM DOCS HOOK'.| xargs -0"${SED_CMD[@]}" -e's/END OF PRE-COMMIT-TERRAFORM DOCS HOOK/END_TF_DOCS/'
    -id:terraform_docsargs:    ---hook-config=--path-to-file=README.md# Valid UNIX path. I.e. ../TFDOC.md or docs/README.md etc.    ---hook-config=--add-to-existing-file=true# Boolean. true or false    ---hook-config=--create-file-if-not-exist=true# Boolean. true or false    ---hook-config=--use-standard-markers=true# Boolean. Defaults to true (v1.93+), false (<v1.93). Set to true for compatibility with terraform-docs# The following two options "--custom-marker-begin" and "--custom-marker-end" are ignored if "--use-standard-markers" is set to false    ---hook-config=--custom-marker-begin=<!-- BEGIN_TF_DOCS --># String.# Set to use custom marker which helps you with using other formats like asciidoc.# For Asciidoc this could be "--hook-config=--custom-marker-begin=// BEGIN_TF_DOCS"    ---hook-config=--custom-marker-end=<!-- END_TF_DOCS --># String.# Set to use custom marker which helps you with using other formats like asciidoc.# For Asciidoc this could be "--hook-config=--custom-marker-end=// END_TF_DOCS"    ---hook-config=--custom-doc-header="# "# String. Defaults to "# "# Set to use custom marker which helps you with using other formats like asciidoc.# For Asciidoc this could be "--hook-config=--custom-marker-end=\= "
  4. If you want to use a terraform-docs config file, you must supply the path to the file, relative to the git repo root path:

    -id:terraform_docsargs:    ---args=--config=.terraform-docs.yml

    Warning
    Avoid userecursive.enabled: true in config file, that can cause unexpected behavior.

  5. You can provideany configuration available interraform-docs as an argument toterraform_docs hook:

    -id:terraform_docsargs:    ---args=--output-mode=replace
  6. If you need some exotic settings, it can be done too. I.e. this one generates HCL files:

    -id:terraform_docsargs:    -tfvars hcl --output-file terraform.tfvars.model .

terraform_docs_replace (deprecated)

DEPRECATED. Will be merged interraform_docs.

terraform_docs_replace replaces the entireREADME.md rather than doing string replacement between markers. Put your additional documentation at the top of yourmain.tf for it to be pulled in.

To replicate functionality interraform_docs hook:

  1. Create.terraform-docs.yml in the repo root with the following content:

    formatter:"markdown"output:file:"README.md"mode:replacetemplate:|-    {{/** End of file fixer */}}
  2. Replaceterraform_docs_replace hook config in.pre-commit-config.yaml with:

    -id:terraform_docsargs:    ---args=--config=.terraform-docs.yml

terraform_fmt

terraform_fmt supports custom arguments so you can passsupported flags. Eg:

 -id:terraform_fmtargs:     ---args=-no-color     ---args=-diff     ---args=-write=false

terraform_providers_lock

Note

The hook requires Terraform 0.14 or later.

Note

The hook can invoketerraform providers lock that can be really slow and requires fetching metadata from remote Terraform registries - not all of that metadata is currently being cached by Terraform.

Note

Read this if you used this hook before v1.80.0 | Planned breaking changes in v2.0
We introduced `--mode` flag for this hook. If you'd like to continue using this hook as before, please:
  • Specify--hook-config=--mode=always-regenerate-lockfile inargs:
  • Beforeterraform_providers_lock, addterraform_validate hook with--hook-config=--retry-once-with-cleanup=true
  • Move--tf-init-args= toterraform_validate hook

In the end, you should get config like this:

-id:terraform_validateargs:    ---hook-config=--retry-once-with-cleanup=true# - --tf-init-args=-upgrade-id:terraform_providers_lockargs:  ---hook-config=--mode=always-regenerate-lockfile

Why? When v2.x will be introduced - the default mode will be changed, probably, toonly-check-is-current-lockfile-cross-platform.

You can check available modes for hook below.

  1. The hook can work in a few different modes:only-check-is-current-lockfile-cross-platform with and withoutterraform_validate hook andalways-regenerate-lockfile - only with terraform_validate hook.

    • only-check-is-current-lockfile-cross-platform without terraform_validate - only checks that lockfile has all required SHAs for all providers already added to lockfile.

      -id:terraform_providers_lockargs:  ---hook-config=--mode=only-check-is-current-lockfile-cross-platform
    • only-check-is-current-lockfile-cross-platform withterraform_validate hook - make up-to-date lockfile by adding/removing providers and only then check that lockfile has all required SHAs.

      ImportantNextterraform_validate flag requires additional dependency to be installed:jq. Also, it could run another slow and time consuming command -terraform init

      -id:terraform_validateargs:    ---hook-config=--retry-once-with-cleanup=true-id:terraform_providers_lockargs:  ---hook-config=--mode=only-check-is-current-lockfile-cross-platform
    • always-regenerate-lockfile only withterraform_validate hook - regenerate lockfile from scratch. Can be useful for upgrading providers in lockfile to latest versions

      -id:terraform_validateargs:    ---hook-config=--retry-once-with-cleanup=true    ---tf-init-args=-upgrade-id:terraform_providers_lockargs:  ---hook-config=--mode=always-regenerate-lockfile
  2. terraform_providers_lock supports custom arguments:

     -id:terraform_providers_lockargs:      ---args=-platform=windows_amd64      ---args=-platform=darwin_amd64
  3. It may happen that Terraform working directory (.terraform) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can find and delete all.terraform directories in your repository:

    echo"function rm_terraform {    find . \( -iname".terraform*" ! -iname".terraform-docs*" \) -print0 | xargs -0 rm -r}">>~/.bashrc# Reload shell and use `rm_terraform` command in the repo root

    terraform_providers_lock hook will try to reinitialize directories before running theterraform providers lock command.

  4. terraform_providers_lock support passing custom arguments to itsterraform init:

    Warning
    DEPRECATION NOTICE: This is available only inno-mode mode, which will be removed in v2.0. Please provide this keys toterraform_validate hook, which, to take effect, should be called beforeterraform_providers_lock

    -id:terraform_providers_lockargs:    ---tf-init-args=-upgrade

terraform_tflint

  1. terraform_tflint supports custom arguments so you can enable module inspection, enable / disable rules, etc.

    Example:

    -id:terraform_tflintargs:    ---args=--module    ---args=--enable-rule=terraform_documented_variables
  2. By default, pre-commit-terraform performs directory switching into the terraform modules for you. If you want to delegate the directory changing to the binary - this will allow tflint to determine the full paths for error/warning messages, rather than just module relative paths.Note: this requirestflint>=0.44.0. For example:

    -id:terraform_tflintargs:    ---hook-config=--delegate-chdir

terraform_tfsec (deprecated)

DEPRECATED.tfsec was replaced by trivy, so please useterraform_trivy.

  1. terraform_tfsec will consume modified files that pre-commitpasses to it, so you can perform whitelisting of directoriesor files to run against viafilespre-commit flag

    Example:

    -id:terraform_tfsecfiles:^prd-infra/

    The above will tell pre-commit to pass down files from theprd-infra/ folderonly such that the underlyingtfsec tool can run against changed files in thisdirectory, ignoring any other folders at the root level

  2. To ignore specific warnings, follow the convention from thedocumentation.

    Example:

    resource"aws_security_group_rule""my-rule" {type="ingress"cidr_blocks=["0.0.0.0/0"]#tfsec:ignore:AWS006}
  3. terraform_tfsec supports custom arguments, so you can pass supported--no-color or--format (output),-e (exclude checks) flags:

     -id:terraform_tfsecargs:     ->       --args=--format json       --no-color       -e aws-s3-enable-bucket-logging,aws-s3-specify-public-access-block

terraform_trivy

  1. terraform_trivy will consume modified files that pre-commitpasses to it, so you can perform whitelisting of directoriesor files to run against viafilespre-commit flag

    Example:

    -id:terraform_trivyfiles:^prd-infra/

    The above will tell pre-commit to pass down files from theprd-infra/ folderonly such that the underlyingtrivy tool can run against changed files in thisdirectory, ignoring any other folders at the root level

  2. To ignore specific warnings, follow the convention from thedocumentation.

    Example:

    #trivy:ignore:AVD-AWS-0107#trivy:ignore:AVD-AWS-0124resource"aws_security_group_rule""my-rule" {type="ingress"cidr_blocks=["0.0.0.0/0"]}
  3. terraform_trivy supports custom arguments, so you can pass supported--format (output),--skip-dirs (exclude directories) and other flags:

     -id:terraform_trivyargs:     ---args=--format=json     ---args=--skip-dirs="**/.terraform"

terraform_validate

Important

If you useTF_PLUGIN_CACHE_DIR, we recommend enabling--hook-config=--retry-once-with-cleanup=true or disabling parallelism (--hook-config=--parallelism-limit=1) to avoidrace conditions whenterraform init writes to it.

  1. terraform_validate supports custom arguments so you can pass supported-no-color or-json flags:

     -id:terraform_validateargs:     ---args=-json     ---args=-no-color
  2. terraform_validate also supports passing custom arguments to itsterraform init:

    -id:terraform_validateargs:    ---tf-init-args=-upgrade    ---tf-init-args=-lockfile=readonly
  3. It may happen that Terraform working directory (.terraform) already exists but not in the best condition (eg, not initialized modules, wrong version of Terraform, etc.). To solve this problem, you can delete broken.terraform directories in your repository:

    Option 1

    -id:terraform_validateargs:    ---hook-config=--retry-once-with-cleanup=true# Boolean. true or false

    Important
    The flag requires additional dependency to be installed:jq.

    Note
    Reinit can be very slow and require downloading data from remote Terraform registries, and not all of that downloaded data or meta-data is currently being cached by Terraform.

    When--retry-once-with-cleanup=true, in each failed directory the cached modules and providers from the.terraform directory will be deleted, before retrying once more. To avoid unnecessary deletion of this directory, the cleanup and retry will only happen if Terraform produces any of the following error messages:

    • "Missing or corrupted provider plugins"
    • "Module source has changed"
    • "Module version requirements have changed"
    • "Module not installed"
    • "Could not load plugin"

    Warning
    When using--retry-once-with-cleanup=true, problematic.terraform/modules/ and.terraform/providers/ directories will be recursively deleted without prompting for consent. Other files and directories will not be affected, such as the.terraform/environment file.

    Option 2

    An alternative solution is to find and delete all.terraform directories in your repository:

    echo"function rm_terraform {    find . \( -iname".terraform*" ! -iname".terraform-docs*" \) -print0 | xargs -0 rm -r}">>~/.bashrc# Reload shell and use `rm_terraform` command in the repo root

    terraform_validate hook will try to reinitialize them before running theterraform validate command.

    Caution
    If you use Terraform workspaces, DO NOT use this option (details). Consider the first option, or wait forforce-init option implementation.

  4. terraform_validate in a repo with Terraform module, written using Terraform 0.15+ and which uses providerconfiguration_aliases (Provider Aliases Within Modules), errors out.

    When running the hook against Terraform code where you have providerconfiguration_aliases defined in arequired_providers configuration block, terraform will throw an error like:

    Error: Provider configuration not presentTo work with<resource> its original provider configuration at provider["registry.terraform.io/hashicorp/aws"].<provider_alias> is required, but it has been removed. This occurs when a provider configuration is removed whileobjects created by that provider still exist in the state. Re-add the provider configuration to destroy<resource>, after which you can remove the provider configuration again.

    This is aknown issue with Terraform and how providers are initialized in Terraform 0.15 and later. To work around this you can add anexclude parameter to the configuration ofterraform_validate hook like this:

    -id:terraform_validateexclude:'^[^/]+$'

    This will exclude the root directory from being processed by this hook. Then add a subdirectory like "examples" or "tests" and put an example implementation in place that defines the providers with the proper aliases, and this will give you validation of your module through the example. If instead you are using this with multiple modules in one repository you'll want to set the path prefix in the regular expression, such asexclude: modules/offendingmodule/[^/]+$.

    Alternately, you can useterraform-config-inspect and use a variant ofthis script to generate a providers file at runtime:

    terraform-config-inspect --json.| jq -r'  [.required_providers[].aliases]  | flatten  | del(.[] | select(. == null))  | reduce .[] as $entry (    {};    .provider[$entry.name] //= [] | .provider[$entry.name] += [{"alias": $entry.alias}]  )'| tee aliased-providers.tf.json

    Save it as.generate-providers.sh in the root of your repository and add apre-commit hook to run it before all other hooks, like so:

    -repos:  -repo:localhooks:      -id:generate-terraform-providersname:generate-terraform-providersrequire_serial:trueentry:.generate-providers.shlanguage:scriptfiles:\.tf(vars)?$pass_filenames:false  -repo:https://github.com/pre-commit/pre-commit-hooks

    Tip
    The latter method will leave an "aliased-providers.tf.json" file in your repo. You will either want to automate a way to clean this up or add it to your.gitignore or both.

terraform_wrapper_module_for_each

terraform_wrapper_module_for_each generates module wrappers for Terraform modules (useful for Terragrunt wherefor_each is not supported). When using this hook without arguments it will create wrappers for the root module and all modules available in "modules" directory.

You may want to customize some of the options:

  1. --module-dir=... - Specify a single directory to process. Values: "." (means just root module), "modules/iam-user" (a single module), or empty (means include all submodules found in "modules/*").
  2. --module-repo-org=... - Module repository organization (e.g. "terraform-aws-modules").
  3. --module-repo-shortname=... - Short name of the repository (e.g. "s3-bucket").
  4. --module-repo-provider=... - Name of the repository provider (e.g. "aws" or "google").

Sample configuration:

-id:terraform_wrapper_module_for_eachargs:    ---args=--module-dir=.# Process only root module    ---args=--dry-run# No files will be created/updated    ---args=--verbose# Verbose output

If you use hook inside Docker:
Theterraform_wrapper_module_for_each hook attempts to determine the module's short name to be inserted into the generatedREADME.md files for thesource URLs. Since the container uses a bind mount at a static location, it can cause this short name to be incorrect.
If the generated name is incorrect, set them by providing themodule-repo-shortname option to the hook:

-id:terraform_wrapper_module_for_eachargs:    -'--args=--module-repo-shortname=ec2-instance'

terrascan

  1. terrascan supports custom arguments so you can pass supported flags like--non-recursive and--policy-type to disable recursive inspection and set the policy type respectively:

    -id:terrascanargs:    ---args=--non-recursive# avoids scan errors on subdirectories without Terraform config files    ---args=--policy-type=azure

    See theterrascan run -h command line help for available options.

  2. Use the--args=--verbose parameter to see the rule ID in the scanning output. Useful to skip validations.

  3. Use--skip-rules="ruleID1,ruleID2" parameter to skip one or more rules globally while scanning (e.g.:--args=--skip-rules="ruleID1,ruleID2").

  4. Use the syntax#ts:skip=RuleID optional_comment inside a resource to skip the rule for that resource.

tfupdate

  1. Out of the boxtfupdate will pin the terraform version:

    -id:tfupdatename:Autoupdate Terraform versions
  2. If you'd like to pin providers, etc., use custom arguments, i.eprovider=PROVIDER_NAME:

    -id:tfupdatename:Autoupdate AWS provider versionsargs:    ---args=provider aws# Will be pined to latest version-id:tfupdatename:Autoupdate Helm provider versionsargs:    ---args=provider helm    ---args=--version 2.5.0# Will be pined to specified version

Checktfupdate usage instructions for other available options and usage examples.
No need to pass--recursive . as it is added automatically.

terragrunt_providers_lock

Tip

Use this hook only in infrastructure repos managed solely byterragrunt and do not mix withterraform_providers_lock to avoid conflicts.

Warning

Hookmay be very slow, because terragrunt invokest init under the hood.

Hook produces same results asterraform_providers_lock, but for terragrunt root modules.

It invokesterragrunt providers lock under the hood and terragruntdoes its' own magic for handling lock files.

-id:terragrunt_providers_lockname:Terragrunt providers lockargs:    ---args=-platform=darwin_arm64    ---args=-platform=darwin_amd64    ---args=-platform=linux_amd64

terragrunt_validate_inputs

Validates Terragrunt unused and undefined inputs. This is useful for keepingconfigs clean when module versions change or if configs are copied.

See theTerragrunt docs for more details.

Example:

-id:terragrunt_validate_inputsname:Terragrunt validate inputsargs:# Optionally check for unused inputs    ---args=--terragrunt-strict-validate

Note

This hook requires authentication to a given account if defined by config to work properly. For example, if you use a third-party tool to store AWS credentials likeaws-vault you must be authenticated first.

See docs for theiam_role attribute and--terragrunt-iam-role flag for more.

Docker Usage

About Docker image security

Pre-built Docker images contain the latest versions of tools available at the time of their build and remain unchanged afterward. Tags should be immutable whenever possible, and it is highly recommended to pin them using hash sums for security and reproducibility.

This means that most Docker images will include known CVEs, and the longer an image exists, the more CVEs it may accumulate. This applies even to the latestvX.Y.Z tags.To address this, you can use thenightly tag, which rebuilds nightly with the latest versions of all dependencies and latestpre-commit-terraform hooks. However, using mutable tags introduces different security concerns.

Note: Currently, we DO NOT test third-party tools or their dependencies for security vulnerabilities, corruption, or injection (including obfuscated content). If you have ideas for introducing image scans or other security improvements, please open an issue or submit a PR. Some ideas are already tracked in#835.

From a security perspective, the best approach is to manage the Docker image yourself and update its dependencies as needed. This allows you to remove unnecessary dependencies, reducing the number of potential CVEs and improving overall security.

File Permissions

A mismatch between the Docker container's user and the local repository file ownership can cause permission issues in the repository wherepre-commit is run. The container runs as theroot user by default, and uses atools/entrypoint.sh script to assume a user ID and group ID if specified by the environment variableUSERID.

Therecommended command to run the Docker container is:

TAG=latestdocker run -e"USERID=$(id -u):$(id -g)" -v$(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a

which uses your current session's user ID and group ID to set the variable in the run command. Without this setting, you may find files and directories owned byroot in your local repository.

If the local repository is using a different user or group for permissions, you can modify theUSERID to the user ID and group ID needed.Do not use the username or groupname in the environment variable, as it has no meaning in the container. You can get the current directory's owner user ID and group ID from the 3rd (user) and 4th (group) columns inls output:

$ ls -aldn.drwxr-xr-x 9 1000 1000 4096 Sep  1 16:23.

Download Terraform modules from private GitHub repositories

If you use a private Git repository as your Terraform module source, you are required to authenticate to GitHub using aPersonal Access Token.

When running pre-commit on Docker, both locally or on CI, you need to configure the~/.netrc file, which contains login and initialization information used by the auto-login process.

This can be achieved by firstly creating the~/.netrc file including yourGITHUB_PAT andGITHUB_SERVER_HOSTNAME

# set GH values (replace with your own values)GITHUB_PAT=ghp_bl481aBlabl481aBlaGITHUB_SERVER_HOSTNAME=github.com# create .netrc fileecho -e"machine$GITHUB_SERVER_HOSTNAME\n\tlogin$GITHUB_PAT">>~/.netrc

The~/.netrc file will look similar to the following:

machine github.com  login ghp_bl481aBlabl481aBla

Tip

The value ofGITHUB_SERVER_HOSTNAME can also refer to a GitHub Enterprise server (i.e.github.my-enterprise.com).

Finally, you can executedocker run with an additional volume mount so that the~/.netrc is accessible within the container

# run pre-commit-terraform with docker# adding volume for .netrc file# .netrc needs to be in /root/ dirdocker run --rm -e"USERID=$(id -u):$(id -g)" -v~/.netrc:/root/.netrc -v$(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:latest run -a

GitHub Actions

You can use this hook in your GitHub Actions workflow together withpre-commit. To easy updependency management, you can use the manageddocker image within your workflow. Make sure to set theimage tag to the version you want to use.

In this repository's pre-commitworkflow file we run pre-commit without the container image.

Here's an example using the container image. It includes caching of pre-commit dependencies and utilizes the pre-commitcommand to run checks (Note: Fixes will not be automatically pushed back to your branch, even when possible.):

name:pre-commit-terraformon:pull_request:jobs:pre-commit:runs-on:ubuntu-latestcontainer:image:ghcr.io/antonbabenko/pre-commit-terraform:latest# latest used here for simplicity, not recommendeddefaults:run:shell:bashsteps:      -uses:actions/checkout@v4with:fetch-depth:0ref:${{ github.event.pull_request.head.sha }}      -run:|          git config --global --add safe.directory $GITHUB_WORKSPACE          git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*      -name:Get changed filesid:file_changesrun:|          export DIFF=$(git diff --name-only origin/${{ github.base_ref }} ${{ github.sha }})          echo "Diff between ${{ github.base_ref }} and ${{ github.sha }}"          echo "files=$( echo "$DIFF" | xargs echo )" >> $GITHUB_OUTPUT      -name:fix tar dependency in alpine container imagerun:|          apk --no-cache add tar          # check python modules installed versions          python -m pip freeze --local      -name:Cache pre-commit since we use pre-commit from containeruses:actions/cache@v4with:path:~/.cache/pre-commitkey:pre-commit-3|${{ hashFiles('.pre-commit-config.yaml') }}      -name:Execute pre-commitrun:|          pre-commit run --color=always --show-diff-on-failure --files ${{ steps.file_changes.outputs.files }}

Authors

This repository is managed byAnton Babenko with help from these awesome contributors:

ContributorsStar History Chart

License

MIT licensed. SeeLICENSE for full details.

Additional information for users from Russia and Belarus

About

pre-commit git hooks to take care of Terraform configurations 🇺🇦

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

[8]ページ先頭

©2009-2025 Movatter.jp