diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 7b5152ba4..abb7c1d2f 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -143,3 +143,11 @@ - --args=terraform files: \.tf$ require_serial: true + +- id: terraform_graph + name: Terraform graph + description: Outputs Terraform dependency graphs in dot format. + entry: hooks/terraform_graph.sh + language: script + files: \.tf$ + exclude: \.terraform\/.*$ diff --git a/README.md b/README.md index 4f0eee1d4..37eab1803 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ If you are using `pre-commit-terraform` already or want to support its developme * [`infracost`](https://github.com/infracost/infracost) required for `infracost_breakdown` hook. * [`jq`](https://github.com/stedolan/jq) required for `terraform_validate` with `--retry-once-with-cleanup` flag, and for `infracost_breakdown` hook. * [`tfupdate`](https://github.com/minamijoyo/tfupdate) required for `tfupdate` hook. +* [`graphviz`](https://www.graphviz.org/download) required for `terraform_graph` hook. * [`hcledit`](https://github.com/minamijoyo/hcledit) required for `terraform_wrapper_module_for_each` hook.
Docker
@@ -138,7 +139,7 @@ Set `-e PRE_COMMIT_COLOR=never` to disable the color output in `pre-commit`.
MacOS
```bash -brew install pre-commit terraform-docs tflint tfsec checkov terrascan infracost tfupdate minamijoyo/hcledit/hcledit jq +brew install pre-commit terraform-docs tflint tfsec checkov terrascan infracost tfupdate minamijoyo/hcledit/hcledit jq graphviz ```
@@ -161,6 +162,7 @@ sudo 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 register curl -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/ +sudo apt install -y graphviz ```
@@ -182,6 +184,7 @@ sudo 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 register curl -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/ +sudo apt install -y graphviz ``` @@ -280,6 +283,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform | `terraform_wrapper_module_for_each` | Generates Terraform wrappers with `for_each` in module. [Hook notes](#terraform_wrapper_module_for_each) | `hcledit` | | `terrascan` | [terrascan](https://github.com/tenable/terrascan) Detect compliance and security violations. [Hook notes](#terrascan) | `terrascan` | | `tfupdate` | [tfupdate](https://github.com/minamijoyo/tfupdate) Update version constraints of Terraform core, providers, and modules. [Hook notes](#tfupdate) | `tfupdate` | +| `terraform_graph` | Generates charts using [terraform graph](https://developer.hashicorp.com/terraform/cli/commands/graph) and [GraphViz](https://www.graphviz.org/). [Hook notes](#terraform_graph) | `dot` from [GraphViz](https://www.graphviz.org/download) | Check the [source file](https://github.com/antonbabenko/pre-commit-terraform/blob/master/.pre-commit-hooks.yaml) to know arguments used for each hook. @@ -926,6 +930,16 @@ If the generated name is incorrect, set them by providing the `module-repo-short Check [`tfupdate` usage instructions](https://github.com/minamijoyo/tfupdate#usage) for other available options and usage examples. No need to pass `--recursive .` as it is added automatically. +### terraform_graph +`terraform_graph` utilizes Terraform's built-in [`graph` command](https://developer.hashicorp.com/terraform/cli/commands/graph) and `dot` from [GraphViz](https://www.graphviz.org/) to generate charts of your configuration. + +```yaml +- id: terraform_graph + args: + - --args=-type=apply # Default is `plan` + - --hook-config=--path-to-file=test-config.svg # Default is tf-graph.svg +``` + ## Docker Usage ### File Permissions diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh new file mode 100755 index 000000000..ab244257d --- /dev/null +++ b/hooks/terraform_graph.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +set -eo pipefail + +# globals variables +# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +function main { + common::initialize "$SCRIPT_DIR" + common::parse_cmdline "$@" + common::export_provided_env_vars "${ENV_VARS[@]}" + common::parse_and_export_env_vars + + # shellcheck disable=SC2153 # False positive + common::per_dir_hook "$HOOK_ID" "${#ARGS[@]}" "${ARGS[@]}" "${FILES[@]}" +} + +####################################################################### +# Unique part of `common::per_dir_hook`. The function is executed in loop +# on each provided dir path. Run wrapped tool with specified arguments +# Arguments: +# dir_path (string) PATH to dir relative to git repo root. +# Can be used in error logging +# change_dir_in_unique_part (string/false) Modifier which creates +# possibilities to use non-common chdir strategies. +# Availability depends on hook. +# args (array) arguments that configure wrapped tool behavior +# Outputs: +# If failed - print out hook checks status +####################################################################### +function per_dir_hook_unique_part { + # shellcheck disable=SC2034 # Unused var. + local -r dir_path="$1" + # shellcheck disable=SC2034 # Unused var. + local -r change_dir_in_unique_part="$2" + shift 2 + local -a -r args=("$@") + + if [[ ! $(command -v dot) ]]; then + echo "ERROR: dot is required by terraform_graph pre-commit hook but is not installed or in the system's PATH." + exit 1 + fi + + # set file name passed from --hook-config + local text_file="tf-graph.svg" + IFS=";" read -r -a configs <<< "${HOOK_CONFIG[*]}" + for c in "${configs[@]}"; do + IFS="=" read -r -a config <<< "$c" + key=${config[0]} + value=${config[1]} + + case $key in + --path-to-file) + text_file=$value + ;; + esac + done + + temp_file=$(mktemp) + # pass the arguments to hook + echo "${args[@]}" >> per_dir_hook_unique_part + terraform graph "${args[@]}" | dot -Tsvg > "$temp_file" + + # check if files are the same + cmp -s "$temp_file" "$text_file" + + # return exit code to common::per_dir_hook + local exit_code=$? + mv "$temp_file" "$text_file" + return $exit_code +} + +# Arguments: +# args (array) arguments that configure wrapped tool behavior +####################################################################### +function run_hook_on_whole_repo { + local -a -r args=("$@") + local text_file="graph.svg" + + # pass the arguments to hook + echo "${args[@]}" >> run_hook_on_whole_repo + terraform graph "$(pwd)" "${args[@]}" | dot -Tsvg > "$text_file" + + # return exit code to common::per_dir_hook + local exit_code=$? + return $exit_code +} + +[ "${BASH_SOURCE[0]}" != "$0" ] || main "$@"