diff --git a/.github/.container-structure-test-config.yaml b/.github/.container-structure-test-config.yaml
index 13880816f..a860febe4 100644
--- a/.github/.container-structure-test-config.yaml
+++ b/.github/.container-structure-test-config.yaml
@@ -55,6 +55,11 @@ commandTests:
args: [ "--version" ]
expectedOutput: [ "([0-9]+\\.){2}[0-9]+\\n$" ]
+ - name: "hcledit"
+ command: "hcledit"
+ args: [ "version" ]
+ expectedOutput: [ "([0-9]+\\.){2}[0-9]+\\n$" ]
+
fileExistenceTests:
- name: 'terrascan init'
path: '/root/.terrascan/pkg/policies/opa/rego/github/github_repository/privateRepoEnabled.rego'
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index dbee5e2a7..4f554ea4d 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -113,6 +113,17 @@
exclude: \.terraform\/.*$
require_serial: true
+- id: terraform_wrapper_module_for_each
+ name: Terraform wrapper with for_each in module
+ description: Generate Terraform wrappers with for_each in module.
+ entry: hooks/terraform_wrapper_module_for_each.sh
+ language: script
+ pass_filenames: false
+ always_run: false
+ require_serial: true
+ files: \.tf$
+ exclude: \.terraform\/.*$
+
- id: terrascan
name: terrascan
description: Runs terrascan on Terraform templates.
diff --git a/Dockerfile b/Dockerfile
index 28e1dedeb..1d5fde7d4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -35,6 +35,7 @@ ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false}
ARG TFLINT_VERSION=${TFLINT_VERSION:-false}
ARG TFSEC_VERSION=${TFSEC_VERSION:-false}
ARG TFUPDATE_VERSION=${TFUPDATE_VERSION:-false}
+ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false}
# Tricky thing to install all tools by set only one arg.
@@ -49,7 +50,8 @@ RUN if [ "$INSTALL_ALL" != "false" ]; then \
echo "export TERRASCAN_VERSION=latest" >> /.env && \
echo "export TFLINT_VERSION=latest" >> /.env && \
echo "export TFSEC_VERSION=latest" >> /.env && \
- echo "export TFUPDATE_VERSION=latest" >> /.env \
+ echo "export TFUPDATE_VERSION=latest" >> /.env && \
+ echo "export HCLEDIT_VERSION=latest" >> /.env \
; else \
touch /.env \
; fi
@@ -138,6 +140,16 @@ RUN . /.env && \
) && tar -xzf tfupdate.tgz tfupdate && rm tfupdate.tgz \
; fi
+# hcledit
+RUN . /.env && \
+ if [ "$HCLEDIT_VERSION" != "false" ]; then \
+ ( \
+ HCLEDIT_RELEASES="https://api.github.com/repos/minamijoyo/hcledit/releases" && \
+ [ "$HCLEDIT_VERSION" = "latest" ] && curl -L "$(curl -s ${HCLEDIT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_linux_amd64.tar.gz")" > hcledit.tgz \
+ || curl -L "$(curl -s ${HCLEDIT_RELEASES} | grep -o -E -m 1 "https://.+?${HCLEDIT_VERSION}_linux_amd64.tar.gz")" > hcledit.tgz \
+ ) && tar -xzf hcledit.tgz hcledit && rm hcledit.tgz \
+ ; fi
+
# Checking binaries versions and write it to debug file
RUN . /.env && \
F=tools_versions_info && \
@@ -151,6 +163,7 @@ RUN . /.env && \
(if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \
(if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \
(if [ "$TFUPDATE_VERSION" != "false" ]; then echo "tfupdate $(./tfupdate --version)" >> $F; else echo "tfupdate SKIPPED" >> $F ; fi) && \
+ (if [ "$HCLEDIT_VERSION" != "false" ]; then echo "hcledit $(./hcledit version)" >> $F; else echo "hcledit SKIPPED" >> $F ; fi) && \
echo -e "\n\n" && cat $F && echo -e "\n\n"
diff --git a/README.md b/README.md
index 81d790a00..2ad16c27e 100644
--- a/README.md
+++ b/README.md
@@ -46,11 +46,12 @@ If you are using `pre-commit-terraform` already or want to support its developme
* [terraform_tflint](#terraform_tflint)
* [terraform_tfsec](#terraform_tfsec)
* [terraform_validate](#terraform_validate)
+ * [terraform_wrapper_module_for_each](#terraform_wrapper_module_for_each)
* [terrascan](#terrascan)
* [tfupdate](#tfupdate)
* [Authors](#authors)
* [License](#license)
- * [Additional terms of use for users from Russia and Belarus](#additional-terms-of-use-for-users-from-russia-and-belarus)
+ * [Additional information for users from Russia and Belarus](#additional-information-for-users-from-russia-and-belarus)
## How to install
@@ -68,6 +69,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 `infracost_breakdown` hook.
* [`tfupdate`](https://github.com/minamijoyo/tfupdate) required for `tfupdate` hook.
+* [`hcledit`](https://github.com/minamijoyo/hcledit) required for `terraform_wrapper_module_for_each` hook.
Docker
@@ -105,6 +107,7 @@ docker build -t pre-commit-terraform \
--build-arg TFLINT_VERSION=0.31.0 \
--build-arg TFSEC_VERSION=latest \
--build-arg TFUPDATE_VERSION=latest \
+ --build-arg HCLEDIT_VERSION=latest \
.
```
@@ -116,7 +119,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 jq
+brew install pre-commit terraform-docs tflint tfsec checkov terrascan infracost tfupdate hcledit jq
```
@@ -138,6 +141,7 @@ curl -L "$(curl -s https://api.github.com/repos/accurics/terrascan/releases/late
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/
```
@@ -158,6 +162,7 @@ curl -L "$(curl -s https://api.github.com/repos/aquasecurity/tfsec/releases/late
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/
```
@@ -218,8 +223,8 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform
| Hook name | Description | Dependencies
[Install instructions here](#1-install-dependencies) |
-| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
-| `checkov` and `terraform_checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov-deprecated-and-terraform_checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` |
+| ------------------------------------------------------ |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
+| `checkov` and `terraform_checkov` | [checkov](https://github.com/bridgecrewio/checkov) static analysis of terraform templates to spot potential security issues. [Hook notes](#checkov-deprecated-and-terraform_checkov) | `checkov`
Ubuntu deps: `python3`, `python3-pip` |
| `infracost_breakdown` | Check how much your infra costs with [infracost](https://github.com/infracost/infracost). [Hook notes](#infracost_breakdown) | `infracost`, `jq`, [Infracost API key](https://www.infracost.io/docs/#2-get-api-key) |
| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) | `terraform-docs` |
| `terraform_docs_replace` | Runs `terraform-docs` and pipes the output directly to README.md. **DEPRECATED**, see [#248](https://github.com/antonbabenko/pre-commit-terraform/issues/248). [Hook notes](#terraform_docs_replace-deprecated) | `python3`, `terraform-docs` |
@@ -231,6 +236,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform
| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) | - |
| `terragrunt_fmt` | Reformat all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) to a canonical format. | `terragrunt` |
| `terragrunt_validate` | Validates all [Terragrunt](https://github.com/gruntwork-io/terragrunt) configuration files (`*.hcl`) | `terragrunt` |
+| `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/accurics/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` |
@@ -651,6 +657,27 @@ Example:
**Note:** 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 where `for_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:
+
+```yaml
+- id: terraform_wrapper_module_for_each
+ args:
+ - --args=--module-dir=. # Process only root module
+ - --args=--dry-run # No files will be created/updated
+ - --args=--verbose # Verbose output
+```
+
### 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:
@@ -709,9 +736,7 @@ This repository is managed by [Anton Babenko](https://github.com/antonbabenko) w
MIT licensed. See [LICENSE](LICENSE) for full details.
-### Additional terms of use for users from Russia and Belarus
-
-By using the code provided in this repository you agree with the following:
+### Additional information for users from Russia and Belarus
* Russia has [illegally annexed Crimea in 2014](https://en.wikipedia.org/wiki/Annexation_of_Crimea_by_the_Russian_Federation) and [brought the war in Donbas](https://en.wikipedia.org/wiki/War_in_Donbas) followed by [full-scale invasion of Ukraine in 2022](https://en.wikipedia.org/wiki/2022_Russian_invasion_of_Ukraine).
* Russia has brought sorrow and devastations to millions of Ukrainians, killed hundreds of innocent people, damaged thousands of buildings, and forced several million people to flee.
diff --git a/hooks/terraform_wrapper_module_for_each.sh b/hooks/terraform_wrapper_module_for_each.sh
new file mode 100755
index 000000000..1a7abed28
--- /dev/null
+++ b/hooks/terraform_wrapper_module_for_each.sh
@@ -0,0 +1,422 @@
+#!/usr/bin/env bash
+set -eo pipefail
+
+# globals variables
+# hook ID, see `- id` for details in .pre-commit-hooks.yaml file
+# shellcheck disable=SC2034 # Unused var.
+HOOK_ID=${0##*/}
+readonly HOOK_ID=${HOOK_ID%%.*}
+# 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::parse_and_export_env_vars
+
+ check_dependencies
+
+ # shellcheck disable=SC2153 # False positive
+ terraform_module_wrapper_ "${ARGS[*]}"
+}
+
+readonly CONTENT_MAIN_TF='module "wrapper" {}'
+readonly CONTENT_VARIABLES_TF='variable "defaults" {
+ description = "Map of default values which will be used for each item."
+ type = any
+ default = {}
+}
+
+variable "items" {
+ description = "Maps of items to create a wrapper from. Values are passed through to the module."
+ type = any
+ default = {}
+}'
+readonly CONTENT_OUTPUTS_TF='output "wrapper" {
+ description = "Map of outputs of a wrapper."
+ value = module.wrapper
+ WRAPPER_OUTPUT_SENSITIVE
+}'
+readonly CONTENT_VERSIONS_TF='terraform {
+ required_version = ">= 0.13.1"
+}'
+# shellcheck disable=SC2016 # False positive
+readonly CONTENT_README='# WRAPPER_TITLE
+
+The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt).
+
+You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module.
+
+This wrapper does not implement any extra functionality.
+
+## Usage with Terragrunt
+
+`terragrunt.hcl`:
+
+```hcl
+terraform {
+ source = "tfr:///MODULE_REPO_ORG/MODULE_REPO_SHORTNAME/MODULE_REPO_PROVIDER//WRAPPER_PATH"
+ # Alternative source:
+ # source = "git::git@github.com:MODULE_REPO_ORG/terraform-MODULE_REPO_PROVIDER-MODULE_REPO_SHORTNAME.git?ref=master//WRAPPER_PATH"
+}
+
+inputs = {
+ defaults = { # Default values
+ create = true
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+ }
+
+ items = {
+ my-item = {
+ # omitted... can be any argument supported by the module
+ }
+ my-second-item = {
+ # omitted... can be any argument supported by the module
+ }
+ # omitted...
+ }
+}
+```
+
+## Usage with Terraform
+
+```hcl
+module "wrapper" {
+ source = "MODULE_REPO_ORG/MODULE_REPO_SHORTNAME/MODULE_REPO_PROVIDER//WRAPPER_PATH"
+
+ defaults = { # Default values
+ create = true
+ tags = {
+ Terraform = "true"
+ Environment = "dev"
+ }
+ }
+
+ items = {
+ my-item = {
+ # omitted... can be any argument supported by the module
+ }
+ my-second-item = {
+ # omitted... can be any argument supported by the module
+ }
+ # omitted...
+ }
+}
+```
+
+## Example: Manage multiple S3 buckets in one Terragrunt layer
+
+`eu-west-1/s3-buckets/terragrunt.hcl`:
+
+```hcl
+terraform {
+ source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers"
+ # Alternative source:
+ # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=master//wrappers"
+}
+
+inputs = {
+ defaults = {
+ force_destroy = true
+
+ attach_elb_log_delivery_policy = true
+ attach_lb_log_delivery_policy = true
+ attach_deny_insecure_transport_policy = true
+ attach_require_latest_tls_policy = true
+ }
+
+ items = {
+ bucket1 = {
+ bucket = "my-random-bucket-1"
+ }
+ bucket2 = {
+ bucket = "my-random-bucket-2"
+ tags = {
+ Secure = "probably"
+ }
+ }
+ }
+}
+```'
+
+function terraform_module_wrapper_ {
+ local args
+ read -r -a args <<< "$1"
+
+ local root_dir
+ local module_dir="" # values: empty (default), "." (just root module), or a single module (e.g. "modules/iam-user")
+ local wrapper_dir="wrappers"
+ local wrapper_relative_source_path="../" # From "wrappers" to root_dir.
+ local module_repo_org
+ local module_repo_name
+ local module_repo_shortname
+ local module_repo_provider
+ local dry_run="false"
+ local verbose="false"
+
+ root_dir=$(git rev-parse --show-toplevel 2> /dev/null || pwd)
+ module_repo_org="terraform-aws-modules"
+ module_repo_name=${root_dir##*/}
+ module_repo_shortname="${module_repo_name#terraform-aws-}"
+ module_repo_provider="aws"
+
+ for argv in "${args[@]}"; do
+
+ local key="${argv%%=*}"
+ local value="${argv#*=}"
+
+ case "$key" in
+ --root-dir)
+ root_dir="$value"
+ ;;
+ --module-dir)
+ module_dir="$value"
+ ;;
+ --wrapper-dir)
+ wrapper_dir="$value"
+ ;;
+ --module-repo-org)
+ module_repo_org="$value"
+ ;;
+ --module-repo-shortname)
+ module_repo_shortname="$value"
+ ;;
+ --module-repo-provider)
+ module_repo_provider="$value"
+ ;;
+ --dry-run)
+ dry_run="true"
+ ;;
+ --verbose)
+ verbose="true"
+ ;;
+ *)
+ cat << EOF
+ERROR: Unrecognized argument: $key
+Hook ID: $HOOK_ID.
+Generate Terraform module wrapper. Available arguments:
+--root-dir=... - Root dir of the repository (Optional)
+--module-dir=... - Single module directory. Options: "." (means just root module),
+ "modules/iam-user" (a single module), or empty (means include all
+ submodules found in "modules/*"). Default: "${module_dir}". (Optional)
+--wrapper-dir=... - Directory where 'wrappers' should be saved. Default: "${wrapper_dir}". (Optional)
+--module-repo-org=... - Module repository organization (e.g., 'terraform-aws-modules'). (Optional)
+--module-repo-shortname=... - Short name of the repository (e.g., for 'terraform-aws-s3-bucket' it should be 's3-bucket'). (Optional)
+--module-repo-provider=... - Name of the repository provider (e.g., for 'terraform-aws-s3-bucket' it should be 'aws'). (Optional)
+--dry-run - Whether to run in dry mode. If not specified, wrapper files will be overwritten.
+--verbose - Show verbose output.
+
+Example:
+--module-dir=modules/object - Generate wrapper for one specific submodule.
+--module-dir=. - Generate wrapper for the root module.
+--module-repo-org=terraform-google-modules --module-repo-shortname=network --module-repo-provider=google - Generate wrappers for repository available by name "terraform-google-modules/network/google" in the Terraform registry and it includes all modules (root and in "modules/*").
+EOF
+ exit 1
+ ;;
+ esac
+
+ done
+
+ if [[ ! $root_dir ]]; then
+ echo "--root-dir can't be empty. Remove it to use default value."
+ exit 1
+ fi
+
+ if [[ ! $wrapper_dir ]]; then
+ echo "--wrapper-dir can't be empty. Remove it to use default value."
+ exit 1
+ fi
+
+ if [[ ! $module_repo_org ]]; then
+ echo "--module-repo-org can't be empty. Remove it to use default value."
+ exit 1
+ fi
+
+ if [[ ! $module_repo_shortname ]]; then
+ echo "--module-repo-shortname can't be empty. It should be part of full repo name (eg, s3-bucket)."
+ exit 1
+ fi
+
+ if [[ ! $module_repo_provider ]]; then
+ echo "--module-repo-provider can't be empty. It should be name of the provider used by the module (eg, aws)."
+ exit 1
+ fi
+
+ if [[ ! -d "$root_dir" ]]; then
+ echo "Root directory $root_dir does not exist!"
+ exit 1
+ fi
+
+ OLD_IFS="$IFS"
+ IFS=$'\n'
+
+ all_module_dirs=("./")
+ # Find all modules directories if nothing was provided via "--module-dir" argument
+ if [[ ! $module_dir ]]; then
+ # shellcheck disable=SC2207
+ all_module_dirs+=($(cd "${root_dir}" && find . -maxdepth 2 -path '**/modules/*' -type d -print))
+ else
+ all_module_dirs=("$module_dir")
+ fi
+
+ IFS="$OLD_IFS"
+
+ for module_dir in "${all_module_dirs[@]}"; do
+
+ # Remove "./" from the "./modules/iam-user" or "./"
+ module_dir="${module_dir/.\//}"
+
+ full_module_dir="${root_dir}/${module_dir}"
+ # echo "FULL=${full_module_dir}"
+
+ if [[ ! -d "$full_module_dir" ]]; then
+ echo "Module directory \"$full_module_dir\" does not exist!"
+ exit 1
+ fi
+
+ # Remove "modules/" from "modules/iam-user"
+ # module_name="${module_dir//modules\//}"
+ module_name="${module_dir#modules/}"
+ if [[ ! $module_name ]]; then
+ wrapper_title="Wrapper for the root module"
+ wrapper_path="${wrapper_dir}"
+ else
+ wrapper_title="Wrapper for module: \`${module_dir}\`"
+ wrapper_path="${wrapper_dir}/${module_name}"
+ fi
+
+ # Wrappers will be stored in "wrappers/{module_name}"
+ output_dir="${root_dir}/${wrapper_dir}/${module_name}"
+
+ [[ ! -d "$output_dir" ]] && mkdir -p "$output_dir"
+
+ # Calculate relative depth for module source by number of slashes
+ module_depth="${module_dir//[^\/]/}"
+
+ local relative_source_path=$wrapper_relative_source_path
+
+ for ((c = 0; c < ${#module_depth}; c++)); do
+ relative_source_path+="../"
+ done
+
+ create_tmp_file_tf
+
+ if [[ "$verbose" == "true" ]]; then
+ echo "Root directory: $root_dir"
+ echo "Module directory: $module_dir"
+ echo "Output directory: $output_dir"
+ echo "Temp file: $tmp_file_tf"
+ echo
+ fi
+
+ # Read content of all terraform files
+ # shellcheck disable=SC2207
+ all_tf_content=$(find "${full_module_dir}" -name '*.tf' -maxdepth 1 -type f -exec cat {} +)
+
+ if [[ ! $all_tf_content ]]; then
+ common::colorify "yellow" "Skipping ${full_module_dir} because there are no *.tf files."
+ continue
+ fi
+
+ # Get names of module variables in all terraform files
+ # shellcheck disable=SC2207
+ module_vars=($(echo "$all_tf_content" | hcledit block list | grep variable. | cut -d'.' -f 2))
+
+ # Get names of module outputs in all terraform files
+ # shellcheck disable=SC2207
+ module_outputs=($(echo "$all_tf_content" | hcledit block list | grep output. | cut -d'.' -f 2))
+
+ # Looking for sensitive output
+ local wrapper_output_sensitive="# sensitive = false # No sensitive module output found"
+ for module_output in "${module_outputs[@]}"; do
+ module_output_sensitive=$(echo "$all_tf_content" | hcledit attribute get "output.${module_output}.sensitive")
+
+ # At least one output is sensitive - the wrapper's output should be sensitive, too
+ if [[ "$module_output_sensitive" == "true" ]]; then
+ wrapper_output_sensitive="sensitive = true # At least one sensitive module output (${module_output}) found (requires Terraform 0.14+)"
+ break
+ fi
+ done
+
+ # Create content of temporary main.tf file
+ hcledit attribute append module.wrapper.source "\"${relative_source_path}${module_dir}\"" --newline -f "$tmp_file_tf" -u
+ hcledit attribute append module.wrapper.for_each var.items --newline -f "$tmp_file_tf" -u
+
+ # Add newline before the first variable in a loop
+ local newline="--newline"
+
+ for module_var in "${module_vars[@]}"; do
+ # Get default value for the variable
+ var_default=$(echo "$all_tf_content" | hcledit attribute get "variable.${module_var}.default")
+
+ # Empty default means that the variable is required
+ if [[ ! $var_default ]]; then
+ var_value="try(each.value.${module_var}, var.defaults.${module_var})"
+ elif [[ "$var_default" == "{" ]]; then
+ # BUG in hcledit ( https://github.com/minamijoyo/hcledit/issues/31 ) which breaks on inline comments
+ # https://github.com/terraform-aws-modules/terraform-aws-security-group/blob/0bd31aa88339194efff470d3b3f58705bd008db0/rules.tf#L8
+ # As a result, wrappers in terraform-aws-security-group module are missing values of the rules variable and is not useful. :(
+ var_value="try(each.value.${module_var}, var.defaults.${module_var}, {})"
+ else
+ var_value="try(each.value.${module_var}, var.defaults.${module_var}, $var_default)"
+ fi
+
+ hcledit attribute append "module.wrapper.${module_var}" "${var_value}" $newline -f "$tmp_file_tf" -u
+
+ newline=""
+ done
+
+ [[ "$verbose" == "true" ]] && cat "$tmp_file_tf"
+
+ if [[ "$dry_run" == "false" ]]; then
+ common::colorify "green" "Saving files into \"${output_dir}\""
+
+ mv "$tmp_file_tf" "${output_dir}/main.tf"
+
+ echo "$CONTENT_VARIABLES_TF" > "${output_dir}/variables.tf"
+ echo "$CONTENT_VERSIONS_TF" > "${output_dir}/versions.tf"
+
+ echo "$CONTENT_OUTPUTS_TF" > "${output_dir}/outputs.tf"
+ sed -i.bak "s|WRAPPER_OUTPUT_SENSITIVE|${wrapper_output_sensitive}|g" "${output_dir}/outputs.tf"
+ rm -rf "${output_dir}/outputs.tf.bak"
+
+ echo "$CONTENT_README" > "${output_dir}/README.md"
+ sed -i.bak -e "
+ s#WRAPPER_TITLE#${wrapper_title}#g
+ s#WRAPPER_PATH#${wrapper_path}#g
+ s#MODULE_REPO_ORG#${module_repo_org}#g
+ s#MODULE_REPO_SHORTNAME#${module_repo_shortname}#g
+ s#MODULE_REPO_PROVIDER#${module_repo_provider}#g
+ " "${output_dir}/README.md"
+ rm -rf "${output_dir}/README.md.bak"
+ else
+ common::colorify "yellow" "There is nothing to save. Remove --dry-run flag to write files."
+ fi
+
+ done
+
+}
+
+function check_dependencies {
+ if ! command -v hcledit > /dev/null; then
+ echo "ERROR: The binary 'hcledit' is required by this hook but is not installed or is not in the system's PATH."
+ echo "Check documentation: https://github.com/minamijoyo/hcledit"
+ exit 1
+ fi
+}
+
+function create_tmp_file_tf {
+ # Can't append extension for mktemp, so renaming instead
+ tmp_file=$(mktemp "${TMPDIR:-/tmp}/tfwrapper-XXXXXXXXXX")
+ mv "$tmp_file" "$tmp_file.tf"
+ tmp_file_tf="$tmp_file.tf"
+
+ echo "$CONTENT_MAIN_TF" > "$tmp_file_tf"
+}
+
+[[ "${BASH_SOURCE[0]}" != "$0" ]] || main "$@"