Skip to content

Instantly share code, notes, and snippets.

@lktslionel
Last active February 27, 2025 11:16
Show Gist options
  • Save lktslionel/400c6a1f4f5a71e58347f1147b38cc06 to your computer and use it in GitHub Desktop.
Save lktslionel/400c6a1f4f5a71e58347f1147b38cc06 to your computer and use it in GitHub Desktop.
Shell Script Sample
#!/bin/bash
# --------------
# TIPS & TRICKS
# --------------
: '
---
- SECTION
---
Long comment section
without needed to add # at the beginning of each lines
'
#
# INIT
#
# Exit if any errros occur
[[ $PRG_PANIC_ON_ERROR == "true" ]] && set -euo pipefail;
# Activate debug mode if needed
[[ $PRG_LOGLEVEL == "debug" ]] && set -x;
# TRAPS
trap '__handle_error' ERR # Custom signal "ERR" when it is sent to stdout/stderr via echo
trap '__handle_exit' EXIT
trap '__handle_interrupt' SIGINT SIGQUIT SIGTERM
# trap '__handle_cleanup' SIGHUP
#
# GLOBALS
#
# Colors
_RESET="\033[0m"
_GREEN="\033[0;32m"
_BOLD="\033[1m"
_RED="\033[0;31m"
_YELLOW="\033[0;33m"
_GREY="\033[0;37m"
_PRG_NAME_=${PRG_NAME:-$(basename $0)}
_PRG_SHORT_DESC_="${PRG_SHORT_DESC}"
# [TODO] : Change all placeholders <...>
_PRG_EXAMPLES="EXAMPLES
<write examples here...>
"
_PRG_NOTES="NOTES
<write notes here...>
"
_PRG_AUTHOR_NAME="<USERNAME>"
_PRG_AUTHOR_EMAIL="<EMAIL>"
_PRG_AUTHOR_YEAR="<YYYY>"
_PRG_AUTHOR="AUTHOR
<write author here...>
"
_PRG_LICENSE="LICENSE
MIT License - Copyright (c) [$_PRG_AUTHOR_YEAR] [$_PRG_AUTHOR_NAME<$_PRG_AUTHOR_EMAIL>]
"
# [/TODO]
#
# INCLUDES / IMPORTS
#
# source <path/to/source/file/to/load
#
# HELPERS
#
function GetDatetime(){
printf "time=%s" $(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") # RFC3339
}
function _log(){
if test $# -gt 0; then
local _orig_args="$*"
local _type="${1}";shift
local _tag="rzo/$_PRG_NAME_"
local _prefix
local _suffix
local _msg="$*"
case $_type in
--err)
_prefix='::'
_color="${_RED}"
;;
--step)
_type="notice"
_prefix='::'
_color="${_BOLD}"
;;
--done)
_type="notice"
_prefix=':: Done.'
_color="${_BOLD}"
;;
--task)
_type="notice"
_prefix=' -'
_suffix='...'
_color="${YELLOW}"
;;
*)
_msg="$_orig_args"
_type="notice"
_prefix=' ?'
_color="${RESET}"
esac
if [[ $PRG_ENABLE_ENHANCED_LOGGER == "yes" ]];then
_msg="${_prefix} $_msg $_suffix"
logger -t $_tag -p ${_type##*-} -s "$_msg"
else
_msg="$(GetDatetime) ${_color}${_prefix} $_tag: $_msg $_suffix $RESET"
echo $_msg
fi
fi
}
function Step() {
echo ""
_log --step "$*"
}
function Task() {
_log --task "$*"
}
function Log() {
if [[ $PRG_LOGLEVEL == "debug" ]]; then
_log "$*"
fi;
}
function Err() {
_log --err "$*"
exit 1
}
function Done() {
_log --done "$*"
echo ""
}
function Check() {
local _code=$1
shift
local _message="${*}"
if [[ $_code != 0 ]]; then
Err "$_message"
exit 1
else
Log "No errors"
fi;
}
#
# FUNCTIONS
#
# [TODO] : Change placeholders <...>
usage(){
cat <<USAGE
NAME
${_PRG_NAME_} - ${_PRG_SHORT_DESC_}
SYNOPSIS
${_PRG_NAME_} [OPTIONS]
OPTIONS
-h|--help Show help
-<OPT_SHORT>|--<OPT_LONG> <OPT_ARGS> <OPT_DESC>
USAGE
}
usage_long(){
# print share version
usage
cat <<USAGE_EXTRAS
${_PRG_NOTES}
${_PRG_EXAMPLES}
${_PRG_AUTHOR}
${_PRG_LICENSE}
USAGE_EXTRAS
}
# [TODO] : Use this bloc to document you function
#
# <FUNC NAME> : <FUNC DESC>
#
# @param <var>:<type> - Desc
# @param <var>:<type> - Desc
#
# @return <var>:<type> - Desc
#
# [/TODO]
#
# OPTIONS
#
if test $# -eq 0
then
usage
fi
while test $# -gt 0; do
case "$1" in
-h|--help)
usage_long
exit 0
;;
-<OPT>|--<OPT_LONG>) # [TODO] : Change HERE
shift
_optval_<OPT>=$1 # [TODO] : Change HERE
shift
;;
*)
e "Option [$1] is not supported!"
usage
exit 1
;;
esac
done
#
# REQUIREMENTS
#
Step "Check requirements"
: "${<VAR>:?<VAR> is required!}" # [TODO] : Change HERE
Done
#
# PARAMS
#
_param_1=${<NAME>:-<VALUE} # [TODO] : Change HERE
... # [TODO] : Change HERE
_param_X=${<NAME>:-<DEFAULT_VALUE>} # [TODO] : Change HERE
#
# LOGIC
#
# HANDLERS
__handle_error(){
Step "Handle Error:"
# Logic here
Done
}
__handle_exit(){
Step "Handle onExit"
# Logic here
Done
}
__handle_interrupt(){
Step "Handle onInterrupt: CTRL+C Hit"
Task "Now exiting"
# Logic here
Done
}
#__handle_cleanup(){
# echo __handle_cleanup
#}
# MAIN
main(){
local _<PARAM_1>="$1" # [TODO] : Change HERE
local _<PARAM_2>="$2" # [TODO] : Change HERE
<logic here> # [TODO] : Change HERE
# Done or Err
}
main $_param_1 ... $_param_X
@lktslionel
Copy link
Author

Example: mark-as-hardened.sh

#!/bin/bash 

# 
# GLOBAL VARS
#

__PROGRAM__=$(basename $0)

#
# INITIALIZE
#

set +e 

# Exit if any errros occur
[[ $PRG_PANIC_ON_ERROR == "true" ]] && set -euo pipefail;

# Activate debug mode if needed
[[ $PRG_LOGLEVEL == "debug" ]] && set -x;


#
# IMPORTS
#

source <source.sh>

#
# VARS
#

_SAWS_VMIHS_METADATA_FILE="${SAWS_VMIHS_METADATA_FILE:-/opt/saws/vmihs/.metadata.json}"

#
# HELPERS
#


usage(){
    cat <<USAGE
Usage : ${__PROGRAM__} [OPTIONS]
    
    Mark the current instance and its AMI as hardened 

 OPTIONS:
    -h|--help               Show help
    -m|--metafile <path>    Metadata file path
USAGE
}

x:err() {                                                                                                            
    echo -e "ERROR - $*"
    exit 1                                                                                                                    
}  

function x:check() {
  local _code=$1
  shift
  local _message="${*}"
  if [[ $_code != 0 ]]; then
    x:err "$_message"
    exit 1
  else
    echo -e "Success."
  fi;
}


#
# LOGIC
#

while test $# -gt 0; do
    case "$1" in
        -h|--help)
            usage
            exit 0
            ;;
        -m|--metafile)
            shift
            _optval_metafile=$1
            shift
            ;;
        *)
            echo "Option [$1] is not supported!"
            usage
            exit 1
            ;;
    esac
done

main() {
    local _metafile="$1"
    local _instance_id_meta="$(ec2-metadata -i)"
    local _instance_id="${_instance_id_meta#*: }"

    local _instance_ami_meta="$(ec2-metadata -a)"
    local _instance_ami_id="${_instance_ami_meta#*: }"

    local _request_input_file=$(mktemp)

    if [[ -z "${_instance_id}" ]] || [[ -z "${_instance_ami_id}" ]]; then
        x:err "ERROR: Unable to get instance[${_instance_id}] and ami[${_instance_ami_id}]"
    fi

    echo -e "Attempt to mark instance[${_instance_id}] and ami[${_instance_ami_id}] as hardened"

    if [[ -z "${_metafile}" ]]; then
        echo -e "No given metafile. use default metadata file[${_SAWS_VMIHS_METADATA_FILE}]"
        _metafile="${_SAWS_VMIHS_METADATA_FILE}"
    fi

    echo -e "Ensuring metadata file[${_metafile}] exists..."
    if [[ ! -f "${_metafile}" ]]; then
        x:err "Metadata file[${_metafile}] not found."
    fi

    echo -e "Building request input file and store it at path[${_request_input_file}]..."
    jq  --arg amiId ${_instance_ami_id} --arg instanceId ${_instance_id} -r '
    {
    "DryRun": false,
    "Resources": [$amiId, $instanceId],
    "Tags": . | to_entries | map({"Key": .key, "Value": .value})
    }'  < ${_metafile} > ${_request_input_file}
    x:check $? "ERROR - Unable to create request input file."

    echo -e "Creating tags on instance[${_instance_id}] and ami[${_instance_ami_id}] from input[${_request_input_file}]..."
    aws ec2 create-tags --cli-input-json file://${_request_input_file}
    x:check $? "ERROR - Unable to create tags."

    echo -e "Cleaning up..."
    rm -rf ${_request_input_file}

    echo -e "Done."

}


main "${_optval_metafile}"

@lktslionel
Copy link
Author

Retries

#!/bin/bash

MAX_RETRIES=5
RETRY_COUNT=0
DELAY=1

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
    /path/to/your/script.sh
    EXIT_CODE=$?

    if [ $EXIT_CODE -eq 0 ]; then
        exit 0
    fi

    RETRY_COUNT=$((RETRY_COUNT + 1))
    sleep $DELAY
    DELAY=$((DELAY * 2))
done

exit 1

@lktslionel
Copy link
Author

lktslionel commented Feb 27, 2025

@lktslionel
Copy link
Author

lktslionel commented Feb 27, 2025

# Log levels:
# - 1: DEBUG
# - 2: INFO
# - 3: WARN
# - 4: ERROR

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