Skip to content

Instantly share code, notes, and snippets.

@callumgare
Last active April 17, 2024 21:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save callumgare/a763692160947c3f41a520838094b1e6 to your computer and use it in GitHub Desktop.
Save callumgare/a763692160947c3f41a520838094b1e6 to your computer and use it in GitHub Desktop.
Creates a shell into an AWS ECS container
#!/usr/bin/env bash
# Enable strict mode
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
usage () {
cat <<EOF
Usage: $(basename "$0") cluster-name [command to execute]
Example usage: $(basename "$0") iss4-dev ls /
Description: Executes a command in an ECS container. If "command to execute"
is not given then "bash" will be used. Use the AWS_PROFILE environment
variable to set the profile for AWS CLI to use.
To sepcify the profile as a quick one-liner you can prefix with
\`env AWS_PROFILE=\$NAME_OF_PROFILE_TO_USE\`. For example
\`env AWS_PROFILE=ds-iss-test $(basename "$0") iss3-test\`.
EOF
}
usage_as_additional_info () {
echo ""
echo "--------------------------------------------------------------------------------"
usage
}
if [[ "${1:-}" == "--help" ]]; then
usage
exit 0;
fi
if [[ -z "${1:-}" ]]; then
echo "Error: Missing first argument." >&2
usage_as_additional_info
exit 1
fi
if [[ -z "${2:-}" ]]; then
echo "Error: Missing second argument." >&2
usage_as_additional_info
exit 1
fi
CLUSTER=${1:-}
shift 1
# Default command to "bash" if not given
if [ -z "${1:-}" ]; then
set -- "bash"
fi
COMMAND="$(printf ' %q' "$@")"
# Refresh aws token if previously issued token has expired
set +o pipefail
if aws configure list 2>&1 | grep -q 'run aws sso login' && true; then
aws sso login
fi
set -o pipefail
AWS_CONFIG_LIST=$(aws configure list | sed 's/^[ \t]*//')
CURRENT_AWS_PROFILE=$(echo "$AWS_CONFIG_LIST" | awk '$1 == "profile"' | awk '{print $2}')
CURRENT_AWS_ACCESS_KEY=$(echo "$AWS_CONFIG_LIST" | awk '$1 == "access_key"' | awk '{print $2}')
if [ "$CURRENT_AWS_ACCESS_KEY" == "<not" ]; then
if [ "$CURRENT_AWS_ACCESS_KEY" == "<not" ]; then
echo "Error: AWS CLI profile not set and no credentials not found. Please set the AWS_PROFILE environment variable used by AWS CLI." >&2
else
echo "Error: AWS CLI profile set to \"$CURRENT_AWS_PROFILE\" but no credentials not found." >&2
fi
exit 1
fi
get_running_containers() {
TASKS_LIST_STDOUT=$(mktemp)
set +e # Disable exit on non-zero return since we want to do things if we get an error
TASKS_LIST=$(aws ecs list-tasks --cluster "$CLUSTER" --output text 2>"$TASKS_LIST_STDOUT")
TASKS_LIST_EXIT_CODE=$?
if [ "$TASKS_LIST_EXIT_CODE" != "0" ]; then
if grep -q "Cluster not found" < "$TASKS_LIST_STDOUT"; then
echo "The ECS cluster \"$CLUSTER\" not found using AWS CLI profile of \"$CURRENT_AWS_PROFILE\"" >&2
exit 1
else
cat "$TASKS_LIST_STDOUT" >&2
rm "$TASKS_LIST_STDOUT"
exit 1
fi
fi
set -e # Re-enable exit on non-zero return
TASK_IDS=$(awk '{print $2}' <<<"$TASKS_LIST");
# Get container-level details, append task-level createdAt and taskDefinitionArnonly, then extract last segument of containerArn, taskArn and image
aws ecs describe-tasks --cluster "$CLUSTER" --tasks $TASK_IDS \
--query "tasks[][containers[?lastStatus=='RUNNING'][name, containerArn, taskArn, image]][][]" --output text \
| xargs -S "$(getconf ARG_MAX)" "-I{}" -- bash -c 'printf "%s %s %s\n" "$0" $(aws ecs describe-tasks --cluster "'"$CLUSTER"'" --tasks $(awk "{print \$3}" <<<"$0") --query "tasks[0] | [createdAt, taskDefinitionArn]" --output text)' {} \
| awk -v OFS=$'\t' '{split($2,a,"/"); $2=a[4]; split($3,a,"/"); $3=a[3]; split($4,a,":"); $4=a[2]; split($6,a,":"); $6=a[7]; print $0}'
}
format_running_containers() {
{
echo -e "Container Name\tContainer Id\t\tTask Id\tImage Tag\tCreation Date\tTask Defintion";
echo "$1"
} | column -t -s$'\t'
}
TASKS_LIST=$(get_running_containers)
TASKS_COUNT=$(wc -l <<<"$TASKS_LIST" | awk '{print $1}')
if [ "$TASKS_COUNT" -gt "1" ]; then
echo "The ECS cluster \"$CLUSTER\" has more than 1 task. Which one would you like to execute the command in?";
echo ""
TASK_IDS=$(awk '{print $2}' <<<"$TASKS_LIST");
# readarray requires bash 4 and later
readarray -t TASKS_TABLE < <(format_running_containers "$TASKS_LIST")
# Print headers
echo " " "${TASKS_TABLE[@]:0:1}"
PS3=$'\nTask line number: '
select TASK_LINE in "${TASKS_TABLE[@]:1}"; do
TASK_ID=$(awk '{print $3}' <<<"$TASK_LINE")
CONTAINER_NAME=$(awk '{print $1}' <<<"$TASK_LINE")
break;
done
elif [ "$TASKS_COUNT" == 0 ]; then
echo "The ECS cluster \"$CLUSTER\" has no tasks." 1>&2
exit 1
else
TASK_ID=$(awk '{print $3}' <<<"$TASKS_LIST")
CONTAINER_NAME=$(awk '{print $1}' <<<"$TASKS_LIST")
fi
exec aws ecs execute-command --cluster "$CLUSTER" --task "$TASK_ID" --container "$CONTAINER_NAME" --interactive --command "$COMMAND"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment