From 684f1734cb20706a6e479bf57bfd6106a1c24314 Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Mon, 24 May 2021 23:17:19 -0400 Subject: [PATCH 1/7] Clone starter demo and deploy --- init-deploy.sh | 33 ++++++++++++++++++ init.sh | 83 ++++++++++++++++++++++++++++++++++++++++++++ scripts/clone.sh | 36 +++++++++++++++++++ scripts/functions.sh | 4 +++ scripts/rover.sh | 5 +++ 5 files changed, 161 insertions(+) create mode 100755 init-deploy.sh create mode 100755 init.sh diff --git a/init-deploy.sh b/init-deploy.sh new file mode 100755 index 00000000..e4db5973 --- /dev/null +++ b/init-deploy.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +export caf_environment=demo + +function main() { + + # DOWNLOAD + + # DEPLOY + # Launchpad + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_launchpad -launchpad -var-folder /tf/caf/sample/configs/demo/level0/launchpad -level level0 -env ${caf_environment} -a apply & + wait + + # Platform + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate caf_foundations.tfstate -var-folder /tf/caf/sample/configs/demo/level1 -level level1 -env ${caf_environment} -a apply & + wait + + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate networking_hub.tfstate -var-folder /tf/caf/sample/configs/demo/level2/networking/hub -level level2 -env ${caf_environment} -a apply & + wait + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate caf_shared_services.tfstate -var-folder /tf/caf/sample/configs/demo/level2/shared_services -level level2 -env ${caf_environment} -a apply & + wait + + # Sample Apps + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_aks.tfstate -var-folder /tf/caf/sample/configs/demo/level3/aks -level level3 -env ${caf_environment} -a apply & + wait + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_app_svc.tfstate -var-folder /tf/caf/sample/configs/demo/level3/app_service -level level3 -env ${caf_environment} -a apply & + wait + bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_aml.tfstate -var-folder /tf/caf/sample/configs/demo/level3/data_analytics/101-aml-workspace -level level3 -env ${caf_environment} -a apply & + wait + +} + +main diff --git a/init.sh b/init.sh new file mode 100755 index 00000000..0bfb7def --- /dev/null +++ b/init.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +export caf_environment=demo + +function main() { + # Downnload zones and demo configs to demo + rm -rf /tf/caf/${caf_environment} + bash /tf/rover/rover.sh --clone-sample "demo" & + wait + + echo -n "Would you like to generate the Deployment script and instructions? (y/n): " + read proceed + + [[ $proceed != "y" ]] && exit + + # Create sample readme + write_md "# Deployment Steps" + init_sh + + # Write the rover command for each configuration set + for line in $(find /tf/caf/${caf_environment}/configs -type f \( -name "configuration.tfvars" -o -name "landingzone.tfvars" \) | sort); do + #TODO: find out tfstate + # grep -R "tfstate = \"" /tf/caf/demo/configs/demo/ | sort + + write_rover ${line%\/*} + write_bash ${line%\/*} + done + + end_sh +} + +function write_rover() { + local var_folder=$1 + + start=$(echo $var_folder | grep -b -o "level" | awk 'BEGIN {FS=":"}{print $1}') + level="level${var_folder:$(expr $start + 5):1}" + + write_md "## Deploy $level: $(basename $var_folder)" + write_md "\`\`\`bash" + write_md "rover \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_md " -launchpad \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_md " -lz /tf/caf/${caf_environment}/landingzones/caf_launchpad \\" || write_md " -lz /tf/caf/${caf_environment}/landingzones/caf_solution \\" + write_md " -var-folder $var_folder \\" + write_md " -level $level \\" + write_md " -env ${caf_environment} \\" + write_md " -a apply" + write_md "\`\`\`" +} + +function write_bash() { + local var_folder=$1 + + start=$(echo $var_folder | grep -b -o "level" | awk 'BEGIN {FS=":"}{print $1}') + level="level${var_folder:$(expr $start + 5):1}" + + write_sh "# Deploy $level $(basename $var_folder)" + write_sh "bash /tf/rover/rover.sh \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_sh " -lz /tf/caf/${caf_environment}/landingzones/caf_launchpad \\" || write_sh " -lz /tf/caf/${caf_environment}/landingzones/caf_solution \\" + write_sh " -var-folder $var_folder \\" + write_sh " -level $level \\" + write_sh " -env ${caf_environment} \\" + write_sh " -a apply" + write_sh "wait" +} + +function init_sh() { + write_sh "#!/bin/bash\n\nfunction main() {\n" +} + +function end_sh() { + write_sh "}\nmain" + chmod +x /tf/caf/${caf_environment}/deploy.sh +} + +function write_md() { + echo -e $1 >>/tf/caf/${caf_environment}/README.md +} + +function write_sh() { + echo -e $1 >>/tf/caf/${caf_environment}/deploy.sh +} + +main diff --git a/scripts/clone.sh b/scripts/clone.sh index a1158d02..d981924b 100644 --- a/scripts/clone.sh +++ b/scripts/clone.sh @@ -57,6 +57,14 @@ function display_clone_instructions { echo shift 1 ;; + --clone-project-name) + echo "--clone-project-name specify the GitHub repo to download from, default is Azure/caf-terraform-landingzones" + echo + echo " Example: --clone-project-name Azure/caf-terraform-landingzones-starter --clone-branch starter will download the starter branch of the Azure/caf-terraform-landingzones-starter repo" + echo " Note: the default --cone-branch is master and this is not available in the example repo above so the starter branch is specified." + echo + shift 1 + ;; --examples) echo "By default the rover will clone the azure/caf-terraform-landingzones into the local rover folder /tf/caf/landinzones" echo @@ -75,6 +83,26 @@ function display_clone_instructions { done } +function clone_sample_repository { + local sample=$1 + + set_clone_exports "/tf/caf/$sample/landingzones" "/caf_launchpad" "1" "Azure/caf-terraform-landingzones" "master" + clone_repository + + set_clone_exports "/tf/caf/$sample/landingzones" "/caf_solution" "1" "Azure/caf-terraform-landingzones" "master" + clone_repository + + set_clone_exports "/tf/caf/$sample/configs" "/configuration/$sample" "2" "Azure/caf-terraform-landingzones-starter" "starter" + clone_repository +} + +function set_clone_exports { + export clone_destination=$1 + export clone_folder=$2 + export clone_folder_strip=$3 + export clone_project_name=$4 + export landingzone_branch=$5 +} function clone_repository { echo "@calling clone_repository" @@ -149,5 +177,13 @@ function process_clone_parameter { export clone_folder_strip=${2} fi ;; + --clone-project-name) + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 24 + else + export clone_project_name=${2} + fi + ;; esac } diff --git a/scripts/functions.sh b/scripts/functions.sh index e04c7751..01fc2e88 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -96,6 +96,10 @@ function process_actions { workspace ${tf_command} exit 0 ;; + clone_sample) + clone_sample_repository "demo" + exit 0 + ;; clone) clone_repository exit 0 diff --git a/scripts/rover.sh b/scripts/rover.sh index ecec7449..09a5e51a 100755 --- a/scripts/rover.sh +++ b/scripts/rover.sh @@ -38,6 +38,11 @@ mkdir -p ${TF_PLUGIN_CACHE_DIR} while (( "$#" )); do case "${1}" in + --clone-sample) + export caf_command="clone_sample" + echo "cloning sample" + shift 1 + ;; --clone|--clone-branch|--clone-folder|--clone-destination|--clone-folder-strip) export caf_command="clone" process_clone_parameter $@ From 0983863ef771ec381d6a4d4de8d039b58a7b908b Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Wed, 2 Jun 2021 13:34:22 -0400 Subject: [PATCH 2/7] Refactored walkthrough, added destroy sh --- .gitignore | 3 +- init-deploy.sh | 33 --- init.sh | 83 ------- scripts/clone.sh | 257 ++++++++++----------- scripts/functions.sh | 449 ++++++++++++++++++------------------- scripts/rover.sh | 331 +++++++++++++-------------- scripts/tfstate_azurerm.sh | 277 +++++++++++------------ scripts/walkthrough.sh | 240 ++++++++++++++++++++ 8 files changed, 883 insertions(+), 790 deletions(-) delete mode 100755 init-deploy.sh delete mode 100755 init.sh create mode 100644 scripts/walkthrough.sh diff --git a/.gitignore b/.gitignore index 50a20da3..fd08989d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ **/*.log version.txt public/ -.data \ No newline at end of file +walkthrough/ +.data diff --git a/init-deploy.sh b/init-deploy.sh deleted file mode 100755 index e4db5973..00000000 --- a/init-deploy.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -export caf_environment=demo - -function main() { - - # DOWNLOAD - - # DEPLOY - # Launchpad - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_launchpad -launchpad -var-folder /tf/caf/sample/configs/demo/level0/launchpad -level level0 -env ${caf_environment} -a apply & - wait - - # Platform - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate caf_foundations.tfstate -var-folder /tf/caf/sample/configs/demo/level1 -level level1 -env ${caf_environment} -a apply & - wait - - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate networking_hub.tfstate -var-folder /tf/caf/sample/configs/demo/level2/networking/hub -level level2 -env ${caf_environment} -a apply & - wait - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate caf_shared_services.tfstate -var-folder /tf/caf/sample/configs/demo/level2/shared_services -level level2 -env ${caf_environment} -a apply & - wait - - # Sample Apps - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_aks.tfstate -var-folder /tf/caf/sample/configs/demo/level3/aks -level level3 -env ${caf_environment} -a apply & - wait - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_app_svc.tfstate -var-folder /tf/caf/sample/configs/demo/level3/app_service -level level3 -env ${caf_environment} -a apply & - wait - bash /tf/rover/rover.sh -lz /tf/caf/sample/landingzones/caf_solution -tfstate landing_zone_aml.tfstate -var-folder /tf/caf/sample/configs/demo/level3/data_analytics/101-aml-workspace -level level3 -env ${caf_environment} -a apply & - wait - -} - -main diff --git a/init.sh b/init.sh deleted file mode 100755 index 0bfb7def..00000000 --- a/init.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -export caf_environment=demo - -function main() { - # Downnload zones and demo configs to demo - rm -rf /tf/caf/${caf_environment} - bash /tf/rover/rover.sh --clone-sample "demo" & - wait - - echo -n "Would you like to generate the Deployment script and instructions? (y/n): " - read proceed - - [[ $proceed != "y" ]] && exit - - # Create sample readme - write_md "# Deployment Steps" - init_sh - - # Write the rover command for each configuration set - for line in $(find /tf/caf/${caf_environment}/configs -type f \( -name "configuration.tfvars" -o -name "landingzone.tfvars" \) | sort); do - #TODO: find out tfstate - # grep -R "tfstate = \"" /tf/caf/demo/configs/demo/ | sort - - write_rover ${line%\/*} - write_bash ${line%\/*} - done - - end_sh -} - -function write_rover() { - local var_folder=$1 - - start=$(echo $var_folder | grep -b -o "level" | awk 'BEGIN {FS=":"}{print $1}') - level="level${var_folder:$(expr $start + 5):1}" - - write_md "## Deploy $level: $(basename $var_folder)" - write_md "\`\`\`bash" - write_md "rover \\" - [[ $(basename $var_folder) = "launchpad" ]] && write_md " -launchpad \\" - [[ $(basename $var_folder) = "launchpad" ]] && write_md " -lz /tf/caf/${caf_environment}/landingzones/caf_launchpad \\" || write_md " -lz /tf/caf/${caf_environment}/landingzones/caf_solution \\" - write_md " -var-folder $var_folder \\" - write_md " -level $level \\" - write_md " -env ${caf_environment} \\" - write_md " -a apply" - write_md "\`\`\`" -} - -function write_bash() { - local var_folder=$1 - - start=$(echo $var_folder | grep -b -o "level" | awk 'BEGIN {FS=":"}{print $1}') - level="level${var_folder:$(expr $start + 5):1}" - - write_sh "# Deploy $level $(basename $var_folder)" - write_sh "bash /tf/rover/rover.sh \\" - [[ $(basename $var_folder) = "launchpad" ]] && write_sh " -lz /tf/caf/${caf_environment}/landingzones/caf_launchpad \\" || write_sh " -lz /tf/caf/${caf_environment}/landingzones/caf_solution \\" - write_sh " -var-folder $var_folder \\" - write_sh " -level $level \\" - write_sh " -env ${caf_environment} \\" - write_sh " -a apply" - write_sh "wait" -} - -function init_sh() { - write_sh "#!/bin/bash\n\nfunction main() {\n" -} - -function end_sh() { - write_sh "}\nmain" - chmod +x /tf/caf/${caf_environment}/deploy.sh -} - -function write_md() { - echo -e $1 >>/tf/caf/${caf_environment}/README.md -} - -function write_sh() { - echo -e $1 >>/tf/caf/${caf_environment}/deploy.sh -} - -main diff --git a/scripts/clone.sh b/scripts/clone.sh index d981924b..c68e69a6 100644 --- a/scripts/clone.sh +++ b/scripts/clone.sh @@ -8,100 +8,86 @@ export landingzone_branch=${landingzone_branch:="master"} current_path=$(pwd) - function display_clone_instructions { - while (( "$#" )); do + while (("$#")); do case "${1}" in - --intro) - echo - echo "Rover clone is used to bring the landing zones dependencies you need to deploy your landing zone" - echo - shift 1 - ;; - --clone) - display_clone_instructions --intro --examples --clone-branch --clone-destination --clone-folder --clone-folder-strip - echo "--clone specify a GitHub organization and project in the for org/project" - echo " The default setting if not set is azure/caf-terraform-landingzones" - echo - shift 1 - ;; - --clone-branch) - echo "--clone-branch set the branch to pull the package." - echo " By default is not set use the master branch." - echo - shift 1 - ;; - --clone-destination) - echo "--clone-destination change the destination local folder." - echo " By default clone the package into the /tf/caf/landingzones folder of the rover" - echo - shift 1 - ;; - --clone-folder) - echo "--clone-folder specify the folder to extract from the original project" - echo - echo " Example: --clone-folder /landingzones/landingzone_caf_foundations will only extract the caf foundations landing zone" - echo - shift 1 - ;; - --clone-folder-strip) - echo "--clone-folder-strip is used strip the base folder structure from the original folder" - echo - echo " In the GitHub package of azure/caf-terraform-landingzones, the data are packaged in the following structure" - echo " caf-terraform-landingzones-master/landingzones/launchpad/main.tf" - echo " [project]-[branch]/landgingzones/[landingzone]" - echo " To reproduce a nice folder structure in the rover it it possible to set the --clone-folder-strip to 2 to remove [project]-[branch]/landingzones and only retrieve the third level folder" - echo "" - echo " Default to 2 when using azure/caf-terraform-landingzones and 1 for all other git projects" - echo - shift 1 - ;; - --clone-project-name) - echo "--clone-project-name specify the GitHub repo to download from, default is Azure/caf-terraform-landingzones" - echo - echo " Example: --clone-project-name Azure/caf-terraform-landingzones-starter --clone-branch starter will download the starter branch of the Azure/caf-terraform-landingzones-starter repo" - echo " Note: the default --cone-branch is master and this is not available in the example repo above so the starter branch is specified." - echo - shift 1 - ;; - --examples) - echo "By default the rover will clone the azure/caf-terraform-landingzones into the local rover folder /tf/caf/landinzones" - echo - echo "Examples:" - echo " - Clone the launchpad: rover --clone-folder /landingzones/launchpad" - echo " - Clone the launchpad in different folder: rover --clone-destination /tf/caf/landingzones/public --clone-folder /landingzones/launchpad" - echo " - Clone the launchpad (branch vnext): rover --clone-folder-strip 2 --clone-destination /tf/rover/landingzones --clone-folder /landingzones/launchpad --clone-branch vnext" - echo - echo " - Clone the CAF foundations landingzone: rover --clone-folder /landingzones/landingzone_caf_foundations" - echo " - Clone the AKS landingzone: rover --clone aztfmod/landingzone_aks --clone-destination /tf/caf/landingzones/landingzone_aks" - echo - echo - shift 1 - ;; + --intro) + echo + echo "Rover clone is used to bring the landing zones dependencies you need to deploy your landing zone" + echo + shift 1 + ;; + --clone) + display_clone_instructions --intro --examples --clone-branch --clone-destination --clone-folder --clone-folder-strip + echo "--clone specify a GitHub organization and project in the for org/project" + echo " The default setting if not set is azure/caf-terraform-landingzones" + echo + shift 1 + ;; + --clone-branch) + echo "--clone-branch set the branch to pull the package." + echo " By default is not set use the master branch." + echo + shift 1 + ;; + --clone-destination) + echo "--clone-destination change the destination local folder." + echo " By default clone the package into the /tf/caf/landingzones folder of the rover" + echo + shift 1 + ;; + --clone-folder) + echo "--clone-folder specify the folder to extract from the original project" + echo + echo " Example: --clone-folder /landingzones/landingzone_caf_foundations will only extract the caf foundations landing zone" + echo + shift 1 + ;; + --clone-folder-strip) + echo "--clone-folder-strip is used strip the base folder structure from the original folder" + echo + echo " In the GitHub package of azure/caf-terraform-landingzones, the data are packaged in the following structure" + echo " caf-terraform-landingzones-master/landingzones/launchpad/main.tf" + echo " [project]-[branch]/landgingzones/[landingzone]" + echo " To reproduce a nice folder structure in the rover it it possible to set the --clone-folder-strip to 2 to remove [project]-[branch]/landingzones and only retrieve the third level folder" + echo "" + echo " Default to 2 when using azure/caf-terraform-landingzones and 1 for all other git projects" + echo + shift 1 + ;; + --clone-project-name) + echo "--clone-project-name specify the GitHub repo to download from, default is Azure/caf-terraform-landingzones" + echo + echo " Example: --clone-project-name Azure/caf-terraform-landingzones-starter --clone-branch starter will download the starter branch of the Azure/caf-terraform-landingzones-starter repo" + echo " Note: the default --cone-branch is master and this is not available in the example repo above so the starter branch is specified." + echo + shift 1 + ;; + --examples) + echo "By default the rover will clone the azure/caf-terraform-landingzones into the local rover folder /tf/caf/landinzones" + echo + echo "Examples:" + echo " - Clone the launchpad: rover --clone-folder /landingzones/launchpad" + echo " - Clone the launchpad in different folder: rover --clone-destination /tf/caf/landingzones/public --clone-folder /landingzones/launchpad" + echo " - Clone the launchpad (branch vnext): rover --clone-folder-strip 2 --clone-destination /tf/rover/landingzones --clone-folder /landingzones/launchpad --clone-branch vnext" + echo + echo " - Clone the CAF foundations landingzone: rover --clone-folder /landingzones/landingzone_caf_foundations" + echo " - Clone the AKS landingzone: rover --clone aztfmod/landingzone_aks --clone-destination /tf/caf/landingzones/landingzone_aks" + echo + echo + shift 1 + ;; esac done } -function clone_sample_repository { - local sample=$1 - - set_clone_exports "/tf/caf/$sample/landingzones" "/caf_launchpad" "1" "Azure/caf-terraform-landingzones" "master" - clone_repository - - set_clone_exports "/tf/caf/$sample/landingzones" "/caf_solution" "1" "Azure/caf-terraform-landingzones" "master" - clone_repository - - set_clone_exports "/tf/caf/$sample/configs" "/configuration/$sample" "2" "Azure/caf-terraform-landingzones-starter" "starter" - clone_repository -} - function set_clone_exports { - export clone_destination=$1 - export clone_folder=$2 - export clone_folder_strip=$3 - export clone_project_name=$4 - export landingzone_branch=$5 + export clone_destination=$1 + export clone_folder=$2 + export clone_folder_strip=$3 + export clone_project_name=$4 + export landingzone_branch=$5 } function clone_repository { @@ -131,59 +117,58 @@ function clone_repository { function process_clone_parameter { echo "@calling process_clone_parameter with $@" - case "${1}" in - --clone) - if [ $# -eq 1 ]; then - display_clone_instructions ${1} - exit 21 - else - export caf_command="clone" - export landingzone_branch=${landingzone_branch:="master"} - export clone_project_name=${2} - export clone_folder_strip=1 - fi - ;; - --clone-branch) - echo $# - if [ $# -eq 1 ]; then - display_clone_instructions ${1} - exit 22 - else - export landingzone_branch=${2} - fi - ;; - --clone-destination) - if [ $# -eq 1 ]; then - display_clone_instructions ${1} - exit 23 - else - export clone_destination=${2} - fi - ;; - --clone-folder) - if [ $# -eq 1 ]; then - display_clone_instructions ${1} - exit 24 - else - export clone_folder=${2} - fi - ;; - --clone-folder-strip) - if [ $# -eq 1 ]; then - display_clone_instructions ${1} - exit 24 - else - export clone_folder_strip=${2} - fi - ;; - --clone-project-name) - if [ $# -eq 1 ]; then + --clone) + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 21 + else + export caf_command="clone" + export landingzone_branch=${landingzone_branch:="master"} + export clone_project_name=${2} + export clone_folder_strip=1 + fi + ;; + --clone-branch) + echo $# + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 22 + else + export landingzone_branch=${2} + fi + ;; + --clone-destination) + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 23 + else + export clone_destination=${2} + fi + ;; + --clone-folder) + if [ $# -eq 1 ]; then display_clone_instructions ${1} exit 24 - else + else + export clone_folder=${2} + fi + ;; + --clone-folder-strip) + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 24 + else + export clone_folder_strip=${2} + fi + ;; + --clone-project-name) + if [ $# -eq 1 ]; then + display_clone_instructions ${1} + exit 24 + else export clone_project_name=${2} - fi - ;; + fi + ;; esac } diff --git a/scripts/functions.sh b/scripts/functions.sh index 01fc2e88..fe916563 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -1,18 +1,16 @@ - - error() { local parent_lineno="$1" local message="$2" local code="${3:-1}" local line_message="" if [ "$parent_lineno" != "" ]; then - line_message="on or near line ${parent_lineno}" + line_message="on or near line ${parent_lineno}" fi - if [[ -n "$message" ]] ; then - >&2 echo -e "\e[41mError $line_message: ${message}; exiting with status ${code}\e[0m" + if [[ -n "$message" ]]; then + echo >&2 -e "\e[41mError $line_message: ${message}; exiting with status ${code}\e[0m" else - >&2 echo -e "\e[41mError $line_message; exiting with status ${code}\e[0m" + echo >&2 -e "\e[41mError $line_message; exiting with status ${code}\e[0m" fi echo "" @@ -22,15 +20,14 @@ error() { } error_message() { - >&2 printf "\e[91m$@\n\e[0m" + printf >&2 "\e[91m$@\n\e[0m" } - debug() { - local message=$1 - if [ "$debug_mode" == "true" ]; then - echo "$message" - fi + local message=$1 + if [ "$debug_mode" == "true" ]; then + echo "$message" + fi } information() { @@ -41,15 +38,14 @@ success() { printf "\e[32m$@\n\e[0m" } - exit_if_error() { - local exit_code=$1 - shift - [[ $exit_code ]] && # do nothing if no error code passed - ((exit_code != 0)) && { # do nothing if error code is 0 - printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here - exit "$exit_code" # we could also check to make sure - # error code is numeric when passed + local exit_code=$1 + shift + [[ $exit_code ]] && # do nothing if no error code passed + ((exit_code != 0)) && { # do nothing if error code is 0 + printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here + exit "$exit_code" # we could also check to make sure + # error code is numeric when passed } } @@ -57,73 +53,71 @@ exit_if_error() { # Execute a command and re-execute it with a backoff retry logic. This is mainly to handle throttling situations in CI # function execute_with_backoff { - local max_attempts=${ATTEMPTS-5} - local timeout=${TIMEOUT-20} - local attempt=0 - local exitCode=0 - - while [[ $attempt < $max_attempts ]] - do - set +e - "$@" - exitCode=$? - set -e - - if [[ $exitCode == 0 ]] - then - break - fi + local max_attempts=${ATTEMPTS-5} + local timeout=${TIMEOUT-20} + local attempt=0 + local exitCode=0 + + while [[ $attempt < $max_attempts ]]; do + set +e + "$@" + exitCode=$? + set -e + + if [[ $exitCode == 0 ]]; then + break + fi - echo "Failure! Return code ${exitCode} - Retrying in $timeout.." 1>&2 - sleep $timeout - attempt=$(( attempt + 1 )) - timeout=$(( timeout * 2 )) - done + echo "Failure! Return code ${exitCode} - Retrying in $timeout.." 1>&2 + sleep $timeout + attempt=$((attempt + 1)) + timeout=$((timeout * 2)) + done - if [[ $exitCode != 0 ]] - then - echo "Hit the max retry count ($@)" 1>&2 - fi + if [[ $exitCode != 0 ]]; then + echo "Hit the max retry count ($@)" 1>&2 + fi - return $exitCode + return $exitCode } function process_actions { echo "@calling process_actions" case "${caf_command}" in - workspace) - workspace ${tf_command} - exit 0 - ;; - clone_sample) - clone_sample_repository "demo" - exit 0 - ;; - clone) - clone_repository - exit 0 - ;; - landingzone_mgmt) - landing_zone ${tf_command} - exit 0 - ;; - launchpad|landingzone) - verify_parameters - deploy ${TF_VAR_workspace} - ;; - tfc) - verify_parameters - deploy_tfc ${TF_VAR_workspace} - ;; - ci) - register_ci_tasks - verify_ci_parameters - set_default_parameters - execute_ci_actions - ;; - *) - display_instructions + workspace) + workspace ${tf_command} + exit 0 + ;; + walkthrough) + execute_walkthrough + exit 0 + ;; + clone) + clone_repository + exit 0 + ;; + landingzone_mgmt) + landing_zone ${tf_command} + exit 0 + ;; + launchpad | landingzone) + verify_parameters + deploy ${TF_VAR_workspace} + ;; + tfc) + verify_parameters + deploy_tfc ${TF_VAR_workspace} + ;; + ci) + register_ci_tasks + verify_ci_parameters + set_default_parameters + execute_ci_actions + ;; + *) + display_instructions + ;; esac } @@ -153,7 +147,7 @@ function display_instructions { if [ -d "/tf/caf/public/landingzones" ]; then for i in $(ls -d /tf/caf/public/landingzones/*); do echo ${i%%/}; done - echo "" + echo "" fi } @@ -223,24 +217,24 @@ function verify_azure_session { fi if [ "${caf_command}" == "logout" ]; then - echo "Closing Azure session" - az logout || true + echo "Closing Azure session" + az logout || true - # Cleaup any service principal session - unset ARM_TENANT_ID - unset ARM_SUBSCRIPTION_ID - unset ARM_CLIENT_ID - unset ARM_CLIENT_SECRET + # Cleaup any service principal session + unset ARM_TENANT_ID + unset ARM_SUBSCRIPTION_ID + unset ARM_CLIENT_ID + unset ARM_CLIENT_SECRET - echo "Azure session closed" - exit + echo "Azure session closed" + exit fi echo "Checking existing Azure session" session=$(az account show -o json 2>/dev/null || true) if [ "$session" == '' ]; then - display_login_instructions - error ${LINENO} "you must login to an Azure subscription first or 'rover login' again" 2 + display_login_instructions + error ${LINENO} "you must login to an Azure subscription first or 'rover login' again" 2 fi } @@ -250,7 +244,7 @@ function check_subscription_required_role { role=$(az role assignment list --role "${1}" --assignee ${TF_VAR_logged_user_objectId} --include-inherited --include-groups) if [ "${role}" == "[]" ]; then - error ${LINENO} "the current account must have ${1} privilege on the subscription to deploy launchpad." 2 + error ${LINENO} "the current account must have ${1} privilege on the subscription to deploy launchpad." 2 else echo "User is ${1} of the subscription" fi @@ -271,10 +265,10 @@ function list_deployed_landingzones { --subscription ${TF_VAR_tfstate_subscription_id} \ -c ${TF_VAR_workspace} \ --auth-mode login \ - --account-name ${storage_account_name} -o json | \ - jq -r '["landing zone", "size in Kb", "last modification"], (.[] | [.name, .properties.contentLength / 1024, .properties.lastModified]) | @csv' | \ - awk 'BEGIN{ FS=OFS="," }NR>1{ $2=sprintf("%.2f",$2) }1' | \ - column -t -s ',' + --account-name ${storage_account_name} -o json | + jq -r '["landing zone", "size in Kb", "last modification"], (.[] | [.name, .properties.contentLength / 1024, .properties.lastModified]) | @csv' | + awk 'BEGIN{ FS=OFS="," }NR>1{ $2=sprintf("%.2f",$2) }1' | + column -t -s ',' echo "" } @@ -309,7 +303,6 @@ function login_as_launchpad { export TF_VAR_tfstate_key=${TF_VAR_tf_name} - if [ ${caf_command} == "landingzone" ]; then if [ ${impersonate} = true ]; then @@ -347,29 +340,29 @@ function deploy_landingzone { RETURN_CODE=$? && echo "Terraform init return code ${RETURN_CODE}" case "${tf_action}" in - "plan") - echo "calling plan" - plan - ;; - "apply") - echo "calling plan and apply" - plan - apply - ;; - "validate") - echo "calling validate" - validate - ;; - "destroy") - echo "calling destroy" - destroy - ;; - "init") - echo "init no-op" - ;; - *) - other - ;; + "plan") + echo "calling plan" + plan + ;; + "apply") + echo "calling plan and apply" + plan + apply + ;; + "validate") + echo "calling validate" + validate + ;; + "destroy") + echo "calling destroy" + destroy + ;; + "init") + echo "init no-op" + ;; + *) + other + ;; esac rm -f "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" @@ -386,23 +379,23 @@ function workspace { get_storage_id if [ "${id}" == "null" ]; then - display_launchpad_instructions - exit 1000 + display_launchpad_instructions + exit 1000 fi case "${1}" in - "list") - workspace_list - ;; - "create") - workspace_create ${2} - ;; - "delete") - workspace_delete ${2} - ;; - *) - echo "launchpad workspace [ list | create | delete ]" - ;; + "list") + workspace_list + ;; + "create") + workspace_create ${2} + ;; + "delete") + workspace_delete ${2} + ;; + *) + echo "launchpad workspace [ list | create | delete ]" + ;; esac } @@ -417,13 +410,13 @@ function workspace_list { export storage_account_name=$(echo ${stg} | jq -r .name) echo " Listing workspaces:" - echo "" + echo "" az storage container list \ --subscription ${TF_VAR_tfstate_subscription_id} \ --auth-mode "login" \ - --account-name ${storage_account_name} -o json | \ - jq -r '["workspace", "last modification", "lease ststus"], (.[] | [.name, .properties.lastModified, .properties.leaseStatus]) | @csv' | \ - column -t -s ',' + --account-name ${storage_account_name} -o json | + jq -r '["workspace", "last modification", "lease ststus"], (.[] | [.name, .properties.lastModified, .properties.leaseStatus]) | @csv' | + column -t -s ',' echo "" } @@ -438,7 +431,7 @@ function workspace_create { export storage_account_name=$(echo ${stg} | jq -r .name) echo " Create $1 workspace" - echo "" + echo "" az storage container create \ --subscription ${TF_VAR_tfstate_subscription_id} \ --name $1 \ @@ -459,7 +452,7 @@ function workspace_delete { export storage_account_name=$(echo ${stg} | jq -r .name) echo " Delete $1 workspace" - echo "" + echo "" az storage container delete \ --subscription ${TF_VAR_tfstate_subscription_id} \ --name $1 \ @@ -492,7 +485,7 @@ function clean_up_variables { unset ARM_ENVIRONMENT echo "clean_up backend_files" - find /tf/caf -name backend.azurerm.tf -delete + find /tf/caf -name backend.azurerm.tf -delete } @@ -506,15 +499,15 @@ function get_resource_from_assignedIdentityInfo { fi case $msi in - *"MSIResource"*) - msiResource=${msi//MSIResource-} + *"MSIResource"*) + msiResource=${msi//MSIResource-/} ;; - *"MSIClient"*) - msiResource=$(az identity list --query "[?clientId=='${msi//MSIClient-}'].{id:id}" -o tsv) + *"MSIClient"*) + msiResource=$(az identity list --query "[?clientId=='${msi//MSIClient-/}'].{id:id}" -o tsv) ;; - *) - echo "Warning: MSI identifier unknown." - msiResource=${msi//MSIResource-} + *) + echo "Warning: MSI identifier unknown." + msiResource=${msi//MSIResource-/} ;; esac @@ -540,16 +533,16 @@ function export_azure_cloud_env { if [ -z "$cloud_name" ]; then case $AZURE_ENVIRONMENT in - AzureCloud) + AzureCloud) tf_cloud_env='public' ;; - AzureChinaCloud) + AzureChinaCloud) tf_cloud_env='china' ;; - AzureUSGovernment) + AzureUSGovernment) tf_cloud_env='usgovernment' ;; - AzureGermanCloud) + AzureGermanCloud) tf_cloud_env='german' ;; esac @@ -592,35 +585,35 @@ function get_logged_user_object_id { export keyvault=$(az keyvault list --subscription ${TF_VAR_tfstate_subscription_id} --query "[?tags.tfstate=='${TF_VAR_level}' && tags.environment=='${TF_VAR_environment}']" -o json | jq -r .[0].name) case "${clientId}" in - "systemAssignedIdentity") - if [ -z ${MSI_ID} ]; then - computerName=$(az rest --method get --headers Metadata=true --url http://169.254.169.254/metadata/instance?api-version=2020-09-01 | jq -r .compute.name) - principalId=$(az resource list -n ${computerName} --query [*].identity.principalId --out tsv) - echo " - logged in Azure with System Assigned Identity - computer name - ${computerName}" - export TF_VAR_logged_user_objectId=${principalId} - export ARM_TENANT_ID=$(az account show | jq -r .tenantId) - else - echo " - logged in Azure with System Assigned Identity - ${MSI_ID}" - export TF_VAR_logged_user_objectId=$(az identity show --ids ${MSI_ID} --query principalId -o tsv) - export ARM_TENANT_ID=$(az identity show --ids ${MSI_ID} --query tenantId -o tsv) - fi - ;; - "userAssignedIdentity") - msi=$(az account show | jq -r .user.assignedIdentityInfo) - echo " - logged in Azure with User Assigned Identity: ($msi)" - msiResource=$(get_resource_from_assignedIdentityInfo "$msi") - export TF_VAR_logged_aad_app_objectId=$(az identity show --ids $msiResource | jq -r .principalId) - export TF_VAR_logged_user_objectId=$(az identity show --ids $msiResource | jq -r .principalId) && echo " Logged in rover msi object_id: ${TF_VAR_logged_user_objectId}" - export ARM_CLIENT_ID=$(az identity show --ids $msiResource | jq -r .clientId) - export ARM_TENANT_ID=$(az identity show --ids $msiResource | jq -r .tenantId) - # set_arm_tenant_id_user_assigned_client // Not sure about this function - ;; - *) - # When connected with a service account the name contains the objectId - export TF_VAR_logged_aad_app_objectId=$(az ad sp show --id ${clientId} --query objectId -o tsv) && echo " Logged in rover app object_id: ${TF_VAR_logged_aad_app_objectId}" - export TF_VAR_logged_user_objectId=$(az ad sp show --id ${clientId} --query objectId -o tsv) && echo " Logged in rover app object_id: ${TF_VAR_logged_aad_app_objectId}" - echo " - logged in Azure AD application: $(az ad sp show --id ${clientId} --query displayName -o tsv)" - ;; + "systemAssignedIdentity") + if [ -z ${MSI_ID} ]; then + computerName=$(az rest --method get --headers Metadata=true --url http://169.254.169.254/metadata/instance?api-version=2020-09-01 | jq -r .compute.name) + principalId=$(az resource list -n ${computerName} --query [*].identity.principalId --out tsv) + echo " - logged in Azure with System Assigned Identity - computer name - ${computerName}" + export TF_VAR_logged_user_objectId=${principalId} + export ARM_TENANT_ID=$(az account show | jq -r .tenantId) + else + echo " - logged in Azure with System Assigned Identity - ${MSI_ID}" + export TF_VAR_logged_user_objectId=$(az identity show --ids ${MSI_ID} --query principalId -o tsv) + export ARM_TENANT_ID=$(az identity show --ids ${MSI_ID} --query tenantId -o tsv) + fi + ;; + "userAssignedIdentity") + msi=$(az account show | jq -r .user.assignedIdentityInfo) + echo " - logged in Azure with User Assigned Identity: ($msi)" + msiResource=$(get_resource_from_assignedIdentityInfo "$msi") + export TF_VAR_logged_aad_app_objectId=$(az identity show --ids $msiResource | jq -r .principalId) + export TF_VAR_logged_user_objectId=$(az identity show --ids $msiResource | jq -r .principalId) && echo " Logged in rover msi object_id: ${TF_VAR_logged_user_objectId}" + export ARM_CLIENT_ID=$(az identity show --ids $msiResource | jq -r .clientId) + export ARM_TENANT_ID=$(az identity show --ids $msiResource | jq -r .tenantId) + # set_arm_tenant_id_user_assigned_client // Not sure about this function + ;; + *) + # When connected with a service account the name contains the objectId + export TF_VAR_logged_aad_app_objectId=$(az ad sp show --id ${clientId} --query objectId -o tsv) && echo " Logged in rover app object_id: ${TF_VAR_logged_aad_app_objectId}" + export TF_VAR_logged_user_objectId=$(az ad sp show --id ${clientId} --query objectId -o tsv) && echo " Logged in rover app object_id: ${TF_VAR_logged_aad_app_objectId}" + echo " - logged in Azure AD application: $(az ad sp show --id ${clientId} --query displayName -o tsv)" + ;; esac fi @@ -636,33 +629,33 @@ function deploy { get_logged_user_object_id case ${id} in - "") - echo "No launchpad found." - if [ "${caf_command}" == "launchpad" ]; then - if [ -e "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" ]; then - echo "Recover from an un-finished previous execution" - if [ "${tf_action}" == "destroy" ]; then - destroy - else - initialize_state - fi + "") + echo "No launchpad found." + if [ "${caf_command}" == "launchpad" ]; then + if [ -e "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" ]; then + echo "Recover from an un-finished previous execution" + if [ "${tf_action}" == "destroy" ]; then + destroy else - rm -rf "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" - if [ "${tf_action}" == "destroy" ]; then - echo "There is no launchpad in this subscription" - else - echo "Deploying from scratch the launchpad" - rm -rf "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" - initialize_state - fi - exit + initialize_state fi else - error ${LINENO} "You need to initialise a launchpad first with the command \n - rover /tf/caf/landingzones/launchpad [plan | apply | destroy] -launchpad" 1000 + rm -rf "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" + if [ "${tf_action}" == "destroy" ]; then + echo "There is no launchpad in this subscription" + else + echo "Deploying from scratch the launchpad" + rm -rf "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" + initialize_state + fi + exit fi + else + error ${LINENO} "You need to initialise a launchpad first with the command \n + rover /tf/caf/landingzones/launchpad [plan | apply | destroy] -launchpad" 1000 + fi ;; - *) + *) # Get the launchpad version caf_launchpad=$(az storage account show --ids $id -o json | jq -r .tags.launchpad) @@ -686,7 +679,7 @@ function deploy { "destroy") destroy_from_remote_state ;; - "plan"|"apply"|"validate"|"import"|"output"|"taint"|"state list"|"state rm"|"state show") + "plan" | "apply" | "validate" | "import" | "output" | "taint" | "state list" | "state rm" | "state show") deploy_from_remote_state ;; *) @@ -697,7 +690,6 @@ function deploy { ;; esac - } function landing_zone { @@ -706,33 +698,33 @@ function landing_zone { get_storage_id case "${1}" in - "list") - echo "Listing the deployed landing zones" - list_deployed_landingzones - ;; - *) - echo "rover landingzone [ list ]" - ;; + "list") + echo "Listing the deployed landing zones" + list_deployed_landingzones + ;; + *) + echo "rover landingzone [ list ]" + ;; esac } function expand_tfvars_folder { - echo " Expanding variable files: ${1}/*.tfvars" + echo " Expanding variable files: ${1}/*.tfvars" - for filename in "${1}"/*.tfvars; do - if [ "${filename}" != "${1}/*.tfvars" ]; then - PARAMS+="-var-file ${filename} " - fi - done + for filename in "${1}"/*.tfvars; do + if [ "${filename}" != "${1}/*.tfvars" ]; then + PARAMS+="-var-file ${filename} " + fi + done - echo " Expanding variable files: ${1}/*.tfvars.json" + echo " Expanding variable files: ${1}/*.tfvars.json" - for filename in "${1}"/*.tfvars.json; do - if [ "${filename}" != "${1}/*.tfvars.json" ]; then - PARAMS+="-var-file ${filename} " - fi - done + for filename in "${1}"/*.tfvars.json; do + if [ "${filename}" != "${1}/*.tfvars.json" ]; then + PARAMS+="-var-file ${filename} " + fi + done } # @@ -764,8 +756,8 @@ function process_target_subscription { account=$(az account show -o json) - target_subscription_name=$( echo ${account} | jq -r .name) - target_subscription_id=$( echo ${account} | jq -r .id) + target_subscription_name=$(echo ${account} | jq -r .name) + target_subscription_id=$(echo ${account} | jq -r .id) export ARM_SUBSCRIPTION_ID=$(echo ${account} | jq -r .id) @@ -775,22 +767,21 @@ function process_target_subscription { export TF_VAR_tfstate_subscription_id=${ARM_SUBSCRIPTION_ID} fi - export target_subscription_name=$( echo ${account} | jq -r .name) - export target_subscription_id=$( echo ${account} | jq -r .id) + export target_subscription_name=$(echo ${account} | jq -r .name) + export target_subscription_id=$(echo ${account} | jq -r .id) echo "caf_command ${caf_command}" echo "target_subscription_id ${target_subscription_id}" echo "TF_VAR_tfstate_subscription_id ${TF_VAR_tfstate_subscription_id}" # Check if rover mode is set to launchpad - if [[ ( "${caf_command}" == "launchpad" ) && ( "${target_subscription_id}" != "${TF_VAR_tfstate_subscription_id}" ) ]]; then + if [[ ("${caf_command}" == "launchpad") && ("${target_subscription_id}" != "${TF_VAR_tfstate_subscription_id}") ]]; then error 51 "To deploy the launchpad, the target and tfstate subscription must be the same." fi echo "Resources from this landing zone are going to be deployed in the following subscription:" echo ${account} | jq -r - echo "debug: ${TF_VAR_tfstate_subscription_id}" tfstate_subscription_name=$(az account show -s ${TF_VAR_tfstate_subscription_id} --output json | jq -r .name) echo "Tfstates subscription set to ${TF_VAR_tfstate_subscription_id} (${tfstate_subscription_name})" diff --git a/scripts/rover.sh b/scripts/rover.sh index 09a5e51a..23507efa 100755 --- a/scripts/rover.sh +++ b/scripts/rover.sh @@ -1,11 +1,11 @@ #!/bin/bash - # Initialize the launchpad first with rover # deploy a landingzone with # rover -lz [landingzone_folder_name] -a [plan | apply | destroy] [parameters] source /tf/rover/clone.sh +source /tf/rover/walkthrough.sh source /tf/rover/tfstate_azurerm.sh source /tf/rover/functions.sh source /tf/rover/banner.sh @@ -36,157 +36,155 @@ current_path=$(pwd) mkdir -p ${TF_PLUGIN_CACHE_DIR} -while (( "$#" )); do - case "${1}" in - --clone-sample) - export caf_command="clone_sample" - echo "cloning sample" - shift 1 - ;; - --clone|--clone-branch|--clone-folder|--clone-destination|--clone-folder-strip) - export caf_command="clone" - process_clone_parameter $@ - shift 2 - ;; - -lz|--landingzone) - export caf_command="landingzone" - export landingzone_name=${2} - export TF_VAR_tf_name=${TF_VAR_tf_name:="$(basename ${landingzone_name}).tfstate"} - shift 2 - ;; - -c|--cloud) - export cloud_name=${2} - shift 2 - ;; - -d|--debug) - export debug_mode="true" - shift 1 - ;; - -a|--action) - export tf_action=${2} - shift 2 - ;; - --clone-launchpad) - export caf_command="clone" - export landingzone_branch=${landingzone_branch:="master"} - export clone_launchpad="true" - export clone_landingzone="false" - echo "cloning launchpad" - shift 1 - ;; - workspace) - shift 1 - export caf_command="workspace" - ;; - landingzone) - shift 1 - export caf_command="landingzone_mgmt" - ;; - login) - shift 1 - export caf_command="login" - ;; - ci) - shift 1 - export caf_command="ci" - export devops="true" - ;; - cd) - shift 1 - export caf_command="cd" - export devops="true" - ;; - -sc|--symphony-config) - export symphony_yaml_file=${2} - shift 2 - ;; - -ct|--ci-task-name) - export ci_task_name=${2} - export symphony_run_all_tasks=false - shift 2 - ;; - -b|--base-dir) - export base_directory=${2} - shift 2 - ;; - -tfc|--tfc) - shift 1 - export caf_command="tfc" - ;; - -t|--tenant) - export tenant=${2} - shift 2 - ;; - -s|--subscription) - export subscription=${2} - shift 2 - ;; - logout) - shift 1 - export caf_command="logout" - ;; - -tfstate) - export TF_VAR_tf_name=${2} - if [ ${TF_VAR_tf_name##*.} != "tfstate" ]; then - echo "tfstate name extension must be .tfstate" - exit 50 - fi - export TF_VAR_tf_plan="${TF_VAR_tf_name%.*}.tfplan" - shift 2 - ;; - -env|--environment) - export TF_VAR_environment=${2} - shift 2 - ;; - -launchpad) - export caf_command="launchpad" - shift 1 - ;; - -o|--output) - tf_output_file=${2} - shift 2 - ;; - -p|--plan) - tf_output_plan_file=${2} - shift 2 - ;; - -w|--workspace) - export TF_VAR_workspace=${2} - shift 2 - ;; - -l|-level) - export TF_VAR_level=${2} - shift 2 - ;; - --impersonate) - export impersonate=true - shift 1 - ;; - -skip-permission-check) - export skip_permission_check=true - shift 1 - ;; - -var-folder) - expand_tfvars_folder ${2} - shift 2 - ;; - -tfstate_subscription_id) - export TF_VAR_tfstate_subscription_id=${2} - shift 2 - ;; - -target_subscription) - export target_subscription=${2} - shift 2 - ;; - - *) # preserve positional arguments - PARAMS+="${1} " - shift - ;; - esac +while (("$#")); do + case "${1}" in + --walkthrough) + export caf_command="walkthrough" + shift 1 + ;; + --clone | --clone-branch | --clone-folder | --clone-destination | --clone-folder-strip) + export caf_command="clone" + process_clone_parameter $@ + shift 2 + ;; + -lz | --landingzone) + export caf_command="landingzone" + export landingzone_name=${2} + export TF_VAR_tf_name=${TF_VAR_tf_name:="$(basename ${landingzone_name}).tfstate"} + shift 2 + ;; + -c | --cloud) + export cloud_name=${2} + shift 2 + ;; + -d | --debug) + export debug_mode="true" + shift 1 + ;; + -a | --action) + export tf_action=${2} + shift 2 + ;; + --clone-launchpad) + export caf_command="clone" + export landingzone_branch=${landingzone_branch:="master"} + export clone_launchpad="true" + export clone_landingzone="false" + echo "cloning launchpad" + shift 1 + ;; + workspace) + shift 1 + export caf_command="workspace" + ;; + landingzone) + shift 1 + export caf_command="landingzone_mgmt" + ;; + login) + shift 1 + export caf_command="login" + ;; + ci) + shift 1 + export caf_command="ci" + export devops="true" + ;; + cd) + shift 1 + export caf_command="cd" + export devops="true" + ;; + -sc | --symphony-config) + export symphony_yaml_file=${2} + shift 2 + ;; + -ct | --ci-task-name) + export ci_task_name=${2} + export symphony_run_all_tasks=false + shift 2 + ;; + -b | --base-dir) + export base_directory=${2} + shift 2 + ;; + -tfc | --tfc) + shift 1 + export caf_command="tfc" + ;; + -t | --tenant) + export tenant=${2} + shift 2 + ;; + -s | --subscription) + export subscription=${2} + shift 2 + ;; + logout) + shift 1 + export caf_command="logout" + ;; + -tfstate) + export TF_VAR_tf_name=${2} + if [ ${TF_VAR_tf_name##*.} != "tfstate" ]; then + echo "tfstate name extension must be .tfstate" + exit 50 + fi + export TF_VAR_tf_plan="${TF_VAR_tf_name%.*}.tfplan" + shift 2 + ;; + -env | --environment) + export TF_VAR_environment=${2} + shift 2 + ;; + -launchpad) + export caf_command="launchpad" + shift 1 + ;; + -o | --output) + tf_output_file=${2} + shift 2 + ;; + -p | --plan) + tf_output_plan_file=${2} + shift 2 + ;; + -w | --workspace) + export TF_VAR_workspace=${2} + shift 2 + ;; + -l | -level) + export TF_VAR_level=${2} + shift 2 + ;; + --impersonate) + export impersonate=true + shift 1 + ;; + -skip-permission-check) + export skip_permission_check=true + shift 1 + ;; + -var-folder) + expand_tfvars_folder ${2} + shift 2 + ;; + -tfstate_subscription_id) + export TF_VAR_tfstate_subscription_id=${2} + shift 2 + ;; + -target_subscription) + export target_subscription=${2} + shift 2 + ;; + + *) # preserve positional arguments + PARAMS+="${1} " + shift + ;; + esac done - set -ETe trap 'error ${LINENO}' ERR 1 2 3 6 @@ -197,22 +195,25 @@ process_target_subscription echo "" echo "mode : '$(echo ${caf_command})'" -echo "terraform command output file : '$(echo ${tf_output_file})'" -echo "terraform plan output file : '$(echo ${tf_output_plan_file})'" -echo "tf_action : '$(echo ${tf_action})'" -echo "command and parameters : '$(echo ${tf_command})'" -echo "" -echo "level (current) : '$(echo ${TF_VAR_level})'" -echo "environment : '$(echo ${TF_VAR_environment})'" -echo "workspace : '$(echo ${TF_VAR_workspace})'" -echo "tfstate : '$(echo ${TF_VAR_tf_name})'" -echo "tfstate subscription id : '$(echo ${TF_VAR_tfstate_subscription_id})'" -echo "target subscription : '$(echo ${target_subscription_name})'" -echo "CI/CD enabled : '$(echo ${devops})'" -echo "Symphony Yaml file path : '$(echo ${symphony_yaml_file})'" -echo "Run all tasks : '$(echo ${symphony_run_all_tasks})'" + +if [ ${caf_command} != "walkthrough" ]; then + echo "terraform command output file : '$(echo ${tf_output_file})'" + echo "terraform plan output file : '$(echo ${tf_output_plan_file})'" + echo "tf_action : '$(echo ${tf_action})'" + echo "command and parameters : '$(echo ${tf_command})'" + echo "" + echo "level (current) : '$(echo ${TF_VAR_level})'" + echo "environment : '$(echo ${TF_VAR_environment})'" + echo "workspace : '$(echo ${TF_VAR_workspace})'" + echo "tfstate : '$(echo ${TF_VAR_tf_name})'" + echo "tfstate subscription id : '$(echo ${TF_VAR_tfstate_subscription_id})'" + echo "target subscription : '$(echo ${target_subscription_name})'" + echo "CI/CD enabled : '$(echo ${devops})'" + echo "Symphony Yaml file path : '$(echo ${symphony_yaml_file})'" + echo "Run all tasks : '$(echo ${symphony_run_all_tasks})'" +fi if [ $symphony_run_all_tasks == false ]; then - echo "Running task : '$(echo ${ci_task_name})'" + echo "Running task : '$(echo ${ci_task_name})'" fi echo "" diff --git a/scripts/tfstate_azurerm.sh b/scripts/tfstate_azurerm.sh index 35b79ee9..1d984cf9 100644 --- a/scripts/tfstate_azurerm.sh +++ b/scripts/tfstate_azurerm.sh @@ -1,4 +1,3 @@ - function initialize_state { echo "@calling initialize_state" @@ -23,45 +22,45 @@ function initialize_state { mkdir -p "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" case ${terraform_version} in - *"15"*) - echo "Terraform version 15" - terraform -chdir=${landingzone_name} \ - init \ - -upgrade=true - ;; - *) - terraform init \ - -get-plugins=true \ - -upgrade=true \ - ${landingzone_name} - ;; + *"15"*) + echo "Terraform version 15" + terraform -chdir=${landingzone_name} \ + init \ + -upgrade=true + ;; + *) + terraform init \ + -get-plugins=true \ + -upgrade=true \ + ${landingzone_name} + ;; esac RETURN_CODE=$? && echo "Line ${LINENO} - Terraform init return code ${RETURN_CODE}" case "${tf_action}" in - "plan") - echo "calling plan" - plan - ;; - "apply") - echo "calling plan and apply" - plan - apply - get_storage_id - upload_tfstate - ;; - "validate") - echo "calling validate" - validate - ;; - "destroy") - echo "No more tfstate file" - exit - ;; - *) - other - ;; + "plan") + echo "calling plan" + plan + ;; + "apply") + echo "calling plan and apply" + plan + apply + get_storage_id + upload_tfstate + ;; + "validate") + echo "calling validate" + validate + ;; + "destroy") + echo "No more tfstate file" + exit + ;; + *) + other + ;; esac rm -rf backend.azurerm.tf @@ -171,36 +170,35 @@ function destroy_from_remote_state { cd "${current_path}" } - function terraform_init_remote { case ${terraform_version} in - *"15"*) - echo "Terraform version 15" - terraform -chdir=${landingzone_name} \ - init \ - -reconfigure \ - -backend=true \ - -upgrade=true \ - -backend-config storage_account_name=${TF_VAR_tfstate_storage_account_name} \ - -backend-config resource_group_name=${TF_VAR_tfstate_resource_group_name} \ - -backend-config container_name=${TF_VAR_workspace} \ - -backend-config key=${TF_VAR_tf_name} \ - -backend-config subscription_id=${TF_VAR_tfstate_subscription_id} - ;; - *) - terraform init \ - -reconfigure=true \ - -backend=true \ - -get-plugins=true \ - -upgrade=true \ - -backend-config storage_account_name=${TF_VAR_tfstate_storage_account_name} \ - -backend-config resource_group_name=${TF_VAR_tfstate_resource_group_name} \ - -backend-config container_name=${TF_VAR_workspace} \ - -backend-config key=${TF_VAR_tf_name} \ - -backend-config subscription_id=${TF_VAR_tfstate_subscription_id} \ - ${landingzone_name} - ;; + *"15"*) + echo "Terraform version 15" + terraform -chdir=${landingzone_name} \ + init \ + -reconfigure \ + -backend=true \ + -upgrade=true \ + -backend-config storage_account_name=${TF_VAR_tfstate_storage_account_name} \ + -backend-config resource_group_name=${TF_VAR_tfstate_resource_group_name} \ + -backend-config container_name=${TF_VAR_workspace} \ + -backend-config key=${TF_VAR_tf_name} \ + -backend-config subscription_id=${TF_VAR_tfstate_subscription_id} + ;; + *) + terraform init \ + -reconfigure=true \ + -backend=true \ + -get-plugins=true \ + -upgrade=true \ + -backend-config storage_account_name=${TF_VAR_tfstate_storage_account_name} \ + -backend-config resource_group_name=${TF_VAR_tfstate_resource_group_name} \ + -backend-config container_name=${TF_VAR_workspace} \ + -backend-config key=${TF_VAR_tf_name} \ + -backend-config subscription_id=${TF_VAR_tfstate_subscription_id} \ + ${landingzone_name} + ;; esac } @@ -215,25 +213,24 @@ function plan { pwd mkdir -p "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" - rm -f $STDERR_FILE case ${terraform_version} in - *"15"*) - echo "Terraform version 15." - terraform -chdir=${landingzone_name} \ - plan ${tf_command} \ - -refresh=true \ - -detailed-exitcode \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ - -out="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} - ;; - *) - terraform plan ${tf_command} \ - -refresh=true \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ - -out="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" $PWD 2>$STDERR_FILE | tee ${tf_output_file} - ;; + *"15"*) + echo "Terraform version 15." + terraform -chdir=${landingzone_name} \ + plan ${tf_command} \ + -refresh=true \ + -detailed-exitcode \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ + -out="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} + ;; + *) + terraform plan ${tf_command} \ + -refresh=true \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ + -out="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" $PWD 2>$STDERR_FILE | tee ${tf_output_file} + ;; esac RETURN_CODE=$? && echo "Terraform plan return code: ${RETURN_CODE}" @@ -243,9 +240,8 @@ function plan { cp "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" "${tf_output_plan_file}" fi - if [ -s $STDERR_FILE ]; then - if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >> ${tf_output_file}; fi + if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >>${tf_output_file}; fi echo "Terraform returned errors:" cat $STDERR_FILE RETURN_CODE=2000 @@ -262,27 +258,25 @@ function apply { echo 'running terraform apply' rm -f $STDERR_FILE - case ${terraform_version} in - *"15"*) - echo "Terraform version 15." - terraform -chdir=${landingzone_name} \ - apply \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ - "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} - ;; - *) - terraform apply \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ - "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} - ;; + *"15"*) + echo "Terraform version 15." + terraform -chdir=${landingzone_name} \ + apply \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ + "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} + ;; + *) + terraform apply \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ + "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_plan}" 2>$STDERR_FILE | tee ${tf_output_file} + ;; esac - RETURN_CODE=$? && echo "Terraform apply return code: ${RETURN_CODE}" if [ -s $STDERR_FILE ]; then - if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >> ${tf_output_file}; fi + if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >>${tf_output_file}; fi echo "Terraform returned errors:" cat $STDERR_FILE RETURN_CODE=2001 @@ -303,7 +297,7 @@ function validate { RETURN_CODE=$? && echo "Terraform validate return code: ${RETURN_CODE}" if [ -s $STDERR_FILE ]; then - if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >> ${tf_output_file}; fi + if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >>${tf_output_file}; fi echo "Terraform returned errors:" cat $STDERR_FILE RETURN_CODE=2002 @@ -326,7 +320,6 @@ function destroy { echo " -TF_VAR_workspace: ${TF_VAR_workspace}" echo " -TF_VAR_tf_name: ${TF_VAR_tf_name}" - rm -f "${TF_DATA_DIR}/terraform.tfstate" sudo rm -f ${landingzone_name}/backend.azurerm.tf @@ -346,19 +339,19 @@ function destroy { RETURN_CODE=$? && echo "Line ${LINENO} - Terraform init return code ${RETURN_CODE}" case ${terraform_version} in - *"15"*) - echo "Terraform version 15." - terraform -chdir=${landingzone_name} \ - destroy \ - -refresh=false \ - ${tf_command} - ;; - *) - terraform destroy \ - -refresh=false \ - ${tf_command} \ - ${landingzone_name} - ;; + *"15"*) + echo "Terraform version 15." + terraform -chdir=${landingzone_name} \ + destroy \ + -refresh=false \ + ${tf_command} ${tf_approve} + ;; + *) + terraform destroy \ + -refresh=false \ + ${tf_command} \ + ${landingzone_name} ${tf_approve} + ;; esac RETURN_CODE=$? @@ -378,22 +371,21 @@ function destroy { unset ARM_CLIENT_SECRET fi - case ${terraform_version} in - *"15"*) - echo "Terraform version 15." - terraform -chdir=${landingzone_name} \ - init \ - -reconfigure=true \ - -upgrade=true - ;; - *) - terraform init \ - -reconfigure=true \ - -get-plugins=true \ - -upgrade=true \ - ${landingzone_name} - ;; + *"15"*) + echo "Terraform version 15." + terraform -chdir=${landingzone_name} \ + init \ + -reconfigure=true \ + -upgrade=true + ;; + *) + terraform init \ + -reconfigure=true \ + -get-plugins=true \ + -upgrade=true \ + ${landingzone_name} + ;; esac RETURN_CODE=$? && echo "Line ${LINENO} - Terraform init return code ${RETURN_CODE}" @@ -402,19 +394,19 @@ function destroy { mkdir -p "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}" case ${terraform_version} in - *"15"*) - echo "Terraform version 15." - terraform -chdir=${landingzone_name} \ - destroy ${tf_command} \ - -refresh=false \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" - ;; - *) - terraform destroy ${tf_command} \ - -refresh=false \ - -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ - ${landingzone_name} - ;; + *"15"*) + echo "Terraform version 15." + terraform -chdir=${landingzone_name} \ + destroy ${tf_command} ${tf_approve} \ + -refresh=false \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" + ;; + *) + terraform destroy ${tf_command} ${tf_approve} \ + -refresh=false \ + -state="${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" \ + ${landingzone_name} + ;; esac RETURN_CODE=$? @@ -423,7 +415,6 @@ function destroy { fi fi - echo "Removing ${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" rm -f "${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace}/${TF_VAR_tf_name}" get_storage_id @@ -432,7 +423,7 @@ function destroy { echo "Delete state file on storage account:" echo " -tfstate: ${TF_VAR_tf_name}" stg_name=$(az storage account show \ - --ids ${id} -o json | \ + --ids ${id} -o json | jq -r .name) && echo " -stg_name: ${stg_name}" fileExists=$(az storage blob exists \ @@ -440,7 +431,7 @@ function destroy { --name ${TF_VAR_tf_name} \ --container-name ${TF_VAR_workspace} \ --auth-mode login \ - --account-name ${stg_name} -o json | \ + --account-name ${stg_name} -o json | jq .exists) if [ "${fileExists}" == "true" ]; then @@ -456,7 +447,7 @@ function destroy { fi fi - rm -rf ${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace} + rm -rf ${TF_DATA_DIR}/tfstates/${TF_VAR_level}/${TF_VAR_workspace} clean_up_variables } @@ -475,7 +466,7 @@ function other { RETURN_CODE=$? && echo "Terraform ${tf_action} return code: ${RETURN_CODE}" if [ -s $STDERR_FILE ]; then - if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >> ${tf_output_file}; fi + if [ ${tf_output_file+x} ]; then cat $STDERR_FILE >>${tf_output_file}; fi echo "Terraform returned errors:" cat $STDERR_FILE RETURN_CODE=2003 diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh new file mode 100644 index 00000000..2763a8a5 --- /dev/null +++ b/scripts/walkthrough.sh @@ -0,0 +1,240 @@ +#!/bin/bash + +export walkthrough_path="/tf/caf/walkthrough" +export config_name="demo" + +function execute_walkthrough { + # clone_landing_zones + + # clone_configurations + + # select_walkthrough_config + + generate_walkthrough_assets + + # execute_deployments +} + +function clone_landing_zones { + echo -e "\n******************************* Rover Walkthrough *******************************" + echo "Cloning Logic repository" + echo " - https://github.com/azure/caf-terraform-landingzones" + echo "" + echo "Step one, download the logic repository. This exposes terraform modules for the launchpad and solution landing zones." + echo -n "Ready to proceed? (y/n): " + read proceed + check_exit_case $proceed + + rm -rf ${walkthrough_path} + + set_clone_exports "${walkthrough_path}/landingzones" "/caf_launchpad" "1" "Azure/caf-terraform-landingzones" "master" + clone_repository + + set_clone_exports "${walkthrough_path}/landingzones" "/caf_solution" "1" "Azure/caf-terraform-landingzones" "master" + clone_repository + echo_section_break +} + +function clone_configurations { + echo "Cloning Configuration repository" + echo " - https://github.com/Azure/caf-terraform-landingzones-starter" + echo "" + echo "Step two, download the configuration repository. These contain terraform configuration files for the modules you want to create." + echo "These are organized by levels for the proper enterprise seperation of concerns." + echo "Read more about the levels here: https://github.com/Azure/caf-terraform-landingzones/blob/master/documentation/code_architecture/hierarchy.md" + echo -n "Ready to proceed? (y/n): " + read proceed + check_exit_case $proceed + + # set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "Azure/caf-terraform-landingzones-starter" "starter" + set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "davesee/caf-terraform-landingzones-starter" "standard_config_tfstate" + clone_repository + echo_section_break +} + +function select_walkthrough_config { + echo "The following configurations were found in the starter repo:" + d=$(pwd) + cd ${walkthrough_path}/configuration/ + ls -d */ | sort | sed 's/\///' + cd $d + echo "" + + config="" + echo -n "Select one configuration for this walkthrough or 'end' to exit (configuration name): " + + while [ -z $config ]; do + read config + + if [ $config == "end" ]; then + echo "Goodbye!" + exit 0 + + elif [ -d "${walkthrough_path}/configuration/$config/" ]; then + echo "" + echo "Found configuration, removing the others..." + find /tf/caf/walkthrough/configuration -maxdepth 1 -mindepth 1 -type d ! -name $config + find /tf/caf/walkthrough/configuration -maxdepth 1 -mindepth 1 -type d ! -name $config -exec rm -rf {} + + + export config_name=$config + echo_section_break + else + echo "Configuration '$config' not found, please try again (configuration name): " + fi + done +} + +function generate_walkthrough_assets { + echo -n "Would you like to generate the deployment/destroy scripts and README instructions? (y/n): " + read proceed + check_exit_case $proceed + + rm -f ${walkthrough_path}/deploy.sh ${walkthrough_path}/destroy.sh ${walkthrough_path}/README.md + + write_md "# Deployment Steps" + init_sh + + index=0 + for path in $(find ${walkthrough_path}/configuration/${config_name} -type f \( -name "configuration.tfvars" -o -name "landingzone.tfvars" \) | sort); do + config_array[$index]="$(get_level_string $path):$path" + ((index += 1)) + done + + IFS=$'\n' sorted_ascending=($(sort <<<"${config_array[*]}")) + unset IFS + + for i in "${sorted_ascending[@]}"; do + level_a=$(echo $i | awk 'BEGIN {FS=":"}{print $1}') + config_a=$(echo $i | awk 'BEGIN {FS=":"}{print $2}') + echo "rover commands generated for $level_a deployment for $(basename ${config_a%\/*})" + + write_doc ${config_a%\/*} + write_bash_apply ${config_a%\/*} + done + + IFS=$'\n' sorted_descending=($(sort -r <<<"${config_array[*]}")) + unset IFS + + for i in "${sorted_descending[@]}"; do + level_d=$(echo $i | awk 'BEGIN {FS=":"}{print $1}') + config_d=$(echo $i | awk 'BEGIN {FS=":"}{print $2}') + write_bash_destroy ${config_d%\/*} + done + + end_sh +} + +function execute_deployments { + echo "" + echo "Deployment script and instructions generated!" + echo_section_break + echo "You can follow the README instructions to deploy one configuration at a time or run deploy.sh to deploy ALL configurations in sequence." + echo "Please note the base folder names are used to name the *.tfstate files and must match the configuration landingzone.tf references." + echo "${walkthrough_path}/deploy.sh" + echo "${walkthrough_path}/README.md" + echo "" + echo "This deployment creates all resources in the currently logged in Azure subscription" + echo "and is the final step, but will take a while to run." + echo -n "Ready to proceed? (y/n): " + read proceed + check_exit_case $proceed + + bash ${walkthrough_path}/deploy.sh & +} + +function get_level_string { + var_folder=$1 + + start=$(echo $var_folder | grep -b -o "level" | awk 'BEGIN {FS=":"}{print $1}') + level="level${var_folder:$(expr $start + 5):1}" + + echo $level +} + +function write_doc { + local var_folder=$1 + + local level=$(get_level_string "$var_folder") + + write_md "## Deploy $level $(basename $var_folder)" + write_md "\`\`\`bash" + write_md "rover \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_md " -launchpad \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_md " -lz ${walkthrough_path}/landingzones/caf_launchpad \\" || write_md " -lz ${walkthrough_path}/landingzones/caf_solution \\" + write_md " -var-folder $var_folder \\" + write_md " -tfstate $(basename $var_folder).tfstate \\" + write_md " -level $level \\" + write_md " -env ${config_name} \\" + write_md " -a [ apply | destroy | plan ]" + write_md "\`\`\`" +} + +function write_bash_apply { + local var_folder=$1 + + local level=$(get_level_string "$var_folder") + + write_sh "# Deploy $level $(basename $var_folder)" + write_sh "bash /tf/rover/rover.sh \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_sh "-lz ${walkthrough_path}/landingzones/caf_launchpad \\" || write_sh "-lz ${walkthrough_path}/landingzones/caf_solution \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_sh "-launchpad \\" + write_sh "-var-folder $var_folder \\" + write_sh "-tfstate $(basename $var_folder).tfstate \\" + write_sh "-level $level \\" + write_sh "-env ${config_name} \\" + write_sh "-a apply &" + write_sh "wait\n" +} + +function write_bash_destroy { + local var_folder=$1 + + local level=$(get_level_string "$var_folder") + + write_sh_destroy "# Deploy $level $(basename $var_folder)" + write_sh_destroy "bash /tf/rover/rover.sh \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_sh_destroy "-lz ${walkthrough_path}/landingzones/caf_launchpad \\" || write_sh_destroy "-lz ${walkthrough_path}/landingzones/caf_solution \\" + [[ $(basename $var_folder) = "launchpad" ]] && write_sh_destroy "-launchpad \\" + write_sh_destroy "-var-folder $var_folder \\" + write_sh_destroy "-tfstate $(basename $var_folder).tfstate \\" + write_sh_destroy "-level $level \\" + write_sh_destroy "-env ${config_name} \\" + write_sh_destroy "-a destroy &" + write_sh_destroy "wait\n" +} + +function init_sh { + write_sh "#!/bin/bash\n\nfunction main {\n" + write_sh_destroy "#!/bin/bash\n\nexport tf_approve=--auto-approve\n\nfunction main {\n" +} + +function end_sh { + write_sh "}\n\nmain" + write_sh_destroy "}\n\nmain" + chmod +x ${walkthrough_path}/deploy.sh ${walkthrough_path}/destroy.sh +} + +function write_md { + echo -e $1 >>${walkthrough_path}/README.md +} + +function write_sh { + echo -e $1 >>${walkthrough_path}/deploy.sh +} + +function write_sh_destroy { + echo -e $1 >>${walkthrough_path}/destroy.sh +} + +function echo_section_break { + echo -e "\n*********************************************************************************" +} + +function check_exit_case { + local proceed=$1 + + if [ $proceed != "y" ]; then + echo "Goodbye!" + exit 0 + fi +} From f0319036b655fffe9d1cff0cf4b2d6d16c901af7 Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Wed, 2 Jun 2021 20:32:11 -0400 Subject: [PATCH 3/7] Updated branch --- scripts/functions.sh | 11 ----------- scripts/walkthrough.sh | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/scripts/functions.sh b/scripts/functions.sh index fe916563..d6476597 100644 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -38,17 +38,6 @@ success() { printf "\e[32m$@\n\e[0m" } -exit_if_error() { - local exit_code=$1 - shift - [[ $exit_code ]] && # do nothing if no error code passed - ((exit_code != 0)) && { # do nothing if error code is 0 - printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here - exit "$exit_code" # we could also check to make sure - # error code is numeric when passed - } -} - # # Execute a command and re-execute it with a backoff retry logic. This is mainly to handle throttling situations in CI # diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh index 2763a8a5..33e93783 100644 --- a/scripts/walkthrough.sh +++ b/scripts/walkthrough.sh @@ -47,7 +47,7 @@ function clone_configurations { check_exit_case $proceed # set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "Azure/caf-terraform-landingzones-starter" "starter" - set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "davesee/caf-terraform-landingzones-starter" "standard_config_tfstate" + set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "davesee/caf-terraform-landingzones-starter" "walkthrough" clone_repository echo_section_break } From d568da34ae40cfd743c673a59f2dcf4146b7d24a Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Wed, 2 Jun 2021 20:39:33 -0400 Subject: [PATCH 4/7] Updated Docker to add walkthrough.sh --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index b1ebd443..d9165527 100644 --- a/Dockerfile +++ b/Dockerfile @@ -277,6 +277,7 @@ COPY ./scripts/tfstate_azurerm.sh . COPY ./scripts/functions.sh . COPY ./scripts/banner.sh . COPY ./scripts/clone.sh . +COPY ./scripts/walkthrough.sh . COPY ./scripts/sshd.sh . COPY ./scripts/backend.hcl.tf . COPY ./scripts/ci.sh . From b10350089f01ca21df041c36be57ec40d36ee3ad Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Thu, 3 Jun 2021 12:19:54 -0400 Subject: [PATCH 5/7] Updated walkthrough steps --- scripts/walkthrough.sh | 85 +++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh index 33e93783..878a8bc0 100644 --- a/scripts/walkthrough.sh +++ b/scripts/walkthrough.sh @@ -4,23 +4,41 @@ export walkthrough_path="/tf/caf/walkthrough" export config_name="demo" function execute_walkthrough { - # clone_landing_zones + clone_landing_zones - # clone_configurations + clone_configurations - # select_walkthrough_config + select_walkthrough_config generate_walkthrough_assets - # execute_deployments + execute_deployments } function clone_landing_zones { - echo -e "\n******************************* Rover Walkthrough *******************************" - echo "Cloning Logic repository" + echo_section_break + echo "*************************************** Rover Walkthrough ***************************************" + echo "*************************************************************************************************" + echo "" + echo "This CAF Rover Walkthrough will guide you through a complete starter solution deployment." + echo "" + echo "Overview of steps:" + echo " 1. Download logic landingzones (Terraform modules)" + echo " 2. Download configuration files (Platform and Solution deployment specifications)" + echo " 3. Rover command generation (deploy and destroy)" + echo " 4. Deploy to Azure!" + echo "" + echo -n "Ready to get started? (y/n): " + read proceed + check_exit_case $proceed + echo_section_break + echo "" + echo "Step 1 - Download the logic repository. This exposes terraform modules for the launchpad and solution " + echo "landingzones. You can download this directly at any time and reuse for multiple deployments." + echo "" + echo "Download logic repository for walkthrough" echo " - https://github.com/azure/caf-terraform-landingzones" echo "" - echo "Step one, download the logic repository. This exposes terraform modules for the launchpad and solution landing zones." echo -n "Ready to proceed? (y/n): " read proceed check_exit_case $proceed @@ -36,12 +54,19 @@ function clone_landing_zones { } function clone_configurations { - echo "Cloning Configuration repository" + echo "" + echo "Step 2 - Download the configuration repository. These contain terraform configuration files for the solution" + echo "sets you want to create and are organized by levels for the proper enterprise seperation of concerns." + echo "" + echo "Learn more about levels" + echo " - https://github.com/Azure/caf-terraform-landingzones/blob/master/documentation/code_architecture/hierarchy.md" + echo "" + echo "Examples to follow" + echo " - https://github.com/aztfmod/terraform-azurerm-caf/tree/master/examples" + echo "" + echo "Download configuration starter CAF repository for walkthrough" echo " - https://github.com/Azure/caf-terraform-landingzones-starter" echo "" - echo "Step two, download the configuration repository. These contain terraform configuration files for the modules you want to create." - echo "These are organized by levels for the proper enterprise seperation of concerns." - echo "Read more about the levels here: https://github.com/Azure/caf-terraform-landingzones/blob/master/documentation/code_architecture/hierarchy.md" echo -n "Ready to proceed? (y/n): " read proceed check_exit_case $proceed @@ -53,7 +78,7 @@ function clone_configurations { } function select_walkthrough_config { - echo "The following configurations were found in the starter repo:" + echo "The following configurations were found in the starter repo, enter one for the walkthrough." d=$(pwd) cd ${walkthrough_path}/configuration/ ls -d */ | sort | sed 's/\///' @@ -61,7 +86,7 @@ function select_walkthrough_config { echo "" config="" - echo -n "Select one configuration for this walkthrough or 'end' to exit (configuration name): " + echo -n "Configuration name: " while [ -z $config ]; do read config @@ -85,7 +110,15 @@ function select_walkthrough_config { } function generate_walkthrough_assets { - echo -n "Would you like to generate the deployment/destroy scripts and README instructions? (y/n): " + echo "" + echo "Step 3 - Generate the deployment and destroy scripts. These rover commands use the landingzone modules" + echo "and configuration tfvars files to execute Terraform init, plan, apply and destroy commands for you." + echo "" + echo "The launchpad in the Platform / Level 0 configuraiton is always deployed first and is used for remote" + echo "state storage. This pattern creates a self contained environment. State is also available between levels" + echo "as defined in each configuration's landingzone.tfvars global_settings_key and tfstates values." + echo "" + echo -n "Ready to proceed? (y/n): " read proceed check_exit_case $proceed @@ -122,19 +155,29 @@ function generate_walkthrough_assets { done end_sh + echo "" + echo "Deployment scripts and instructions generated!" + echo_section_break } function execute_deployments { echo "" - echo "Deployment script and instructions generated!" - echo_section_break - echo "You can follow the README instructions to deploy one configuration at a time or run deploy.sh to deploy ALL configurations in sequence." - echo "Please note the base folder names are used to name the *.tfstate files and must match the configuration landingzone.tf references." + echo "Step 4 - Deploy to Azure. The generated bash script will deploy ALL configuration levels in order" + echo "for you. In an enterprise deployment, the platform configurations may be stored and deployed" + echo "in a repo with a pipeline of its own while solution configuration repos keep changes focused, " + echo "allow greater frequency of deployment and require lower level of permission." echo "${walkthrough_path}/deploy.sh" + echo "" + echo "The destroy script removes the Azure configuration levels in reverse order. You can run this" + echo "manually at any time after the deployment to remove the Azure resources." + echo "${walkthrough_path}/destroy.sh" + echo "" + echo "You can also stop here and follow the README.md instructions to deploy individual configurations." echo "${walkthrough_path}/README.md" echo "" - echo "This deployment creates all resources in the currently logged in Azure subscription" - echo "and is the final step, but will take a while to run." + echo "The deployment script creates ALL resources in the currently logged in Azure subscription" + echo "and is the final step, but may take up to an hour to run." + echo "" echo -n "Ready to proceed? (y/n): " read proceed check_exit_case $proceed @@ -227,7 +270,7 @@ function write_sh_destroy { } function echo_section_break { - echo -e "\n*********************************************************************************" + echo -e "\n*************************************************************************************************" } function check_exit_case { From c1adfc22895e22ff000b6267f2c4ac25e54d4297 Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Thu, 3 Jun 2021 18:27:12 -0400 Subject: [PATCH 6/7] Spellchk --- scripts/walkthrough.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh index 878a8bc0..22c499f1 100644 --- a/scripts/walkthrough.sh +++ b/scripts/walkthrough.sh @@ -114,7 +114,7 @@ function generate_walkthrough_assets { echo "Step 3 - Generate the deployment and destroy scripts. These rover commands use the landingzone modules" echo "and configuration tfvars files to execute Terraform init, plan, apply and destroy commands for you." echo "" - echo "The launchpad in the Platform / Level 0 configuraiton is always deployed first and is used for remote" + echo "The launchpad in the Platform / Level 0 configuration is always deployed first and is used for remote" echo "state storage. This pattern creates a self contained environment. State is also available between levels" echo "as defined in each configuration's landingzone.tfvars global_settings_key and tfstates values." echo "" @@ -162,20 +162,20 @@ function generate_walkthrough_assets { function execute_deployments { echo "" - echo "Step 4 - Deploy to Azure. The generated bash script will deploy ALL configuration levels in order" + echo "Step 4 - Deploy to Azure. The generated bash script will deploy ALL levels ascending in order" echo "for you. In an enterprise deployment, the platform configurations may be stored and deployed" - echo "in a repo with a pipeline of its own while solution configuration repos keep changes focused, " + echo "in a repo with a pipeline of its own while solution application repos keep changes focused, " echo "allow greater frequency of deployment and require lower level of permission." echo "${walkthrough_path}/deploy.sh" echo "" - echo "The destroy script removes the Azure configuration levels in reverse order. You can run this" + echo "The destroy script removes the Azure levels and resources in reverse order. You can run this" echo "manually at any time after the deployment to remove the Azure resources." echo "${walkthrough_path}/destroy.sh" echo "" echo "You can also stop here and follow the README.md instructions to deploy individual configurations." echo "${walkthrough_path}/README.md" echo "" - echo "The deployment script creates ALL resources in the currently logged in Azure subscription" + echo "This deployment script creates ALL resources in the currently logged in Azure subscription" echo "and is the final step, but may take up to an hour to run." echo "" echo -n "Ready to proceed? (y/n): " From 22a126b3bcec4759f3418337531eb64b62773dcf Mon Sep 17 00:00:00 2001 From: Dave Seepersad <“davesee@microsoft.com”> Date: Fri, 11 Jun 2021 12:40:11 -0400 Subject: [PATCH 7/7] Updated source repo to main starter proj --- scripts/walkthrough.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/walkthrough.sh b/scripts/walkthrough.sh index 22c499f1..ec9f557d 100644 --- a/scripts/walkthrough.sh +++ b/scripts/walkthrough.sh @@ -71,14 +71,14 @@ function clone_configurations { read proceed check_exit_case $proceed - # set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "Azure/caf-terraform-landingzones-starter" "starter" - set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "davesee/caf-terraform-landingzones-starter" "walkthrough" + set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "Azure/caf-terraform-landingzones-starter" "starter" + # set_clone_exports "${walkthrough_path}/configuration" "/configuration" "2" "davesee/caf-terraform-landingzones-starter" "walkthrough" clone_repository echo_section_break } function select_walkthrough_config { - echo "The following configurations were found in the starter repo, enter one for the walkthrough." + echo "The following configurations were found in the starter repo. Currenlty ONLY demo works with the walkthrough. This was accomplished by standardizing the tfstate file names to match the containing folder name." d=$(pwd) cd ${walkthrough_path}/configuration/ ls -d */ | sort | sed 's/\///' @@ -86,7 +86,7 @@ function select_walkthrough_config { echo "" config="" - echo -n "Configuration name: " + echo -n "Enter 'demo' to confirm this configuration: " while [ -z $config ]; do read config