Created
          September 11, 2017 00:13 
        
      - 
      
- 
        Save stevenringo/2aa8485706677c0a92320f17c1efb3a8 to your computer and use it in GitHub Desktop. 
Revisions
- 
        stevenringo created this gist Sep 11, 2017 .There are no files selected for viewingThis file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,209 @@ set -o pipefail _exit_error() { message=$1 code=$2 echo "$message" >&2 exit $code } _exit_ok() { message=$1 echo "$message" exit 0 } get-stack-state() { local stack_name=$1 local region=$2 local stack_state stack_state=$(aws cloudformation describe-stacks --region $region \ --stack-name $stack_name \ --query "Stacks[0].StackStatus" \ --output text 2>&1) retcode="$?" if <<<"${stack_state}" grep -q "does not exist"; then _exit_ok DOES_NOT_EXIST fi if [ $retcode -ne 0 ]; then _exit_error "$stack_state" $retcode fi _exit_ok "$stack_state" } get-stack-action() { local stack_state=$1 local stack_action case $stack_state in *_IN_PROGRESS) # busy _exit_error "Stack is currently in $stack_state state and cannot be updated" 127 ;; *_FAILED) # errored _exit_error "Stack is currently in $stack_state state and cannot be updated" 127 ;; ROLLBACK_COMPLETE) # ok stack_action="delete-then-create" ;; *_COMPLETE) # ok stack_action="update" ;; DOES_NOT_EXIST) # nope stack_action="create" ;; *) # unknown ;; esac echo "$stack_action" } upsert-stack() { local stack_required_action=$1 local stack_name=$2 local stack_template=$3 local region=$4 local upsert_result local retcode echo "Attempting to $stack_required_action stack: $stack_name..." >&2 upsert_result=$(aws cloudformation ${stack_required_action}-stack --region $region \ --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \ --stack-name $stack_name \ --template-body file://./cloudformation/$stack_template.yml \ --parameters file://./cloudformation/.tmp/cloudformation_parameters-$stack_template.json \ --query StackId \ --output text 2>&1) retcode="$?" if <<<"${upsert_result}" grep -q "No updates are to be performed"; then _exit_ok "No updates are to be performed" fi if [ $retcode -ne 0 ]; then _exit_error "$upsert_result" $retcode fi echo "$upsert_result" } tail_stack() { local stack_name=$1 local region=$2 local current local final_line local output local output_result local previous local stack_events local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ") local headings="| Time | Resource | Status | Message |" local markline="+--------------+--------------------------------+--------------------------------+--------------------------------------------------------------+" local header="$markline\n$headings\n$markline\n" local colour_reset='\033[0m' local colour_red='\033[31m' local colour_green='\033[32m' local colour_yellow='\033[33m' local colour_blue='\033[34m' local colour_magenta='\033[35m' local colour_cyan='\033[36m' local colour_white='\033[97m' while true; do stack_events=$(aws cloudformation describe-stack-events --region $region \ --stack-name $stack_name \ --output text \ --query "sort_by(StackEvents, &Timestamp)[?Timestamp>='$now'].[ Timestamp, LogicalResourceId, ResourceStatus, ResourceStatusReason, ResourceType ]" 2>&1) retcode="$?" echo "$stack_events" | egrep -q "does not exist" && break if [ $retcode -ne 0 ]; then _exit_error "$stack_events" $retcode fi if [ -z "$stack_events" ]; then sleep 1 continue fi stack_events_table=$( while IFS=$'\t' read timestamp resource status message rtype; do # ResourceType (rtype) not used, included for completeness printf "| %b%-12s%b | %b%-30s%b | %b%-30s%b | %b%-60s%b |\n" \ "$colour_green" "${timestamp:11:8} UTC" "$colour_reset" \ "$colour_red" "$(echo $resource | awk -v len=30 '{ if(length($0)>len) print "…" substr($0,length($0)-len+2,length($0)); else print;}')" "$colour_reset" \ "$colour_cyan" "$(echo $status | awk -v len=30 '{ if(length($0)>len) print "…" substr($0,length($0)-len+2,length($0)); else print;}')" "$colour_reset" \ "$colour_blue" "$(echo $message | awk -v len=60 '{ if(length($0)>len) print "…" substr($0,length($0)-len+2,length($0)); else print;}')" "$colour_reset" done <<< "$stack_events" ) current=$(echo -e "$header$stack_events_table") if [ -z "$previous" ]; then echo "$current" elif [ "$current" != "$previous" ]; then comm -13 <(echo "$previous") <(echo "$current") fi previous="$current" stack_state=$(aws cloudformation describe-stacks --region $region \ --stack-name $stack_name \ --query "Stacks[0].StackStatus" \ --output text 2>&1) retcode="$?" echo "$stack_state" | egrep -q ".*_(COMPLETE|FAILED)$" && break [ $retcode -ne 0 ] && break sleep 1 done echo $markline } deploy_stack() { local application=$1 local environment=$2 local stack_template=$3 local region=$4 local stack_name local stack_required_action stack_name=$(echo "$application-$environment-$stack_template" | tr '_' '-') echo "Checking state of stack: $stack_name..." stack_state=$(get-stack-state $stack_name $region) || exit $? stack_action=$(get-stack-action $stack_state) || exit $? if [[ $stack_action == "delete-then-create" ]]; then echo "Stack exists in non-updatable state. Deleting..." >&2 drop_stack $application $environment $stack_template $region stack_action="create" fi upsert_result=$(upsert-stack $stack_action $stack_name $stack_template $region) || exit $? if <<<"${upsert_result}" grep -q "No updates are to be performed"; then _exit_ok "$upsert_result" fi echo "Stack with id $upsert_result is being ${stack_action}d" tail_stack $stack_name $region } delete-stack() { local stack_name=$1 local region=$2 local delete_result local retcode echo "Attempting to delete stack: $stack_name..." >&2 delete_result=$(aws cloudformation delete-stack --stack-name $stack_name --region $region 2>&1) retcode="$?" if [ $retcode -ne 0 ]; then _exit_error "$delete_result" $retcode fi # delete_result is empty if there is no error, so no return here } drop_stack() { local environment=$1 local application=$2 local stack_template=$3 local region=$4 local stack_name local stack_required_action stack_name=$(echo "$environment-$application-$stack_template" | tr '_' '-') echo "Checking state of stack: $stack_name..." stack_state=$(get-stack-state $stack_name $region) || exit $? if <<<"${stack_state}" egrep -q "DOES_NOT_EXIST"; then _exit_error "The stack $stack_name does not exist" fi delete_result=$(delete-stack $stack_name $region) || exit $? tail_stack $stack_name $region }