Last active
October 2, 2020 11:46
-
-
Save gcchaan/ad8fd83a68467503ec3e6392ebbd519a to your computer and use it in GitHub Desktop.
troposphere runner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# https://gist.github.com/gcchaan/ad8fd83a68467503ec3e6392ebbd519a | |
set -ef -o pipefail | |
function message(){ | |
echo 🍣 "$1" | |
} | |
function help(){ | |
cat <<- EOF | |
Usage: | |
cfn_run.sh [OPTIONS] TEMPLATE_FILE | |
Options: | |
-r region | |
-c leave change-set | |
-s TODO: silent mode | |
-h help | |
Examples: | |
run: | |
run.sh -r us-northeast-1 stack.py | |
EOF | |
exit 0 | |
} | |
function set_region(){ | |
readonly master_regions=($(aws ec2 describe-regions | jq -r ".Regions[].RegionName | @text")) | |
for master_region in "${master_regions[@]}"; do | |
[[ "$master_region" == "$OPTARG" ]] && region_exists=true | |
done | |
if ${region_exists:-false}; then | |
readonly region_from_option=$OPTARG | |
message "set region" | |
echo ${region_from_option} | |
else | |
echo "Could not find region '$OPTARG'." | |
echo "You could choose from below." | |
echo "${master_regions[@]}" | |
exit 1 | |
fi | |
} | |
while getopts r:c:h opt; do | |
case "$opt" in | |
r) set_region $OPTARG;; | |
c) readonly leaving_change_set=true;; | |
h) help;; | |
\?) exit 1;; | |
esac | |
done | |
shift $((--OPTIND)) | |
readonly dir=$(cd "$(dirname "$0")" && pwd) | |
readonly json_file="$dir"/tmp.json | |
readonly region=${region_from_option:-ap-northeast-1} | |
readonly aws_command="aws --region $region" | |
readonly aws_cfn_command="$aws_command cloudformation" | |
if [[ -f "$1" ]]; then | |
template_file=$1 | |
stack_name=$(echo $template_file | sed s/.py// | sed s/_/-/g) | |
else | |
echo "Could not find file '$1'"; exit 1 | |
fi | |
function set_notification(){ | |
message "set notification" | |
readonly topic_arn=$($aws_command sns list-topics | jq -r '.Topics[].TopicArn') | |
readonly topic_name=$(echo $topic_arn | sed 's/.*\://') | |
if [[ $topic_name == "SlackTopic" ]]; then | |
readonly notification_flag="--notification-arns $topic_arn " | |
echo "$topic_arn" | |
else | |
readonly notification_flag="" | |
echo "no setting." | |
fi | |
} | |
function remove_local_json(){ | |
message "removing local temporary file" | |
rm "$1" | |
} | |
function display_change_set_url(){ | |
message "change set URL here." | |
[[ -z $1 ]] && echo "arn could not find." && return | |
local _change_set_arn=$1 | |
echo "https://${region}.console.aws.amazon.com/cloudformation/home?region=${region}#/changeset/detail?changeSetId=${_change_set_arn}" | |
} | |
function delete_change_set(){ | |
message "delete change set" | |
[[ -z $1 ]] && echo "arn could not find." && return | |
local _change_set_arn=$1 | |
if ${leaving_change_set:-false}; then | |
echo "skipped." | |
else | |
$aws_command cloudformation delete-change-set --change-set-name $_change_set_arn | |
echo "change set is deleted. if you would leave it, use '-c'." | |
fi | |
} | |
function display_stack_url(){ | |
message "Management console URL here." | |
[[ -z $1 ]] && echo "arn could not find." && return | |
local _stack_arn=$1 | |
echo "https://${region}.console.aws.amazon.com/cloudformation/home?region=${region}#/stack/detail?stackId=${_stack_arn}" | |
} | |
function confirm_prompt(){ | |
echo | |
message "execute? (Y/n): " | |
read answer | |
case $answer in | |
y|Y|yes) | |
echo -e "tyeped yes.\n" | |
return 0 | |
;; | |
n|N|no) | |
echo -e "tyeped no.\n" | |
remove_local_json $json_file | |
return 1 | |
;; | |
*) | |
echo -e "cannot understand $answer.\n" | |
confirm_prompt | |
;; | |
esac | |
} | |
function update_stack(){ | |
message "creating change-set" | |
readonly change_set=$( | |
$aws_cfn_command create-change-set \ | |
--change-set-name ${stack_name}-$(date "+%Y%m%d%H%M%S") \ | |
--stack-name ${stack_name} \ | |
--template-body file://${json_file} \ | |
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM) | |
echo ${change_set} | jq | |
readonly stack_arn=$(echo ${change_set} | jq -r '.StackId') | |
readonly change_set_arn=$(echo ${change_set} | jq -r '.Id') | |
sleep 1 | |
while true; do | |
change_set_json=$($aws_cfn_command describe-change-set --change-set-name $change_set_arn) | |
change_set_status=$(echo $change_set_json | jq '.Status') | |
case $change_set_status in | |
'"CREATE_COMPLETE"') | |
message "describe change-set" | |
echo ${change_set_json} | jq | |
display_change_set_url "$change_set_arn" | |
confirm_prompt | |
message "update stack" | |
$aws_cfn_command update-stack \ | |
--stack-name "$stack_name" \ | |
--template-body file://"$json_file" \ | |
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \ | |
$notification_flag | |
break;; | |
'"FAILED"') | |
message "describe change-set" | |
echo ${change_set_json} | jq | |
message "Error occured. show 'StatusReason' of change-set here." | |
echo ${change_set_json} | jq '.StatusReason' | |
break;; | |
* ) | |
sleep 1 | |
echo "status still ${change_set_status}..." ;; | |
esac | |
done | |
delete_change_set "$change_set_arn" | |
display_stack_url "$stack_arn" | |
} | |
function create_stack(){ | |
message "creation preview" | |
cat $json_file | jq | |
confirm_prompt | |
message "create stack" | |
readonly create_stack_result_json=$($aws_cfn_command create-stack \ | |
--stack-name "$stack_name" \ | |
--template-body file://"$json_file" \ | |
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \ | |
$notification_flag) | |
readonly stack_arn=$(echo $create_stack_result_json | jq -r '.StackId') | |
echo "executed." | |
display_stack_url "$stack_arn" | |
} | |
message "flake8 linting" | |
flake8 $template_file | |
message "converting json" | |
python $template_file > "$json_file" | |
message "checking json syntax" | |
$aws_cfn_command validate-template --template-body file://"$json_file" | jq | |
message "fetch list" | |
readonly stacks=($($aws_cfn_command list-stacks \ | |
--query 'StackSummaries[?StackStatus!=`DELETE_COMPLETE`]' \ | |
| jq -r '.[].StackName')) | |
echo ${stacks[@]} | |
stack_exists=false | |
for stack in "${stacks[@]}"; do | |
[[ "$stack" == "$stack_name" ]] && stack_exists=true | |
done | |
set_notification | |
if "$stack_exists"; then | |
update_stack | |
else | |
create_stack | |
fi | |
remove_local_json $json_file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment