From d4902313ce11cc12c738397463f307b830a9ba3e Mon Sep 17 00:00:00 2001 From: Maksym Vlasov Date: Tue, 5 Jul 2022 19:07:01 +0300 Subject: [PATCH] feat: Add support for set env vars inside hook runtime (#408) --- README.md | 33 ++++++----- hooks/_common.sh | 32 ++++++++++- hooks/infracost_breakdown.sh | 1 + hooks/terraform_checkov.sh | 1 + hooks/terraform_docs.sh | 1 + hooks/terraform_fmt.sh | 1 + hooks/terraform_providers_lock.sh | 1 + hooks/terraform_tflint.sh | 1 + hooks/terraform_tfsec.sh | 1 + hooks/terraform_validate.sh | 67 +--------------------- hooks/terraform_wrapper_module_for_each.sh | 1 + hooks/terragrunt_fmt.sh | 1 + hooks/terragrunt_validate.sh | 1 + hooks/terrascan.sh | 1 + hooks/tfupdate.sh | 1 + 15 files changed, 65 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 6037c82ce..e47dd774e 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ If you are using `pre-commit-terraform` already or want to support its developme * [Available Hooks](#available-hooks) * [Hooks usage notes and examples](#hooks-usage-notes-and-examples) * [All hooks: Usage of environment variables in `--args`](#all-hooks-usage-of-environment-variables-in---args) + * [All hooks: Set env vars inside hook at runtime](#all-hooks-set-env-vars-inside-hook-at-runtime) * [checkov (deprecated) and terraform_checkov](#checkov-deprecated-and-terraform_checkov) * [infracost_breakdown](#infracost_breakdown) * [terraform_docs](#terraform_docs) @@ -283,6 +284,22 @@ Config example: If for config above set up `export CONFIG_NAME=.tflint; export CONFIG_EXT=hcl` before `pre-commit run`, args will be expanded to `--config=.tflint.hcl --module`. +### 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. + +Config example: + +```yaml +- id: terraform_validate + args: + - --envs=AWS_DEFAULT_REGION="us-west-2" + - --envs=AWS_ACCESS_KEY_ID="anaccesskey" + - --envs=AWS_SECRET_ACCESS_KEY="asecretkey" +``` + ### checkov (deprecated) and terraform_checkov > `checkov` hook is deprecated, please use `terraform_checkov`. @@ -614,17 +631,7 @@ Example: - --args=-no-color ``` -2. `terraform_validate` also supports custom environment variables passed to the pre-commit runtime: - - ```yaml - - id: terraform_validate - args: - - --envs=AWS_DEFAULT_REGION="us-west-2" - - --envs=AWS_ACCESS_KEY_ID="anaccesskey" - - --envs=AWS_SECRET_ACCESS_KEY="asecretkey" - ``` - -3. `terraform_validate` also supports passing custom arguments to its `terraform init`: +2. `terraform_validate` also supports passing custom arguments to its `terraform init`: ```yaml - id: terraform_validate @@ -632,7 +639,7 @@ Example: - --tf-init-args=-lockfile=readonly ``` -4. 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: +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: ```bash echo " @@ -648,7 +655,7 @@ Example: **Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation. -5. `terraform_validate` in a repo with Terraform module, written using Terraform 0.15+ and which uses provider `configuration_aliases` ([Provider Aliases Within Modules](https://www.terraform.io/language/modules/develop/providers#provider-aliases-within-modules)), errors out. +4. `terraform_validate` in a repo with Terraform module, written using Terraform 0.15+ and which uses provider `configuration_aliases` ([Provider Aliases Within Modules](https://www.terraform.io/language/modules/develop/providers#provider-aliases-within-modules)), errors out. When running the hook against Terraform code where you have provider `configuration_aliases` defined in a `required_providers` configuration block, terraform will throw an error like: > diff --git a/hooks/_common.sh b/hooks/_common.sh index 1dcd82d61..77cf83257 100644 --- a/hooks/_common.sh +++ b/hooks/_common.sh @@ -26,6 +26,8 @@ function common::initialize { # ARGS (array) arguments that configure wrapped tool behavior # HOOK_CONFIG (array) arguments that configure hook behavior # TF_INIT_ARGS (array) arguments for `terraform init` command +# ENVS (array) environment variables will be available +# for all 3rd-party tools executed by a hook. # FILES (array) filenames to check # Arguments: # $@ (array) all specified in `hooks.[].args` in @@ -37,9 +39,11 @@ function common::parse_cmdline { ARGS=() HOOK_CONFIG=() FILES=() # Used inside `common::terraform_init` function TF_INIT_ARGS=() + # Used inside `common::export_provided_env_vars` function + ENVS=() local argv - argv=$(getopt -o a:,h:,i: --long args:,hook-config:,init-args:,tf-init-args: -- "$@") || return + argv=$(getopt -o a:,h:,i:,e: --long args:,hook-config:,init-args:,tf-init-args:,envs: -- "$@") || return eval "set -- $argv" for argv; do @@ -60,6 +64,11 @@ function common::parse_cmdline { TF_INIT_ARGS+=("$1") shift ;; + -e | --envs) + shift + ENVS+=("$1") + shift + ;; --) shift # shellcheck disable=SC2034 # Variable is used @@ -270,3 +279,24 @@ function common::terraform_init { return $exit_code } + +####################################################################### +# Export provided K/V as environment variables. +# Arguments: +# env_vars (array) environment variables will be available +# for all 3rd-party tools executed by a hook. +####################################################################### +function common::export_provided_env_vars { + local -a -r env_vars=("$@") + + local var + local var_name + local var_value + + for var in "${env_vars[@]}"; do + var_name="${var%%=*}" + var_value="${var#*=}" + # shellcheck disable=SC2086 + export $var_name="$var_value" + done +} diff --git a/hooks/infracost_breakdown.sh b/hooks/infracost_breakdown.sh index b5460205c..aecc4be0d 100755 --- a/hooks/infracost_breakdown.sh +++ b/hooks/infracost_breakdown.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive infracost_breakdown_ "${HOOK_CONFIG[*]}" "${ARGS[*]}" diff --git a/hooks/terraform_checkov.sh b/hooks/terraform_checkov.sh index 06013bf96..13008e8cf 100755 --- a/hooks/terraform_checkov.sh +++ b/hooks/terraform_checkov.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # Support for setting PATH to repo root. # shellcheck disable=SC2178 # It's the simplest syntax for that case diff --git a/hooks/terraform_docs.sh b/hooks/terraform_docs.sh index 63a65fd4b..5b43deb87 100755 --- a/hooks/terraform_docs.sh +++ b/hooks/terraform_docs.sh @@ -13,6 +13,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # Support for setting relative PATH to .terraform-docs.yml config. # shellcheck disable=SC2178 # It's the simplest syntax for that case diff --git a/hooks/terraform_fmt.sh b/hooks/terraform_fmt.sh index 241a1aeef..b89266cde 100755 --- a/hooks/terraform_fmt.sh +++ b/hooks/terraform_fmt.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh index 1bdf8597a..4edeeabf8 100755 --- a/hooks/terraform_providers_lock.sh +++ b/hooks/terraform_providers_lock.sh @@ -11,6 +11,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh index 5795a318f..8d5707e97 100755 --- a/hooks/terraform_tflint.sh +++ b/hooks/terraform_tflint.sh @@ -11,6 +11,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # Support for setting PATH to repo root. # shellcheck disable=SC2178 # It's the simplest syntax for that case diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh index 4b5d8f06d..fdbb436f8 100755 --- a/hooks/terraform_tfsec.sh +++ b/hooks/terraform_tfsec.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # Support for setting PATH to repo root. # shellcheck disable=SC2178 # It's the simplest syntax for that case diff --git a/hooks/terraform_validate.sh b/hooks/terraform_validate.sh index 8e1eff08b..be5f1e067 100755 --- a/hooks/terraform_validate.sh +++ b/hooks/terraform_validate.sh @@ -12,73 +12,13 @@ export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-us-east-1} function main { common::initialize "$SCRIPT_DIR" - parse_cmdline_ "$@" + common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars - - # Export provided env var K/V pairs to environment - local var var_name var_value - for var in "${ENVS[@]}"; do - var_name="${var%%=*}" - var_value="${var#*=}" - # shellcheck disable=SC2086 - export $var_name="$var_value" - done - # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" } -####################################################################### -# Parse args and filenames passed to script and populate respective -# global variables with appropriate values -# Globals (init and populate): -# ARGS (array) arguments that configure wrapped tool behavior -# HOOK_CONFIG (array) arguments that configure hook behavior -# TF_INIT_ARGS (array) arguments to `terraform init` command -# ENVS (array) environment variables that will be used with -# `terraform` commands -# FILES (array) filenames to check -# Arguments: -# $@ (array) all specified in `hooks.[].args` in -# `.pre-commit-config.yaml` and filenames. -####################################################################### -function parse_cmdline_ { - declare argv - argv=$(getopt -o e:i:a:h: --long envs:,tf-init-args:,init-args:,args: -- "$@") || return - eval "set -- $argv" - - for argv; do - case $argv in - -a | --args) - shift - ARGS+=("$1") - shift - ;; - -h | --hook-config) - shift - HOOK_CONFIG+=("$1;") - shift - ;; - # TODO: Planned breaking change: remove `--init-args` as not self-descriptive - -i | --init-args | --tf-init-args) - shift - TF_INIT_ARGS+=("$1") - shift - ;; - -e | --envs) - shift - ENVS+=("$1") - shift - ;; - --) - shift - FILES=("$@") - break - ;; - esac - done -} - ####################################################################### # Unique part of `common::per_dir_hook`. The function is executed in loop # on each provided dir path. Run wrapped tool with specified arguments @@ -120,7 +60,4 @@ function per_dir_hook_unique_part { return $exit_code } -# global arrays -declare -a ENVS - [ "${BASH_SOURCE[0]}" != "$0" ] || main "$@" diff --git a/hooks/terraform_wrapper_module_for_each.sh b/hooks/terraform_wrapper_module_for_each.sh index 4e1718427..905fa8de9 100755 --- a/hooks/terraform_wrapper_module_for_each.sh +++ b/hooks/terraform_wrapper_module_for_each.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars check_dependencies diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh index f6233628c..5ef51570d 100755 --- a/hooks/terragrunt_fmt.sh +++ b/hooks/terragrunt_fmt.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh index e7ffee46d..ae9be6b17 100755 --- a/hooks/terragrunt_validate.sh +++ b/hooks/terragrunt_validate.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh index 16f6c750e..ca8d87452 100755 --- a/hooks/terrascan.sh +++ b/hooks/terrascan.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}" diff --git a/hooks/tfupdate.sh b/hooks/tfupdate.sh index 309ee877c..f720fbb3d 100755 --- a/hooks/tfupdate.sh +++ b/hooks/tfupdate.sh @@ -10,6 +10,7 @@ readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" function main { common::initialize "$SCRIPT_DIR" common::parse_cmdline "$@" + common::export_provided_env_vars "${ENVS[@]}" common::parse_and_export_env_vars # shellcheck disable=SC2153 # False positive common::per_dir_hook "${ARGS[*]}" "$HOOK_ID" "${FILES[@]}"