Skip to content

Instantly share code, notes, and snippets.

@anniethiessen
Last active October 27, 2024 17:58
Show Gist options
  • Select an option

  • Save anniethiessen/b046f013229b9448e94bf9e834c04864 to your computer and use it in GitHub Desktop.

Select an option

Save anniethiessen/b046f013229b9448e94bf9e834c04864 to your computer and use it in GitHub Desktop.
Script file to prepare and deploy AWS ElasticBeanstalk applications.
#!/usr/bin/env bash
: '
Script file to prepare and deploy AWS ElasticBeanstalk applications.
Ensure AWS EB CLI is installed and configured,
define script variables, then execute script.
First argument must be ElasticBeanstalk environment name,
secondly optional option -c <configuration>, and
all remaining options are passed to 'eb deploy' command.
Accepts -v (verbose output) and -q (quiet output) arguments.
Prerequisites:
- awsebcli 3.20.10 -> https://pypi.org/project/awscli/
Script Variables:
- CONDA_ENV: The conda environment that has required packages installed.
Optional, if current environment has them installed.
- APPLICATION_PATH: The path to the application root.
Should be set if this script file is not in application root.
Optional, defaults to null.
- PERFORM_PREPARE: Whether to perform the ""prepare"" step.
The ""prepare"" function prepares config files
by copying them into application root from '.elasticbeanstalk/env_configs/env_name/'.
This is useful if different environments have unique configurations.
Should only be set to "false" if configuration files are already in their directories
and in this case ""PERFORM_CLEAN"" should also be set to "false".
Optional, defaults to "true".
- PERFORM_DEPLOY: Whether to perform the ""deploy"" step.
The ""deploy"" function deploys application to ElasticBeanstalk.
Optional, defaults to "true".
- PERFORM_CLEAN: Whether to perform the ""clean"" step.
The ""clean"" function deletes configuration files copied in the ""prepare"" step.
Should only be set to "false" if ""PERFORM_PREPARE"" is set to "false"
Optional, defaults to "true".
Usage Example:
CONDA_ENV=xxx
APPLICATION_PATH=xxx
wget -cO - "<this_file_url>" > "temp.sh"
chmod +x "temp.sh"
source "temp.sh" "$@"
rm -rf "temp.sh"
'
#--------------------------------------------
#----------------- CONSTANTS ----------------
#--------------------------------------------
CONDA_ENV="${CONDA_ENV}"
APPLICATION_PATH="${APPLICATION_PATH}"
PERFORM_PREPARE=${PERFORM_PREPARE:-true}
PERFORM_DEPLOY=${PERFORM_DEPLOY:-true}
PERFORM_CLEAN=${PERFORM_CLEAN:-true}
#------- DO NOT EDIT BELOW THIS LINE --------
FORMAT_SCRIPT_URL="https://gist.githubusercontent.com/anniethiessen/efb6bc0e52ccfc8b330aa41364b53e97/raw/0012edc1f009a36d196f03f09fda68e70691860b/shell_script_essentials.sh"
FORMAT_SCRIPT_NAME="shell_script_essentials.sh"
#--------------------------------------------
#--------------- FUNCTIONS -----------------
#--------------------------------------------
function set_opts {
if [ $# -eq 0 ]; then
echo "ElasticBeanstalk environment not specified." ${PROMPT_VERBOSE}
exit 1
fi
ENV=$1; shift;
while getopts ":c:qv" flag; do
case "${flag}" in
c) CONFIG=${OPTARG}; shift 2;;
q) OUTPUT_FLAG="-q"; shift;;
v) OUTPUT_FLAG="-v"; shift;;
*)
esac
done
CONFIG="${CONFIG:-${ENV}}"
OUTPUT_FLAG="${OUTPUT_FLAG:--q}"
EB_OPTS=("$@")
WORKING_PATH=$( pwd )
if [[ "${APPLICATION_PATH: -1}" == "/" ]]; then APPLICATION_PATH="${APPLICATION_PATH%?}"; fi
if [[ "${APPLICATION_PATH:0:1}" == "/" ]]; then APPLICATION_PATH="${APPLICATION_PATH:1}"; fi
if [[ -z "${APPLICATION_PATH}" ]]; then
APPLICATION_PATH="${WORKING_PATH}"
else APPLICATION_PATH="${WORKING_PATH}/${APPLICATION_PATH}"; fi
}
function activate_env {
local retval
output_info_message "Activating local environment '${CONDA_ENV}'."
if [ -n "${CONDA_ENV}" ]; then
eval "$(conda shell.bash hook)"
conda activate "${CONDA_ENV}"
fi
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "Local environment activated."
else
output_error_message "Local environment activation error." ${PROMPT_VERBOSE}
exit_script
fi
if [[ "${OUTPUT}" == "${VERBOSE_OUTPUT}" ]]; then
PYTHON_VERSION=$( python --version 2>&1 )
AWSEBCLI_VERSION=$( eb --version 2>&1 )
output_warning_message "Using ${PYTHON_VERSION} and ${AWSEBCLI_VERSION}"
fi
}
function run_format_script {
wget -cO - ${FORMAT_SCRIPT_URL} > ${FORMAT_SCRIPT_NAME}
chmod +x ${FORMAT_SCRIPT_NAME}
source ${FORMAT_SCRIPT_NAME} "$@"
rm -rf ${FORMAT_SCRIPT_NAME}
}
function cancel_abort_clean_exit_trap {
trap '' EXIT
output_warning_message "Deployment to ElasticBeanstalk environment '${ENV}' cancelled."
abort
clean
exit_script
}
function cancel_clean_trap {
trap '' EXIT
output_warning_message "Deployment to ElasticBeanstalk environment '${ENV}' cancelled."
clean
}
function check_status {
local ret status
cd "${APPLICATION_PATH}"
ret=$( eb status "${ENV}" )
if [[ "${ret}" == "ERROR: NotFoundError - Environment \"${ENV}\" not Found." ]]; then
status="Not Found"
else
status=$( awk -F"Status: " '{print $2}' <<< "${ret}" )
status=$( echo "${status##*$'\n'}" )
status=$( sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "${status}" )
fi
cd "${WORKING_PATH}"
echo "${status}"
}
function abort {
cd "${APPLICATION_PATH}"
status=$( check_status )
if [[ "${status}" == "Updating" ]]; then
output_warning_message "Aborting deployment to ElasticBeanstalk environment '${ENV}'."
eb abort "${ENV}" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "Deployment aborted."
else
output_error_message "Abort deployment error." ${PROMPT_VERBOSE}
fi
fi
cd "${WORKING_PATH}"
}
function prepare {
if [ "${PERFORM_PREPARE}" == false ]; then
output_warning_message "Preparation skipped"
return
fi
local retval
output_info_message "Copying config files '${CONFIG}'."
cd "${APPLICATION_PATH}"
trap cancel_clean_trap EXIT
if [ -d ".elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/" ]; then
output_info_message "Copying '.ebextensions' config files from '${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/'."
cp -R ".elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/." "${APPLICATION_PATH}/.ebextensions/" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.ebextensions' config files copied."
else
output_error_message "'.ebextensions' config file copy error." ${PROMPT_VERBOSE}
exit_script
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/' directory does not exist, no files copied."
fi
if [ -d ".elasticbeanstalk/env_configs/${CONFIG}/.platform/" ]; then
output_info_message "Copying '.platform' config files from '${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.platform/'."
cp -R ".elasticbeanstalk/env_configs/${CONFIG}/.platform/." "${APPLICATION_PATH}/.platform/" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.platform' config files copied."
else
output_error_message "'.platform' config file copy error." ${PROMPT_VERBOSE}
exit_script
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.platform/' directory does not exist, no files copied."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/env.yaml" ]; then
output_info_message "Copying 'env' config file '${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/env.yaml'."
cp ".elasticbeanstalk/env_configs/${CONFIG}/env.yaml" "${APPLICATION_PATH}/env.yaml" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'env.yaml' config file copied."
else
output_error_message "'env.yaml' config file copy error." ${PROMPT_VERBOSE}
exit_script
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/env.yaml' file does not exist, no file copied."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/requirements.txt" ]; then
output_info_message "Copying 'requirements' config file '${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/requirements.txt'."
cp ".elasticbeanstalk/env_configs/${CONFIG}/requirements.txt" "${APPLICATION_PATH}/requirements.txt" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'requirements.txt' config file copied."
else
output_error_message "'requirements.txt' config file copy error." ${PROMPT_VERBOSE}
exit_script
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/requirements.txt' file does not exist, no file copied."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/.ebignore" ]; then
output_info_message "Copying 'ebignore' config file '${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebignore'."
cp ".elasticbeanstalk/env_configs/${CONFIG}/.ebignore" "${APPLICATION_PATH}/.ebignore" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.ebignore' config file copied."
else
output_error_message "'.ebignore' config file copy error." ${PROMPT_VERBOSE}
exit_script
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebignore' file does not exist, no file copied."
fi
cd "${WORKING_PATH}"
}
function deploy {
if [ "${PERFORM_DEPLOY}" == false ]; then
output_warning_message "Deploy skipped"
return
fi
local rv retval create
output_info_message "Deploying to ElasticBeanstalk environment '${ENV}'"
trap cancel_clean_trap EXIT
status=$( check_status )
if [[ "${status}" == "Ready" ]]; then
output_info_message "ElasticBeanstalk environment '${ENV}' ready."
create=false
elif [[ "${status}" == "Not Found" ]]; then
output_info_message "ElasticBeanstalk environment '${ENV}' will be created."
create=true
else
output_error_message "ElasticBeanstalk environment '${ENV}' not ready, status is '${status}'."
exit_script
fi
cd "${APPLICATION_PATH}"
trap cancel_abort_clean_exit_trap SIGINT
if [[ "${create}" == true ]]; then
output_info_message "Running 'create' command with options:"
output_info_message "${EB_OPTS[*]}"
eb create "${ENV}" "${EB_OPTS[@]}" &> ${OUTPUT}
else
output_info_message "Running 'deploy' command with options:"
output_info_message "${EB_OPTS[*]}"
eb deploy "${ENV}" "${EB_OPTS[@]}" &> ${OUTPUT}
fi
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "Application deployed."
else
output_error_message "Application deployment error." ${PROMPT_VERBOSE}
exit_script
fi
cd "${WORKING_PATH}"
}
function clean {
if [ "${PERFORM_CLEAN}" == false ]; then
output_warning_message "Clean skipped"
return
fi
local retval
output_info_message "Deleting artifact config files."
trap '' EXIT
cd "${APPLICATION_PATH}"
if [ -d ".elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/" ]; then
output_info_message "Deleting '.ebextensions' artifact config files in '${APPLICATION_PATH}/.ebextensions/'."
rm -rf ".ebextensions/" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.ebextensions' artifact config files deleted."
else
output_error_message "'.ebextensions' artifact config file delete error." ${PROMPT_VERBOSE}
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebextensions/' directory does not exist, no files deleted."
fi
if [ -d ".elasticbeanstalk/env_configs/${CONFIG}/.platform/" ]; then
output_info_message "Deleting '.platform' artifact config files in '${APPLICATION_PATH}/.platform/'."
rm -rf ".platform/" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.platform' artifact config files deleted."
else
output_error_message "'.platform' artifact config file delete error." ${PROMPT_VERBOSE}
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.platform/' directory does not exist, no files deleted."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/env.yaml" ]; then
output_info_message "Deleting 'env.yaml' artifact config file '${APPLICATION_PATH}/env.yaml'."
rm -f "env.yaml" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'env.yaml' artifact config file deleted."
else
output_error_message "'env.yaml' artifact config file delete error." ${PROMPT_VERBOSE}
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/env.yaml' file does not exist, file not deleted."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/requirements.txt" ]; then
output_info_message "Deleting 'requirements.txt' artifact config file '${APPLICATION_PATH}/requirements.txt'."
rm -f "requirements.txt" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'requirements.txt' artifact config file deleted."
else
output_error_message "'requirements.txt' artifact config file delete error." ${PROMPT_VERBOSE}
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/requirements.txt' file does not exist, file not deleted."
fi
if [ -f ".elasticbeanstalk/env_configs/${CONFIG}/.ebignore" ]; then
output_info_message "Deleting 'ebignore' artifact config file '${APPLICATION_PATH}/.ebignore'."
rm -f ".ebignore" &> ${OUTPUT}
retval=$?
if [[ ${retval} -eq 0 ]]; then
output_success_message "'.ebignore' artifact config file deleted."
else
output_error_message "'.ebignore' artifact config file delete error." ${PROMPT_VERBOSE}
fi
else
output_warning_message "'${APPLICATION_PATH}/.elasticbeanstalk/env_configs/${CONFIG}/.ebignore' file does not exist, file not deleted."
fi
cd "${WORKING_PATH}"
}
#--------------------------------------------
#------------------ MAIN --------------------
#--------------------------------------------
set_opts "$@"
run_format_script ${OUTPUT_FLAG}
activate_env
output_header_message "----------------------------------------"
output_header_message "[1/3] PREPARE"
output_header_message "preparing config artifact files ..."
output_header_message "----------------------------------------"
if [ "${PERFORM_PREPARE}" == true ] ; then
prepare
else output_warning_message "Preparation skipped"; fi
output_header_message "----------------------------------------"
output_header_message "[2/3] DEPLOY"
output_header_message "deploying application to ElasticBeanstalk ..."
output_header_message "----------------------------------------"
if [ "${PERFORM_DEPLOY}" == true ] ; then
deploy
else output_warning_message "Deploy skipped"; fi
output_header_message "----------------------------------------"
output_header_message "[3/3] CLEAN"
output_header_message "cleaning config artifact files ..."
output_header_message "----------------------------------------"
if [ "${PERFORM_CLEAN}" == true ] ; then
clean
else output_warning_message "Clean skipped"; fi
@anniethiessen
Copy link
Author

anniethiessen commented Oct 25, 2023

Change Log:

v1: Initial

v2:
-feat: added CONDA_ENV variable: Conda environment is activated if defined (fixes v1 issue #1)
-fix: removed debug 'ls -a' command in ""deploy"" function (fixes v1 issue #2)
-fix: updated "Application Deployed" from warning message to success message (fixes v1 issue #3)
-fix: fixed incorrect directory in clean function (fixes v1 issue #4)

v3:
-update: "APPLICATION_PATH" can be null and include forward-slashes
-update: each function changes to required directory, in case only some functions are called
-fix: abort and clean trap on ctrl-c
-add: clean trap on exit
-update: check "PERFORM_*" variables inside functions in case they are called from other functions
-feat: add "activate_env" function

v4:
-feat: added create command if env does not exist
-reorganized config file prep & clean to .elasticbeanstalk/env_configs/
-add env.yaml and requirements.txt to config file prep and clean

v5:
-feat: ebignore file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment