From 125b7719304a8b07b39131bc27b8217526ecae41 Mon Sep 17 00:00:00 2001 From: bmatei-visma Date: Fri, 29 Sep 2023 17:43:57 +0300 Subject: [PATCH] feat: commit work in progress version of old tf.sh --- new_tf | 539 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100755 new_tf diff --git a/new_tf b/new_tf new file mode 100755 index 0000000..2c4e356 --- /dev/null +++ b/new_tf @@ -0,0 +1,539 @@ +#!/bin/bash + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +## utility +contains() { + if [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] + then + true + else + false + fi +} + +install_required() { + if [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${YELLOW}Running install_required${NC}" + fi + + if [ "$(which tfswitch)" == "" ]; then + echo "tfswitch is not installed...\nInstalling latest version of tfswitch" + curl -L https://raw.githubusercontent.com/warrensbox/terraform-switcher/release/install.sh -o $HOME/install.sh && chmod 755 $HOME/install.sh + $HOME/install.sh -b $HOME/bin && rm $HOME/bin/install.sh + echo "${GREEN}Tfswitch has been installed${NC}" + elif [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${GREEN}tfswitch is installed${NC}" + fi +} + +search_up() { + if [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${YELLOW}Running search_up${NC}" + fi + + local LOOK=${PWD%/} + while [[ -n $LOOK ]]; do + [[ -d $LOOK/.tf ]] && { + DIR="$LOOK" + return + } + LOOK=${LOOK%/*} + done + if [[ -d /.tf ]] + then + DIR=/ + fi +} + +terraform_version_select() { + if [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${YELLOW}Running terraform_version_select${NC}" + fi + # Define which terraform version to use + TF_VERSION_FILE=$DIR/stacks/$TERRAFORM_STACK/terraform.version + TF_VERSION_FILE_ROOT=$DIR/terraform.version + + #TF_VERSION_FILE needs to match the SEMVAR MAJOR.MINOR.PATCH + if [ -f "$TF_VERSION_FILE" ]; then + TF_VERSION=`cat $TF_VERSION_FILE` + elif [ -f "$TF_VERSION_FILE_ROOT" ]; then + TF_VERSION=`cat $TF_VERSION_FILE_ROOT` + else + (>&2 echo -e "${RED}No version of terraform has been specified. Create first a terraform.version file with a proper semver${NC}") + exit 1 + fi + + if ! [[ $TF_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "The version specified in the 'terraform.version', doesn't match semver MAJOR.MINOR.PATCH" + exit 1 + fi + + if [ "$(terraform version | head -n 1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$TF_VERSION" ]; then + echo -e "${YELLOW}Terraform couldn't be found or it didn't match the $TF_VERSION${NC} version" + tfswitch "$TF_VERSION" + fi +} + +read_accounts() { + if [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${YELLOW}Running read_accounts${NC}" + fi + + ACCOUNTS_FILE=$DIR/accounts + if ! [[ -e $ACCOUNTS_FILE ]]; then + (>&2 echo -e "${RED}$ACCOUNTS_FILE file missing.${NC}") + exit 1 + fi + + while IFS=$'=' read -r -a anArray + do + accounts[${anArray[0]}]=`echo ${anArray[1]} | sed s/[^0-9]//` + done < $ACCOUNTS_FILE +} + +bootstrap() { + if [ "$TF_WRAPPER_DEBUG" == "true" ]; then + echo -e "${YELLOW}Running bootstrap${NC}" + fi + install_required + search_up + read_accounts + + # Setup variables used throughout the script + WORKSPACE_FILE=$DIR/.tf/.workspace + STACK_FILE=$DIR/.tf/.stack + VALID_STACKS=$(find $DIR/stacks -maxdepth 1 -mindepth 1 -type d -printf "%f " | tr ' ' '\n' | sort | tr '\n' ' ') + + # Local configuration file, for some api keys + if [ -f "$DIR/terraform.tfvars" ]; then + EXTRA_VAR_FILE=-var-file=../../terraform.tfvars + fi + + terraform_version_select +} + +context() { + if [ ! -e $WORKSPACE_FILE ] || [ ! -e $STACK_FILE ]; then + echo -e "${RED}Workspace or stack file doesn't exist!${NC}" + exit 1 + fi + + export TERRAFORM_WORKSPACE=$(cat $WORKSPACE_FILE) + export TERRAFORM_STACK=$(cat $STACK_FILE) + export AWS_PROFILE=$TERRAFORM_WORKSPACE + + if [ -z "$TERRAFORM_WORKSPACE" ] || [ -z "$TERRAFORM_STACK" ]; then + echo -e "${RED}Missing value from workspace or stack file!${NC}" + exit 1 + fi + + echo -e "Current stack: ${GREEN}$TERRAFORM_STACK${NC}" + echo -e "Current workspace: ${GREEN}$TERRAFORM_WORKSPACE${NC}" +} + +## workspace + +workspace() { + WORKSPACE_SUBCOMMAND=$1 + shift + case $WORKSPACE_SUBCOMMAND in + select) + workspace_select "$@" + ;; + list) + echo -e "Currently using workspace: [${GREEN}$(cat $WORKSPACE_FILE)${NC}]" + echo -e "Available workspaces are: [${YELLOW}${!accounts[@]}${NC}]" + ;; + help) + echo -e "Usage: ${YELLOW}$TF_BIN_NAME workspace ${NC} +Available subcommands: + +select Selects the working workspace +list List the selected workspace and available workspaces +help Prints available workspace subcommands + +${YELLOW}To initialize an already selected workspace use '$TF_BIN_NAME init'${NC}" + ;; + *) + echo -e "The command ${RED}$WORKSPACE_SUBCOMMAND${NC} is not available.\nUse '${YELLOW}$TF_BIN_NAME workspace help${NC}' to see the available command" + ;; + esac + return $? +} + +workspace_setup() { + echo $TERRAFORM_STACK + CURRENT_WORKSPACE=`terraform workspace show` + if [ $CURRENT_WORKSPACE != $TERRAFORM_WORKSPACE ] + then + echo -e "${GREEN}Workspace switched to $TERRAFORM_WORKSPACE${NC}" + workspace_init + fi +} + +workspace_init() { + stack_verify + + if [ -z $TF_USE_CURRENT_PROFILE ] + then + export AWS_PROFILE=$TERRAFORM_WORKSPACE + fi + STACK_DIR=$DIR/stacks/$TERRAFORM_STACK + + rm -rf $STACK_DIR/.terraform $STACK_DIR/terraform.tfstate.d $STACK_DIR/.terraform.lock.hcl + BACKEND_BUCKET="terraform-state-${accounts[${TERRAFORM_WORKSPACE}]}" + STATE_KEY_ID=$(aws kms list-aliases --query "Aliases[?AliasName==\`alias/terraform-state\`].{keyid:TargetKeyId}" --output text) + + terraform -chdir=$STACK_DIR init -backend-config="bucket=${BACKEND_BUCKET}" -backend-config="key=stacks/$TERRAFORM_STACK" -backend-config="encrypt=true" -backend-config="kms_key_id=${STATE_KEY_ID}" + set +e + terraform -chdir=$STACK_DIR workspace select $TERRAFORM_WORKSPACE + if [ $? != 0 ] + then + terraform -chdir=$STACK_DIR workspace new $TERRAFORM_WORKSPACE + terraform -chdir=$STACK_DIR workspace select $TERRAFORM_WORKSPACE + fi + set -e +} + +workspace_verify() { + if [ -n "$TERRAFORM_WORKSPACE" ]; then + if [ ${accounts[$TERRAFORM_WORKSPACE]+abc} ]; then + echo -e "${GREEN}Current workspace: $TERRAFORM_WORKSPACE (from env)${NC}" + workspace_setup + else + (>&2 echo -e "${RED}Invalid workspace '$TERRAFORM_WORKSPACE'. Valid workspaces: [${!accounts[@]}].${NC}") + return 1 + fi + elif [ -e $WORKSPACE_FILE ]; then + WORKSPACE=`cat $WORKSPACE_FILE` + if [ ${accounts[$WORKSPACE]+abc} ]; then + export TERRAFORM_WORKSPACE=$WORKSPACE + echo -e "${GREEN}Current workspace: $TERRAFORM_WORKSPACE${NC}" + workspace_init + else + (>&2 echo -e "${RED}No current workspace. Usage: $TF_BIN_NAME workspace [${!accounts[@]}].${NC}") + return 1 + fi + else + (>&2 echo -e "${RED}No current workspace. Usage: $$TF_BIN_NAME workspace [${!accounts[@]}].${NC}") + return 1 + fi + return 0 +} + +workspace_select() { + if [ -n "$1" ] && [ ${accounts[$1]+abc} ] + then + export TERRAFORM_WORKSPACE=$1 + echo $TERRAFORM_WORKSPACE > $WORKSPACE_FILE + echo -e "${YELLOW}Switching workspace to $TERRAFORM_WORKSPACE${NC}" + workspace_setup + else + (>&2 echo -e "${RED}workspace '$1' is invalid. Usage: $TF_BIN_NAME workspace accounts[@]}].${NC}") + return 1 + fi + return 0 +} + +## stack + +stack() { + STACK_SUBCOMMAND=$1 + shift + case $STACK_SUBCOMMAND in + select) + stack_select "$@" + ;; + list) + echo -e "Currently using stack: [${GREEN}$(cat $STACK_FILE)${NC}]" + echo -e "Available stacks are: [${YELLOW}$VALID_STACKS${NC}]" + ;; + help) + echo -e "Usage: ${YELLOW}$TF_BIN_NAME stack ${NC} +Available subcommands: + +select Selects the working stack +list List the selected stack and available stacks +help Prints available stack subcommands" + ;; + *) + echo -e "The command ${RED}$STACK_SUBCOMMAND${NC} is not available.\nUse '${YELLOW}$TF_BIN_NAME stack help${NC}' to see the available command" + ;; + esac + return $? +} + +stack_verify() { + if [ -n "$TERRAFORM_STACK" ]; then + if contains "$VALID_STACKS" $TERRAFORM_STACK + then + echo -e "${GREEN}Current stack: $TERRAFORM_STACK (from env)${NC}" + else + (>&2 echo -e "${RED}Invalid stack '$TERRAFORM_STACK' (from env). Valid stacks: $VALID_STACKS.${NC}") + return 1 + fi + elif [ -e $STACK_FILE ]; then + STACK=`cat $STACK_FILE` + if contains "$VALID_STACKS" $STACK + then + export TERRAFORM_STACK=$STACK + echo -e "${GREEN}Current stack: $TERRAFORM_STACK${NC}" + + stack_global=$DIR/stacks/$TERRAFORM_STACK/global.symlink.tf + stack_backend=$DIR/stacks/$TERRAFORM_STACK/backend.symlink.tf + if [ -f $stack_global ]; then + rm $stack_global + fi + + if [ ! -h $stack_global ]; then + ln -s $DIR/global.tf $stack_global + fi + + if [ -f $stack_backend ]; then + rm $stack_backend + fi + + if [ ! -h $stack_backend ]; then + ln -s $DIR/backend.tf $stack_backend + fi + else + (>&2 echo -e "${RED}No current stack. Use: $TF_BIN_NAME stack [stack name]${NC}") + fi + else + (>&2 echo -e "${RED}No current stack. Usage: $TF_BIN_NAME stack [$VALID_STACKS]${NC}") + return 1 + fi + + return $? +} + +stack_select() { + if contains "$VALID_STACKS" $1 + then + echo -e "${YELLOW}Switching stack to $1.${NC}" + export TERRAFORM_STACK=$1 + echo $TERRAFORM_STACK > $STACK_FILE + echo -e "${GREEN}Stack switched to $TERRAFORM_STACK.${NC}" + else + (>&2 echo -e "${RED}stack '$1' is invalid. Usage: $TF_BIN_NAME stack [$VALID_STACKS]${NC}") + return 1 + fi + + terraform_version_select + return $? +} + +## backend +backend() { + if [ ! -e $WORKSPACE_FILE ]; then + echo -e "${RED}Workspace file doesn't exist!${NC}" + return 1 + fi + + TERRAFORM_WORKSPACE=$(cat $WORKSPACE_FILE) + export AWS_PROFILE=$TERRAFORM_WORKSPACE + + echo -e "Using directory: ${GREEN}state-management${NC}" + echo -e "Current workspace: ${GREEN}$TERRAFORM_WORKSPACE${NC}" + backend=$DIR/state-management/backend.symlink.tf + if [ -f $backend ]; then + rm $backend + fi + + if [ ! -h $backend ]; then + ln -s $DIR/backend.tf $backend + fi + + BACKEND_BUCKET="terraform-state-${accounts[${TERRAFORM_WORKSPACE}]}" + STATE_KEY_ID=$(aws kms list-aliases --query "Aliases[?AliasName==\`alias/terraform-state\`].{keyid:TargetKeyId}" --output text) + if [ "$1" == "init" ]; then + rm -rf .terraform terraform.tfstate.d .terraform.lock.hcl + terraform -chdir=$DIR/state-management $1 ${@:2} -backend-config="bucket=${BACKEND_BUCKET}" -backend-config="key=backend/terraform.tfstate" -backend-config="encrypt=true" -backend-config="kms_key_id=${STATE_KEY_ID}" + else + terraform -chdir=$DIR/state-management "$@" + fi + return $? +} + +## dependencies +dependencies() { + context + + ADD_STATUS=d + if [ "x$1" = "xstatus" ] + then + ADD_STATUS=1 + fi + # check dependencies between modules + echo -e "digraph { + compound = \"true\" + newrank = \"true\" + node[style=filled]\n" + for stack in $VALID_STACKS + do + ATTRIBUTES= + if [ "$ADD_STATUS" = "1" ] + then + COLOR=red + set +e + result=$(export TERRAFORM_STACK=$stack; $0 plan -detailed-exitcode 2> /dev/null) + EXIT_CODE=$? + set -e + if [[ $EXIT_CODE == 2 ]] + then + COLOR=yellow + elif [[ $EXIT_CODE == 0 ]] + then + COLOR=green + fi + if [[ -n "$ATTRIBUTES" ]] + then + ATTRIBUTES=${ATTRIBUTES}, + fi + ATTRIBUTES=${ATTRIBUTES}fillcolor=${COLOR} + fi + echo -e \"$stack\"[$ATTRIBUTES] + done + grep \\\"terraform_remote_state $DIR/stacks/*/*.tf | sed -e 's/.*\/stacks\///; s/\/.*terraform_remote_state//; s/["{]//g; s/ /" -> "/; s/\s\+$/"/; s/^/"/' + + echo -e "}" +} + +## chamber +chamber() { + export AWS_REGION=eu-west-1 + COMMAND=$1 + case $COMMAND in + exec|help|history|list) + chamber "$@" + return $? + ;; + read|write) + shift + SERVICE=$1 + shift + export CHAMBER_KMS_KEY_ALIAS=alias/$SERVICE-configuration-secrets + chamber $COMMAND $SERVICE "$@" + return $? + ;; + esac + return 1 +} + +## conf +conf() { + context + + PREFIX=$1 + shift + COMMAND=$1 + case $COMMAND in + diff) + shift + FILE=$1 + shift + if [ -z $FILE ] + then + (>&2 echo -e "${RED}Specify a file name${NC}") + exit 1 + fi + aws s3 cp s3://$PREFIX-configuration-${accounts[$TERRAFORM_WORKSPACE]}/$FILE - | diff $DIR/configurationfiles/$PREFIX/$TERRAFORM_WORKSPACE/$FILE "$@" -- - + ;; + cp) + shift + CONF_KEY_ID=$(aws kms list-aliases --query "Aliases[?AliasName==\`alias/$PREFIX-configuration\`].{keyid:TargetKeyId}" --output text) + aws s3 cp --sse "aws:kms" --sse-kms-key-id "${CONF_KEY_ID}" $DIR/configurationfiles/$PREFIX/$TERRAFORM_WORKSPACE/ s3://$PREFIX-configuration-${accounts[$TERRAFORM_WORKSPACE]}/ --recursive "$@" + ;; + sync) + shift + CONF_KEY_ID=$(aws kms list-aliases --query "Aliases[?AliasName==\`alias/$PREFIX-configuration\`].{keyid:TargetKeyId}" --output text) + aws s3 sync --sse "aws:kms" --sse-kms-key-id "${CONF_KEY_ID}" --delete $DIR/configurationfiles/$PREFIX/$TERRAFORM_WORKSPACE/ s3://$PREFIX-configuration-${accounts[$TERRAFORM_WORKSPACE]}/ + ;; + *) + echo "Files on S3:" + aws s3 ls --recursive s3://$PREFIX-configuration-${accounts[$TERRAFORM_WORKSPACE]}/ + echo "Sync (dryrun):" + aws s3 sync --delete --dryrun $DIR/configurationfiles/$PREFIX/$TERRAFORM_WORKSPACE/ s3://$PREFIX-configuration-${accounts[$TERRAFORM_WORKSPACE]}/ + ;; + esac + return $? +} + +## completion + +## main +main() { + bootstrap + + TF_COMMAND=$1 + shift + case $TF_COMMAND in + deps) + dependencies "$@" + ;; + conf) + conf "$@" + ;; + chamber) + chamber "$@" + ;; + init) + workspace_verify + ;; + backend) + backend "$@" + ;; + apply) + context + export TF_CLI_ARGS="-var-file=$DIR/envvars/${TERRAFORM_WORKSPACE}.tfvars -var-file=$DIR/global.tfvars $EXTRA_VAR_FILE -input=false -auto-approve=false" + terraform -chdir=$DIR/stacks/$TERRAFORM_STACK $TF_COMMAND "$@" + ;; + plan|destroy|import|refresh|console) + context + export TF_CLI_ARGS="-var-file=$DIR/envvars/${TERRAFORM_WORKSPACE}.tfvars -var-file=$DIR/global.tfvars $EXTRA_VAR_FILE -input=false" + terraform -chdir=$DIR/stacks/$TERRAFORM_STACK $TF_COMMAND "$@" + ;; + get|validate|state|graph|fmt|show|taint|untaint|version|output|force-unlock|metadata|login|logout|providers) + context + terraform -chdir=$DIR/stacks/$TERRAFORM_STACK $TF_COMMAND "$@" + ;; + workspace) + workspace "$@" + ;; + stack) + stack "$@" + ;; + help) + echo -e "${YELLOW}Available $TF_BIN_NAME commands\n$TF_BIN_NAME has priority over terraform${NC}" + echo " + deps Displays dependencies between stacks + conf Interact with S3 configuration buckets + chamber Run chamber commands + init Forcefully initialize a workspace + backend Run normal terraform commands on the terraform state bucket + stack Manage stacks for the selected workspace\n" + echo -e "\n${YELLOW}Available terraform commands${NC}\n" + terraform -help + ;; + *) + (>&2 echo -e "${RED}Command '$TF_COMMAND' is not supported. Use '$TF_BIN_NAME help' for more information${NC}") + echo "${YELLOW}$TF_BIN_NAME is compatible with terrafom version ~>1.5. If you require a command that is above this version, please raise an issue at https://github.com/vismaosscomponents/terraform-wrapper${NC}" + ;; + esac + + exit $? +} + +## Running script +# Get binary name for output purposes +TF_BIN_NAME=$(echo $0 | rev | cut -d '/' -f1 | rev) +# globally scoped +declare -A accounts +main "$@" +## End of running script