From 9a472b2d162a7149835d460f86d1070e65f7dd19 Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:20:36 -0500 Subject: [PATCH 1/7] Add `terraform_graph` hook --- .pre-commit-hooks.yaml | 8 ++++ hooks/terraform_graph.sh | 93 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100755 hooks/terraform_graph.sh 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/hooks/terraform_graph.sh b/hooks/terraform_graph.sh new file mode 100755 index 000000000..793f33c43 --- /dev/null +++ b/hooks/terraform_graph.sh @@ -0,0 +1,93 @@ +#!/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="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 + 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 +} + +####################################################################### +# Unique part of `common::per_dir_hook`. The function is executed one time +# in the root git repo +# 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 "$@" From 3527e5ec5b5b85b92ccd37a033bf734ec73ee81c Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:58:55 -0500 Subject: [PATCH 2/7] Update default file name --- hooks/terraform_graph.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh index 793f33c43..19582b729 100755 --- a/hooks/terraform_graph.sh +++ b/hooks/terraform_graph.sh @@ -44,7 +44,7 @@ function per_dir_hook_unique_part { fi # set file name passed from --hook-config - local text_file="graph.svg" + local text_file="tf-graph.svg" IFS=";" read -r -a configs <<< "${HOOK_CONFIG[*]}" for c in "${configs[@]}"; do IFS="=" read -r -a config <<< "$c" From 820cac96dc94f135c4b965c1ba16d4ac3c03de68 Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:59:09 -0500 Subject: [PATCH 3/7] Add docs --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 From 60eb114c9edeb288b4f36cf483df2e42c1307eb7 Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:59:07 -0500 Subject: [PATCH 4/7] Remove whitespace --- hooks/terraform_graph.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh index 19582b729..e69c983f7 100755 --- a/hooks/terraform_graph.sh +++ b/hooks/terraform_graph.sh @@ -64,7 +64,7 @@ function per_dir_hook_unique_part { # 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" From 5cf8772dba8e379f9fac84c370e5a15dddcad8c0 Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:06:50 -0500 Subject: [PATCH 5/7] Remove `run_hook_on_whole_repo` --- hooks/terraform_graph.sh | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh index e69c983f7..1eed3487a 100755 --- a/hooks/terraform_graph.sh +++ b/hooks/terraform_graph.sh @@ -71,23 +71,4 @@ function per_dir_hook_unique_part { return $exit_code } -####################################################################### -# Unique part of `common::per_dir_hook`. The function is executed one time -# in the root git repo -# 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 "$@" From b273235a36f2ea4cdc99d7432041ea31675581ad Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:57:21 -0500 Subject: [PATCH 6/7] Re-add `run_hook_on_whole_repo` --- hooks/terraform_graph.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh index 1eed3487a..40ca44b01 100755 --- a/hooks/terraform_graph.sh +++ b/hooks/terraform_graph.sh @@ -71,4 +71,20 @@ function per_dir_hook_unique_part { 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 "$@" From a4999f9d0188740ef95a8fe9c3ecc06c086ee49f Mon Sep 17 00:00:00 2001 From: Jordan Bradford <36420801+jrdnbradford@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:00:15 -0500 Subject: [PATCH 7/7] Troubleshooting blank svgs --- hooks/terraform_graph.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/hooks/terraform_graph.sh b/hooks/terraform_graph.sh index 40ca44b01..ab244257d 100755 --- a/hooks/terraform_graph.sh +++ b/hooks/terraform_graph.sh @@ -60,6 +60,7 @@ function per_dir_hook_unique_part { 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