|
#!/bin/bash |
|
|
|
# EC2 Instance Management Script |
|
# This script manages (start/stop/status) EC2 instances |
|
|
|
set -e # Exit on any error |
|
|
|
# Configuration |
|
DEFAULT_PROJECT_TAG="MyProject" # Default project tag to filter instances |
|
DEFAULT_ENVIRONMENT_TAG="dev" # Default environment tag |
|
|
|
# Colors for output |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' # No Color |
|
|
|
# Function to print colored output |
|
print_status() { |
|
echo -e "${GREEN}[INFO]${NC} $1" |
|
} |
|
|
|
print_warning() { |
|
echo -e "${YELLOW}[WARNING]${NC} $1" |
|
} |
|
|
|
print_error() { |
|
echo -e "${RED}[ERROR]${NC} $1" |
|
} |
|
|
|
print_header() { |
|
echo -e "${BLUE}$1${NC}" |
|
} |
|
|
|
# Function to check if AWS CLI is installed and configured |
|
check_aws_cli() { |
|
if ! command -v aws &> /dev/null; then |
|
print_error "AWS CLI is not installed. Please install it first." |
|
exit 1 |
|
fi |
|
|
|
if ! aws sts get-caller-identity &> /dev/null; then |
|
print_error "AWS CLI is not configured or credentials are invalid." |
|
exit 1 |
|
fi |
|
} |
|
|
|
# Function to get instance IDs |
|
get_instance_ids() { |
|
local source=$1 |
|
local instance_ids="" |
|
|
|
case $source in |
|
"project") |
|
local project_tag="${2:-$DEFAULT_PROJECT_TAG}" |
|
print_status "Finding instances with Project tag: $project_tag" >&2 |
|
|
|
# First, let's try with JSON output to avoid potential XML issues |
|
local json_output |
|
json_output=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:Project,Values=$project_tag" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output json 2>&1) |
|
|
|
if [[ $? -ne 0 ]]; then |
|
print_error "AWS describe-instances failed: $json_output" >&2 |
|
exit 1 |
|
fi |
|
|
|
# Parse JSON to get instance IDs |
|
instance_ids=$(echo "$json_output" | jq -r '.[]' 2>/dev/null | tr '\n' ' ') |
|
|
|
# Fallback to text output if jq is not available |
|
if [[ -z "$instance_ids" || "$instance_ids" == "null " ]]; then |
|
instance_ids=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:Project,Values=$project_tag" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
fi |
|
;; |
|
"environment") |
|
local env_tag="${2:-$DEFAULT_ENVIRONMENT_TAG}" |
|
print_status "Finding instances with Environment tag: $env_tag" >&2 |
|
instance_ids=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:Environment,Values=$env_tag" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
;; |
|
"both") |
|
local project_tag="${2:-$DEFAULT_PROJECT_TAG}" |
|
local env_tag="${3:-$DEFAULT_ENVIRONMENT_TAG}" |
|
print_status "Finding instances with Project: $project_tag AND Environment: $env_tag" >&2 |
|
instance_ids=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:Project,Values=$project_tag" "Name=tag:Environment,Values=$env_tag" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
;; |
|
"managed") |
|
print_status "Finding all managed instances (ManagedBy=automation)" >&2 |
|
instance_ids=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:ManagedBy,Values=automation" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
;; |
|
"manual") |
|
read -p "Enter instance IDs (space-separated): " instance_ids |
|
;; |
|
"custom") |
|
read -p "Enter tag name to filter instances by: " tag_name |
|
read -p "Enter tag value: " tag_value |
|
print_status "Finding instances with $tag_name: $tag_value" >&2 |
|
instance_ids=$(aws ec2 describe-instances \ |
|
--filters "Name=tag:$tag_name,Values=$tag_value" "Name=instance-state-name,Values=running,stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
;; |
|
esac |
|
|
|
if [[ -z "$instance_ids" || "$instance_ids" == "None" ]]; then |
|
print_error "No instance IDs found with the specified criteria!" >&2 |
|
exit 1 |
|
fi |
|
|
|
# Clean up instance IDs - normalize whitespace |
|
instance_ids=$(echo "$instance_ids" | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') |
|
|
|
# Validate instance ID format (should be i-xxxxxxxxxxxxxxxxx) |
|
for id in $instance_ids; do |
|
if [[ ! "$id" =~ ^i-[0-9a-f]{8,17}$ ]]; then |
|
print_error "Invalid instance ID format: '$id'" >&2 |
|
exit 1 |
|
fi |
|
done |
|
|
|
echo "$instance_ids" |
|
} |
|
|
|
# Function to display instance status |
|
show_status() { |
|
local instance_ids=$1 |
|
print_header "Current Instance Status:" |
|
|
|
# Convert space-separated IDs to array for proper quoting |
|
IFS=' ' read -ra ids_array <<< "$instance_ids" |
|
|
|
if [ ${#ids_array[@]} -eq 0 ]; then |
|
print_error "No instance IDs provided" |
|
return 1 |
|
fi |
|
|
|
print_status "Querying status for ${#ids_array[@]} instance(s)..." |
|
|
|
# Try with JSON output first, then parse manually to avoid XML issues |
|
local json_result |
|
json_result=$(aws ec2 describe-instances --instance-ids "${ids_array[@]}" --output json 2>&1) |
|
|
|
if [[ $? -ne 0 ]]; then |
|
print_error "Failed to get instance status: $json_result" |
|
return 1 |
|
fi |
|
|
|
# Parse and display results |
|
echo "$json_result" | jq -r '.Reservations[].Instances[] | [.InstanceId, .State.Name, .InstanceType, .PublicIpAddress // "N/A", .PrivateIpAddress // "N/A", (.Tags[] | select(.Key=="Name") | .Value) // "N/A"] | @tsv' 2>/dev/null | \ |
|
{ |
|
printf "%-19s %-10s %-12s %-15s %-15s %s\n" "Instance ID" "State" "Type" "Public IP" "Private IP" "Name" |
|
printf "%-19s %-10s %-12s %-15s %-15s %s\n" "-------------------" "----------" "------------" "---------------" "---------------" "----" |
|
while IFS=$'\t' read -r id state type pub_ip priv_ip name; do |
|
printf "%-19s %-10s %-12s %-15s %-15s %s\n" "$id" "$state" "$type" "$pub_ip" "$priv_ip" "$name" |
|
done |
|
} |
|
|
|
# Fallback to table format if jq fails |
|
if [[ $? -ne 0 ]]; then |
|
print_warning "jq not available, using basic table format" |
|
aws ec2 describe-instances --instance-ids "${ids_array[@]}" \ |
|
--query 'Reservations[].Instances[].[InstanceId,State.Name,InstanceType,PublicIpAddress,PrivateIpAddress,Tags[?Key==`Name`].Value|[0]]' \ |
|
--output table |
|
fi |
|
} |
|
|
|
# Function to start instances |
|
start_instances() { |
|
local instance_ids=$1 |
|
print_status "Starting instances: $instance_ids" |
|
|
|
IFS=' ' read -ra ids_array <<< "$instance_ids" |
|
local stopped_instances |
|
stopped_instances=$(aws ec2 describe-instances --instance-ids "${ids_array[@]}" \ |
|
--filters "Name=instance-state-name,Values=stopped" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
|
|
if [[ -n "$stopped_instances" ]]; then |
|
aws ec2 start-instances --instance-ids $stopped_instances |
|
print_status "Start command sent. Waiting for instances to reach 'running' state..." |
|
aws ec2 wait instance-running --instance-ids $stopped_instances |
|
print_status "All instances are now running!" |
|
show_status "$instance_ids" |
|
else |
|
print_warning "No stopped instances found to start" |
|
fi |
|
} |
|
|
|
# Function to stop instances |
|
stop_instances() { |
|
local instance_ids=$1 |
|
print_status "Stopping instances: $instance_ids" |
|
|
|
IFS=' ' read -ra ids_array <<< "$instance_ids" |
|
local running_instances |
|
running_instances=$(aws ec2 describe-instances --instance-ids "${ids_array[@]}" \ |
|
--filters "Name=instance-state-name,Values=running" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
|
|
if [[ -n "$running_instances" ]]; then |
|
aws ec2 stop-instances --instance-ids $running_instances |
|
print_status "Stop command sent. Waiting for instances to reach 'stopped' state..." |
|
aws ec2 wait instance-stopped --instance-ids $running_instances |
|
print_status "All instances are now stopped!" |
|
show_status "$instance_ids" |
|
else |
|
print_warning "No running instances found to stop" |
|
fi |
|
} |
|
|
|
# Function to terminate instances |
|
terminate_instances() { |
|
local instance_ids=$1 |
|
print_warning "WARNING: This will permanently delete the instances!" |
|
|
|
IFS=' ' read -ra ids_array <<< "$instance_ids" |
|
|
|
# Show what will be terminated |
|
print_status "Instances to be terminated:" |
|
aws ec2 describe-instances --instance-ids "${ids_array[@]}" \ |
|
--query 'Reservations[].Instances[].[InstanceId,Tags[?Key==`Name`].Value|[0],Tags[?Key==`Project`].Value|[0],Tags[?Key==`Environment`].Value|[0]]' \ |
|
--output table |
|
|
|
read -p "Are you sure you want to terminate these instances? (type 'yes' to confirm): " confirm |
|
|
|
if [[ "$confirm" == "yes" ]]; then |
|
print_status "Terminating instances: $instance_ids" |
|
local result |
|
result=$(aws ec2 terminate-instances --instance-ids "${ids_array[@]}" --output json 2>&1) |
|
|
|
if [[ $? -eq 0 ]]; then |
|
print_status "Termination command sent. Waiting for instances to be fully terminated..." |
|
aws ec2 wait instance-terminated --instance-ids "${ids_array[@]}" |
|
print_status "✓ All ${#ids_array[@]} instances have been terminated successfully" |
|
else |
|
print_error "Failed to terminate instances: $result" |
|
fi |
|
else |
|
print_warning "Termination cancelled" |
|
fi |
|
} |
|
|
|
# Function to reboot instances |
|
reboot_instances() { |
|
local instance_ids=$1 |
|
print_status "Rebooting instances: $instance_ids" |
|
|
|
IFS=' ' read -ra ids_array <<< "$instance_ids" |
|
local running_instances |
|
running_instances=$(aws ec2 describe-instances --instance-ids "${ids_array[@]}" \ |
|
--filters "Name=instance-state-name,Values=running" \ |
|
--query 'Reservations[].Instances[].InstanceId' \ |
|
--output text) |
|
|
|
if [[ -n "$running_instances" ]]; then |
|
aws ec2 reboot-instances --instance-ids $running_instances |
|
print_status "Reboot command sent to running instances!" |
|
else |
|
print_warning "No running instances found to reboot" |
|
fi |
|
} |
|
|
|
# Function to show help |
|
show_help() { |
|
echo "Usage: $0 [COMMAND] [OPTIONS]" |
|
echo |
|
echo "Commands:" |
|
echo " start Start stopped instances" |
|
echo " stop Stop running instances" |
|
echo " status Show current status of instances" |
|
echo " reboot Reboot running instances" |
|
echo " terminate Terminate instances (permanent deletion)" |
|
echo " help Show this help message" |
|
echo |
|
echo "Instance Selection Options:" |
|
echo " -p, --project [TAG] Select by project tag (default: $DEFAULT_PROJECT_TAG)" |
|
echo " -e, --environment [TAG] Select by environment tag (default: $DEFAULT_ENVIRONMENT_TAG)" |
|
echo " -b, --both [PROJECT] [ENV] Select by both project and environment tags" |
|
echo " -a, --managed Select all managed instances (ManagedBy=automation)" |
|
echo " -m, --manual Manually enter instance IDs" |
|
echo " -c, --custom Select by custom tag name/value" |
|
echo |
|
echo "Examples:" |
|
echo " $0 start # Start instances with default project tag" |
|
echo " $0 stop --project MyWebApp # Stop instances tagged with Project=MyWebApp" |
|
echo " $0 status --environment prod # Show status of production instances" |
|
echo " $0 terminate --both MyApp staging # Terminate instances with Project=MyApp AND Environment=staging" |
|
echo " $0 start --managed # Start all automation-managed instances" |
|
echo " $0 reboot --custom # Reboot instances selected by custom tag" |
|
} |
|
|
|
# Main function |
|
main() { |
|
echo "==================================================" |
|
echo " EC2 Instance Management Script" |
|
echo "==================================================" |
|
|
|
check_aws_cli |
|
|
|
# Parse command line arguments |
|
local command="" |
|
local source="project" # Default to project tag |
|
local tag_value="" |
|
local env_value="" |
|
|
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
start|stop|status|reboot|terminate) |
|
command=$1 |
|
shift |
|
;; |
|
-p|--project) |
|
source="project" |
|
if [[ -n "$2" && "$2" != -* ]]; then |
|
tag_value=$2 |
|
shift |
|
fi |
|
shift |
|
;; |
|
-e|--environment) |
|
source="environment" |
|
if [[ -n "$2" && "$2" != -* ]]; then |
|
tag_value=$2 |
|
shift |
|
fi |
|
shift |
|
;; |
|
-b|--both) |
|
source="both" |
|
if [[ -n "$2" && "$2" != -* ]]; then |
|
tag_value=$2 |
|
shift |
|
fi |
|
if [[ -n "$2" && "$2" != -* ]]; then |
|
env_value=$2 |
|
shift |
|
fi |
|
shift |
|
;; |
|
-a|--managed) |
|
source="managed" |
|
shift |
|
;; |
|
-m|--manual) |
|
source="manual" |
|
shift |
|
;; |
|
-c|--custom) |
|
source="custom" |
|
shift |
|
;; |
|
help|--help|-h) |
|
show_help |
|
exit 0 |
|
;; |
|
*) |
|
print_error "Unknown option: $1" |
|
show_help |
|
exit 1 |
|
;; |
|
esac |
|
done |
|
|
|
# If no command provided, show interactive menu |
|
if [[ -z "$command" ]]; then |
|
echo "Select an action:" |
|
echo "1) Start instances" |
|
echo "2) Stop instances" |
|
echo "3) Show status" |
|
echo "4) Reboot instances" |
|
echo "5) Terminate instances" |
|
echo "6) Exit" |
|
read -p "Enter choice (1-6): " choice |
|
|
|
case $choice in |
|
1) command="start" ;; |
|
2) command="stop" ;; |
|
3) command="status" ;; |
|
4) command="reboot" ;; |
|
5) command="terminate" ;; |
|
6) exit 0 ;; |
|
*) print_error "Invalid choice"; exit 1 ;; |
|
esac |
|
|
|
# Ask for instance selection method |
|
echo |
|
echo "How do you want to select instances?" |
|
echo "1) By project tag (default: $DEFAULT_PROJECT_TAG)" |
|
echo "2) By environment tag (default: $DEFAULT_ENVIRONMENT_TAG)" |
|
echo "3) By both project and environment tags" |
|
echo "4) All managed instances (automation created)" |
|
echo "5) Enter manually" |
|
echo "6) By custom tag" |
|
read -p "Enter choice (1-6): " source_choice |
|
|
|
case $source_choice in |
|
1) |
|
source="project" |
|
read -p "Enter project tag value (default: $DEFAULT_PROJECT_TAG): " tag_value |
|
;; |
|
2) |
|
source="environment" |
|
read -p "Enter environment tag value (default: $DEFAULT_ENVIRONMENT_TAG): " tag_value |
|
;; |
|
3) |
|
source="both" |
|
read -p "Enter project tag value (default: $DEFAULT_PROJECT_TAG): " tag_value |
|
read -p "Enter environment tag value (default: $DEFAULT_ENVIRONMENT_TAG): " env_value |
|
;; |
|
4) source="managed" ;; |
|
5) source="manual" ;; |
|
6) source="custom" ;; |
|
*) print_error "Invalid choice"; exit 1 ;; |
|
esac |
|
fi |
|
|
|
# Get instance IDs based on selection method |
|
local instance_ids |
|
if [[ "$source" == "both" ]]; then |
|
instance_ids=$(get_instance_ids "$source" "${tag_value:-$DEFAULT_PROJECT_TAG}" "${env_value:-$DEFAULT_ENVIRONMENT_TAG}") |
|
else |
|
instance_ids=$(get_instance_ids "$source" "${tag_value}") |
|
fi |
|
|
|
print_status "Selected instances: $instance_ids" |
|
# Clean word count by using array - properly handle the string |
|
IFS=' ' read -ra clean_array <<< "$instance_ids" |
|
print_status "Number of instances: ${#clean_array[@]}" |
|
echo |
|
|
|
# Execute command |
|
case $command in |
|
"start") |
|
start_instances "$instance_ids" |
|
;; |
|
"stop") |
|
stop_instances "$instance_ids" |
|
;; |
|
"status") |
|
show_status "$instance_ids" |
|
;; |
|
"reboot") |
|
reboot_instances "$instance_ids" |
|
;; |
|
"terminate") |
|
terminate_instances "$instance_ids" |
|
;; |
|
esac |
|
} |
|
|
|
# Run main function with all arguments |
|
main "$@" |