Skip to content

Instantly share code, notes, and snippets.

@ab
Created January 27, 2026 03:17
Show Gist options
  • Select an option

  • Save ab/f405abff524e7e0ea0c22e980095bf3a to your computer and use it in GitHub Desktop.

Select an option

Save ab/f405abff524e7e0ea0c22e980095bf3a to your computer and use it in GitHub Desktop.
Wait for a CloudFormation stack to reach a terminal state.
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat >&2 <<EOM
usage: $(basename "$0") [OPTIONS] STACK_NAME
Wait for a CloudFormation stack to reach a terminal state. Print the final
state on stdout.
-h, --help Print this help message
-s, --say Use \`say' to play a text to speech message
-e, --errexit Exit with status 255 if CloudFormation stack errors
See also the \`rain' CLI: https://github.com/aws-cloudformation/rain
EOM
}
run() {
echo >&2 "+ $*"
"$@"
}
log() {
echo >&2 -n "$(date "+%F %R:%S %z") | "
echo >&2 "$*"
}
# All statuses:
# CREATE_COMPLETE
# CREATE_FAILED
# CREATE_IN_PROGRESS
# DELETE_COMPLETE
# DELETE_FAILED
# DELETE_IN_PROGRESS
# DELETE_SKIPPED
# IMPORT_COMPLETE
# IMPORT_IN_PROGRESS
# IMPORT_ROLLBACK_COMPLETE
# IMPORT_ROLLBACK_FAILED
# IMPORT_ROLLBACK_IN_PROGRESS
# REVIEW_IN_PROGRESS
# ROLLBACK_COMPLETE
# ROLLBACK_FAILED
# ROLLBACK_IN_PROGRESS
# UPDATE_COMPLETE
# UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
# UPDATE_FAILED
# UPDATE_IN_PROGRESS
# UPDATE_ROLLBACK_COMPLETE
# UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS
# UPDATE_ROLLBACK_FAILED
# UPDATE_ROLLBACK_IN_PROGRESS
is_terminal_state() {
case "$1" in
*_FAILED |\
*_ROLLBACK_COMPLETE |\
*_COMPLETE )
return 0
;;
*)
return 1
;;
esac
}
is_success_state() {
case "$1" in
CREATE_COMPLETE |\
DELETE_COMPLETE |\
IMPORT_COMPLETE |\
UPDATE_COMPLETE )
return 0
;;
*)
return 1
;;
esac
}
get_status() {
local stack
stack="$1"
run aws cloudformation describe-stacks --stack-name "$stack" --query 'Stacks[0].StackStatus' --output text
}
wait_for_stack() {
local stack cur_status ret
stack="$1"
cur_status="$(get_status "$stack")"
log "Stack: $stack"
log "Current status: $cur_status"
if is_terminal_state "$cur_status"; then
log "Already in terminal state"
if is_success_state "$cur_status"; then
ret=0
else
ret=255
fi
report_status "$cur_status" "$ret"
return
fi
local final_status
case "$cur_status" in
CREATE_IN_PROGRESS)
cond='stack-create-complete'
final_status=CREATE_COMPLETE
;;
DELETE_IN_PROGRESS)
cond='stack-delete-complete'
final_status=DELETE_COMPLETE
;;
IMPORT_IN_PROGRESS)
cond='stack-import-complete'
final_status=IMPORT_COMPLETE
;;
UPDATE_IN_PROGRESS)
cond='stack-update-complete'
final_status=UPDATE_COMPLETE
;;
*)
echo >&2 "error: unknown status $cur_status -- not sure what event to wait for"
return 10
esac
local ret
run aws cloudformation wait "$cond" --stack-name "$stack" && ret=$? || ret=$?
# On success, just assume we already know the final status.
# (For DELETE_COMPLETE, describe-stacks will return an error since it's
# "deleted" (unless you pass the ARN), which is a nuisance.)
if [ "$ret" -ne 0 ]; then
final_status="$(get_status "$stack")"
fi
log "Final status: $final_status"
report_status "$final_status" "$ret"
}
report_status() {
local status exitcode
status="$1"
exitcode="$2"
echo "$status"
if [ -n "$action_say" ]; then
run say "CloudFormation stack reached final status $status"
fi
if [ -n "$errexit" ]; then
return "$exitcode"
fi
}
action_say=
errexit=
while [ $# -ge 1 ] && [[ $1 == -* ]]; do
case "$1" in
-h|--help)
usage
exit
;;
-s|--say)
action_say=1
;;
-e|--errexit)
errexit=1
;;
*)
usage
exit 1
;;
esac
shift
done
if [ $# -ne 1 ]; then
usage
exit 1
fi
wait_for_stack "$1"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment