Created
May 15, 2021 18:45
-
-
Save antonbabenko/d77f8cf8bf891e589a6b5b0ab0e773ae to your computer and use it in GitHub Desktop.
Generator of Terraform module wrappers (see results: https://github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/master/wrappers)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
readonly CONTENT_MAIN_TF='module "wrapper" {}' | |
readonly CONTENT_VARIABLES_TF='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 | |
}' | |
readonly CONTENT_VERSIONS_TF='terraform { | |
required_version = ">= 0.13" | |
}' | |
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 = "git::[email protected]:MODULE_REPO_ORG/terraform-aws-MODULE_REPO_SHORTNAME.git?ref=master//WRAPPER_PATH" | |
} | |
inputs = { | |
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/aws//WRAPPER_PATH" | |
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 = "git::[email protected]:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=master//wrappers" | |
} | |
inputs = { | |
items = { | |
bucket1 = { | |
bucket = "my-random-bucket-1" | |
force_destroy = true | |
} | |
bucket2 = { | |
bucket = "my-random-bucket-2" | |
force_destroy = true | |
} | |
} | |
} | |
```' | |
check_dependencies() { | |
if [[ ! $(command -v hcledit) ]]; then | |
echo "ERROR: The binary 'hcledit' is required by this script but is not installed or in the system's PATH." | |
echo "Check documentation: https://github.com/minamijoyo/hcledit" | |
exit 1 | |
fi | |
} | |
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" | |
} | |
main() { | |
check_dependencies | |
local root_dir=$(git rev-parse --show-toplevel 2>/dev/null || pwd) | |
local module_dir="" # empty (default) or modules/iam-user | |
local wrapper_dir="wrappers" | |
local module_repo_org="terraform-aws-modules" | |
local module_repo_name=$(basename "$root_dir") | |
local module_repo_shortname="${module_repo_name//terraform-aws-/}" | |
local overwrite="false" | |
local verbose="false" | |
local relative_source_path="../" # From "wrappers" to root_dir | |
local newline="--newline" | |
while [[ $# -gt 0 ]]; do | |
local key="$1" | |
case "$key" in | |
--root-dir) | |
root_dir="$2" | |
shift | |
;; | |
--module-dir) | |
module_dir="$2" | |
shift | |
;; | |
--wrapper-dir) | |
wrapper_dir="$2" | |
shift | |
;; | |
--module-repo-org) | |
module_repo_org="$2" | |
shift | |
;; | |
--module-repo-shortname) | |
module_repo_shortname="$2" | |
shift | |
;; | |
--overwrite) | |
overwrite="true" | |
;; | |
--verbose) | |
verbose="true" | |
;; | |
*) | |
echo "ERROR: Unrecognized argument: $key" | |
echo "Usage: $0 [--root-dir <root-dir>] [--module-dir <module-dir>] [--wrapper-dir <wrapper-dir>] [--module-repo-shortname <module-repo-shortname>] [--overwrite] [--verbose]" | |
echo "Example 1: $0 --root-dir /Users/Bob/Sites/terraform-aws-modules/terraform-aws-s3-bucket --module-dir modules/object" | |
echo "Example 2: $0 --module-dir modules/object" | |
echo "Example 3: $0 --overwrite --verbose" | |
exit 1 | |
;; | |
esac | |
shift | |
done | |
if [[ -z "$root_dir" ]]; then | |
echo "--root-dir can't be empty. Remove it to use default value." | |
exit 1 | |
fi | |
if [[ -z "$wrapper_dir" ]]; then | |
echo "--wrapper-dir can't be empty. Remove it to use default value." | |
exit 1 | |
fi | |
if [[ -z "$module_repo_org" ]]; then | |
echo "--module-repo-org can't be empty. Remove it to use default value." | |
exit 1 | |
fi | |
if [[ -z "$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 [ ! -d "$root_dir" ]; then | |
echo "Root directory $root_dir does not exist!" | |
exit 1 | |
fi | |
full_module_dir="${root_dir}/${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\//}" | |
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//[^\/]}" | |
for _ in $(seq "${#module_depth}"); 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 | |
# Get names of module variables in all terraform files | |
declare -a module_vars=($(cat "${full_module_dir}"/*.tf | hcledit block list | grep variable. | cut -d'.' -f 2)) | |
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 | |
for module_var in "${module_vars[@]}"; do | |
# Get default value for the variable | |
var_default=$(cat "${full_module_dir}"/*.tf | hcledit attribute get "variable.${module_var}.default") | |
# Empty default means that the variable is required | |
if [ "$var_default" == "" ]; then | |
var_value="each.value.${module_var}" | |
else | |
var_value="lookup(each.value, \"${module_var}\", "$var_default")" | |
fi | |
hcledit attribute append "module.wrapper.${module_var}" "${var_value}" $newline -f "$tmp_file_tf" -u | |
newline="" | |
done | |
if [ "$verbose" == "true" ]; then | |
cat "$tmp_file_tf" | |
fi | |
if [ "$overwrite" == "true" ]; then | |
echo "Saving files into ${output_dir}" | |
mv "$tmp_file_tf" "${output_dir}/main.tf" | |
echo "$CONTENT_VARIABLES_TF" > "${output_dir}/variables.tf" | |
echo "$CONTENT_OUTPUTS_TF" > "${output_dir}/outputs.tf" | |
echo "$CONTENT_VERSIONS_TF" > "${output_dir}/versions.tf" | |
echo "$CONTENT_README" > "${output_dir}/README.md" | |
sed -i "s#WRAPPER_TITLE#${wrapper_title}#g" "${output_dir}/README.md" | |
sed -i "s#WRAPPER_PATH#${wrapper_path}#g" "${output_dir}/README.md" | |
sed -i "s#MODULE_REPO_ORG#${module_repo_org}#g" "${output_dir}/README.md" | |
sed -i "s#MODULE_REPO_SHORTNAME#${module_repo_shortname}#g" "${output_dir}/README.md" | |
else | |
echo "There is nothing to save. Add --overwrite flag to write files." | |
fi | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment