Skip to content

Commit

Permalink
feat: update tf-wrapper.sh script to deal with generic folder hierarc…
Browse files Browse the repository at this point in the history
…hy (#992)
  • Loading branch information
daniel-cit committed Nov 20, 2023
1 parent 79455c9 commit 4d7e822
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 195 deletions.
215 changes: 164 additions & 51 deletions build/tf-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,78 +23,189 @@ project_id=$4
policy_type=$5 # FILESYSTEM or CLOUDSOURCE
base_dir=$(pwd)
tmp_plan="${base_dir}/tmp_plan" #if you change this, update build triggers
environments_regex="^(development|non-production|production|shared)$"

#==============================================================================#
# Configuration for the search depth of folders in the source code that
# contains the terraform configurations.
#==============================================================================#
max_depth=1 # Must be configured based in your directory design
min_depth=1 # Must be configured based in your directory design


#==============================================================================#
# The regex to find folders that contains the Terraform configurations to apply.
#
# When using environments as leaf nodes (default) the regex contains the there
# branches/environments development, non-production, and "production" and the
# additional special value "shared"
#
# When using environments as root nodes the regex contains the name of the
# folder that contain the Terraform configuration e.g: business_unit_1
# and business_unit_2
#==============================================================================#

# Environments as leaf nodes in source code case
leaf_regex_plan="^(development|non-production|production|shared)$"

# Environments as root nodes in source code case
# leaf_regex_plan="^(business_unit_1|business_unit_2)$"

#====================================================================#
# Function used for the criteria for running terraform int/plan/show
# and gcloud beta terraform vet for all the Terraform configurations.
#====================================================================#
do_plan() {
local leaf
leaf="$(basename "$1")"
if [[ "$leaf" =~ $leaf_regex_plan ]] ; then
echo "true"
else
echo "false"
fi
}

#=========================================================#
# Environments as leaf nodes in source code case (Default)
# Example:
# git-repo
# └── business_unit_1
# ├── development
# ├── non-production
# └── production
# └── business_unit_2
# ├── development
# ├── non-production
# └── production
#=========================================================#

##### Start of default source organization - comment to use Environments as root nodes #####
do_action() {
local leaf
leaf="$(basename "$1")"
if [[ "$leaf" == "$branch" ]] || [[ "$leaf" == "shared" && "$branch" == "production" ]]; then
echo "true"
else
echo "false"
fi
}
##### End of default source organization #####

#=============================================================#
# Environments as root nodes in source code Case (alternative)
# Example:
# git-repo
# └── development
# ├── business_unit_1
# └── business_unit_2
# └── non-production
# ├── business_unit_1
# └── business_unit_2
# └── production
# ├── business_unit_1
# └── business_unit_2
#=============================================================#

##### Start of alternative source organization - uncomment to use Environments as root nodes #####

# leaf_regex_action="^(business_unit_1|business_unit_2)$" # edit this list
# do_action() {
# local env_path="$1"
# local tf_env="${env_path#$base_dir/}"
# local tf_leaf
# local tf_root
# tf_leaf="$(basename "$env_path")"
# tf_root="$(echo "$tf_env" | cut -d/ -f1)"
# if [[ "$tf_leaf" =~ $leaf_regex_action ]] && [[ "$tf_root" == "$branch" ]] ; then
# echo "true"
# else
# if [[ "$tf_leaf" =~ $leaf_regex_action && "$tf_root" == "shared" && "$branch" == "production" ]]; then
# echo "true"
# else
# echo "false"
# fi
# fi
# }

##### End of alternative source organization #####

#====================================================================#
# Function to replace '/' with '-' to convert a path to a file name
#====================================================================#
convert_path() {
echo "$1" | sed -r 's/\//-/g'
}

## Terraform apply for single environment.
tf_apply() {
local path=$1
local tf_env=$2
local tf_component=$3
local tf_env="${path#$base_dir/}"
local tf_file
tf_file="$(convert_path "$tf_env")"
echo "*************** TERRAFORM APPLY *******************"
echo " At environment: ${tf_component}/${tf_env} "
echo " At environment: ${tf_env} "
echo "***************************************************"
if [ -d "$path" ]; then
cd "$path" || exit
terraform apply -input=false -auto-approve "${tmp_plan}/${tf_component}-${tf_env}.tfplan" || exit 1
terraform apply -input=false -auto-approve "${tmp_plan}/${tf_file}.tfplan" || exit 1
cd "$base_dir" || exit
else
echo "ERROR: ${path} does not exist"
echo "ERROR: ${path} does not exist"
fi
}

## terraform init for single environment.
tf_init() {
local path=$1
local tf_env=$2
local tf_component=$3
local tf_env="${path#$base_dir/}"
echo "*************** TERRAFORM INIT *******************"
echo " At environment: ${tf_component}/${tf_env} "
echo " At environment: ${tf_env} "
echo "**************************************************"
if [ -d "$path" ]; then
cd "$path" || exit
terraform init || exit 11
cd "$base_dir" || exit
else
echo "ERROR: ${path} does not exist"
echo "ERROR: ${path} does not exist"
fi
}

## terraform plan for single environment.
tf_plan() {
local path=$1
local tf_env=$2
local tf_component=$3
local tf_env="${path#$base_dir/}"
local tf_file
tf_file="$(convert_path "$tf_env")"
echo "*************** TERRAFORM PLAN *******************"
echo " At environment: ${tf_component}/${tf_env} "
echo " At environment: ${tf_env} "
echo "**************************************************"
if [ ! -d "${tmp_plan}" ]; then
mkdir "${tmp_plan}" || exit
fi
if [ -d "$path" ]; then
cd "$path" || exit
terraform plan -input=false -out "${tmp_plan}/${tf_component}-${tf_env}.tfplan" || exit 21
terraform plan -input=false -out "${tmp_plan}/${tf_file}.tfplan" || exit 21
cd "$base_dir" || exit
else
echo "ERROR: ${tf_env} does not exist"
echo "ERROR: ${path} does not exist"
fi
}

## terraform init/plan/validate for all valid environments matching regex.
#============================================================================#
# terraform init/plan/validate for all valid environments matching condition.
#============================================================================#
tf_plan_validate_all() {
local env
local component
local leaf
find "$base_dir" -mindepth 1 -maxdepth 1 -type d \
-not -path "$base_dir/modules" \
-not -path "$base_dir/.git" \
-not -path "$base_dir/.terraform" | while read -r component_path ; do
component="$(basename "$component_path")"
find "$component_path" -mindepth 1 -maxdepth 1 -type d | while read -r env_path ; do
env="$(basename "$env_path")"
if [[ "$env" =~ $environments_regex ]] ; then
tf_init "$env_path" "$env" "$component"
tf_plan "$env_path" "$env" "$component"
tf_validate "$env_path" "$env" "$policysource" "$component"
find "$component_path" -mindepth "$min_depth" -maxdepth "$max_depth" -type d | while read -r env_path ; do
if [[ "$(do_plan "$env_path")" == "true" ]] ; then
tf_init "$env_path"
tf_plan "$env_path"
tf_validate "$env_path" "$policysource"
else
echo "$component/$env doesn't match $environments_regex; skipping"
echo "${env_path#$base_dir/} doesn't match $leaf_regex_plan; skipping"
fi
done
done
Expand All @@ -103,28 +214,30 @@ tf_plan_validate_all() {
## terraform show for single environment.
tf_show() {
local path=$1
local tf_env=$2
local tf_component=$3
local tf_env="${path#$base_dir/}"
local tf_file
tf_file="$(convert_path "$tf_env")"
echo "*************** TERRAFORM SHOW *******************"
echo " At environment: ${tf_component}/${tf_env} "
echo " At environment: ${tf_env} "
echo "**************************************************"
if [ -d "$path" ]; then
cd "$path" || exit
terraform show "${tmp_plan}/${tf_component}-${tf_env}.tfplan" || exit 41
terraform show "${tmp_plan}/${tf_file}.tfplan" || exit 41
cd "$base_dir" || exit
else
echo "ERROR: ${path} does not exist"
echo "ERROR: ${path} does not exist"
fi
}

## terraform validate for single environment.
tf_validate() {
local path=$1
local tf_env=$2
local policy_file_path=$3
local tf_component=$4
local policy_file_path=$2
local tf_env="${path#$base_dir/}"
local tf_file
tf_file="$(convert_path "$tf_env")"
echo "*************** TERRAFORM VALIDATE ******************"
echo " At environment: ${tf_component}/${tf_env} "
echo " At environment: ${tf_env} "
echo " Using policy from: ${policy_file_path} "
echo "*****************************************************"
if [ -z "$policy_file_path" ]; then
Expand All @@ -133,7 +246,7 @@ tf_validate() {
else
if [ -d "$path" ]; then
cd "$path" || exit
terraform show -json "${tmp_plan}/${tf_component}-${tf_env}.tfplan" > "${tf_env}.json" || exit 32
terraform show -json "${tmp_plan}/${tf_file}.tfplan" > "${tf_file}.json" || exit 32
if [[ "$policy_type" == "CLOUDSOURCE" ]]; then
# Check if $policy_file_path is empty so we clone the policies repo only once
if [ -z "$(ls -A "${policy_file_path}" 2> /dev/null)" ]; then
Expand All @@ -152,53 +265,53 @@ tf_validate() {
popd
fi
fi
gcloud beta terraform vet "${tf_env}.json" --policy-library="${policy_file_path}" --project="${project_id}" || exit 33
gcloud beta terraform vet "${tf_file}.json" --policy-library="${policy_file_path}" --project="${project_id}" || exit 33
cd "$base_dir" || exit
else
echo "ERROR: ${path} does not exist"
echo "ERROR: ${path} does not exist"
fi
fi
}

#=================================================================#
# Runs single action for each instance of env in folder hierarchy.
#=================================================================#
single_action_runner() {
local env
local component
local leaf
# filter folders that does not contain Terraform configurations
find "$base_dir" -mindepth 1 -maxdepth 1 -type d \
-not -path "$base_dir/modules" \
-not -path "$base_dir/.git" \
-not -path "$base_dir/.terraform" | while read -r component_path ; do
component="$(basename "$component_path")"
# sort -r is added to ensure shared is first if it exists.
find "$component_path" -mindepth 1 -maxdepth 1 -type d | sort -r | while read -r env_path ; do
env="$(basename "$env_path")"
# perform action only if folder matches branch OR folder is shared & branch is production.
if [[ "$env" == "$branch" ]] || [[ "$env" == "shared" && "$branch" == "production" ]]; then
find "$component_path" -mindepth "$min_depth" -maxdepth "$max_depth" -type d | sort -r | while read -r env_path ; do
if [[ "$(do_action "$env_path")" == "true" ]]; then
case "$action" in
apply )
tf_apply "$env_path" "$env" "$component"
tf_apply "$env_path"
;;

init )
tf_init "$env_path" "$env" "$component"
tf_init "$env_path"
;;

plan )
tf_plan "$env_path" "$env" "$component"
tf_plan "$env_path"
;;

show )
tf_show "$env_path" "$env" "$component"
tf_show "$env_path"
;;

validate )
tf_validate "$env_path" "$env" "$policysource" "$component"
tf_validate "$env_path" "$policysource"
;;
* )
echo "unknown option: ${action}"
;;
esac
else
echo "${env} doesn't match ${branch}; skipping"
echo "${env_path#$base_dir/} doesn't match ${branch}; skipping"
fi
done
done
Expand Down

0 comments on commit 4d7e822

Please sign in to comment.