Skip to content

Instantly share code, notes, and snippets.

@bewithdhanu
Created November 13, 2025 15:37
Show Gist options
  • Select an option

  • Save bewithdhanu/d00d4e0048fba6f256adb5a8ca689cd6 to your computer and use it in GitHub Desktop.

Select an option

Save bewithdhanu/d00d4e0048fba6f256adb5a8ca689cd6 to your computer and use it in GitHub Desktop.
AWS CloudShell script to performs a snapshot then terminate and release Elastic IPs
#!/bin/bash
set -euo pipefail
snapshot_and_cleanup() {
INSTANCE_ID=$1
echo "============================================="
echo "➑ Starting cleanup workflow for $INSTANCE_ID"
echo "============================================="
# ---- VALIDATE INSTANCE ----
if ! aws ec2 describe-instances --instance-ids "$INSTANCE_ID" >/dev/null 2>&1; then
echo "❌ ERROR: Instance $INSTANCE_ID not found!"
return 1
fi
# ---- FETCH INSTANCE DETAILS ----
INSTANCE_NAME=$(aws ec2 describe-instances --instance-id "$INSTANCE_ID" \
--query "Reservations[0].Instances[0].Tags[?Key=='Name'].Value | [0]" \
--output text)
INSTANCE_TYPE=$(aws ec2 describe-instances --instance-id "$INSTANCE_ID" \
--query "Reservations[0].Instances[0].InstanceType" --output text)
AVAIL_ZONE=$(aws ec2 describe-instances --instance-id "$INSTANCE_ID" \
--query "Reservations[0].Instances[0].Placement.AvailabilityZone" --output text)
echo "Name: $INSTANCE_NAME"
echo "Type: $INSTANCE_TYPE"
echo "AZ: $AVAIL_ZONE"
# ---- GET VOLUMES ----
VOLUME_IDS=$(aws ec2 describe-instances --instance-id "$INSTANCE_ID" \
--query "Reservations[0].Instances[0].BlockDeviceMappings[*].Ebs.VolumeId" \
--output text)
echo "Attached Volumes: $VOLUME_IDS"
# ---- DELETE ANY OLD SNAPSHOTS ----
echo "🧹 Checking for old snapshots of attached volumes..."
for VOL in $VOLUME_IDS; do
OLD_SNAPSHOTS=$(aws ec2 describe-snapshots --owner-ids self \
--filters "Name=volume-id,Values=$VOL" \
--query "Snapshots[*].SnapshotId" --output text)
if [ -n "$OLD_SNAPSHOTS" ]; then
echo "⚠ Found snapshots for volume $VOL: $OLD_SNAPSHOTS"
for SNAP in $OLD_SNAPSHOTS; do
echo " β†’ Attempting to delete snapshot $SNAP"
DELETE_OUTPUT=$(aws ec2 delete-snapshot --snapshot-id "$SNAP" 2>&1) || true
if echo "$DELETE_OUTPUT" | grep -q "AWS Backup"; then
echo " ⚠ Snapshot is managed by AWS Backup - using Backup API..."
# Get recovery point ARN from AWS Backup
RECOVERY_POINT=$(aws backup list-recovery-points-by-resource \
--resource-arn "arn:aws:ec2:*::snapshot/$SNAP" \
--query "RecoveryPoints[0].[RecoveryPointArn,BackupVaultName]" \
--output text 2>/dev/null || echo "")
if [ -n "$RECOVERY_POINT" ]; then
RECOVERY_ARN=$(echo "$RECOVERY_POINT" | awk '{print $1}')
VAULT_NAME=$(echo "$RECOVERY_POINT" | awk '{print $2}')
echo " β†’ Deleting from backup vault: $VAULT_NAME"
if aws backup delete-recovery-point \
--backup-vault-name "$VAULT_NAME" \
--recovery-point-arn "$RECOVERY_ARN" 2>/dev/null; then
echo " βœ” Deleted from AWS Backup"
else
echo " ❌ Failed to delete from AWS Backup (check permissions)"
fi
else
echo " ❌ Could not find recovery point in AWS Backup"
fi
elif echo "$DELETE_OUTPUT" | grep -q "error"; then
echo " ❌ Error: $DELETE_OUTPUT"
else
echo " βœ” Deleted successfully (EC2 API)"
fi
done
else
echo "β„Ή No old snapshots found for volume $VOL"
fi
done
# ---- CREATE SNAPSHOTS ----
SNAPSHOT_IDS=()
echo "πŸ“Έ Creating fresh snapshots..."
for VOL in $VOLUME_IDS; do
DESC="Backup ($INSTANCE_NAME - $INSTANCE_ID) | Type: $INSTANCE_TYPE | AZ: $AVAIL_ZONE"
SNAP_ID=$(aws ec2 create-snapshot --volume-id "$VOL" --description "$DESC" \
--tag-specifications "ResourceType=snapshot,Tags=[{Key=InstanceName,Value=$INSTANCE_NAME},{Key=InstanceId,Value=$INSTANCE_ID},{Key=InstanceType,Value=$INSTANCE_TYPE},{Key=AvailabilityZone,Value=$AVAIL_ZONE}]" \
--query SnapshotId --output text)
SNAPSHOT_IDS+=("$SNAP_ID")
echo " β†’ Created snapshot $SNAP_ID for volume $VOL"
done
# ---- WAIT FOR SNAPSHOTS WITH FAILURE CHECK ----
echo "⏳ Waiting for snapshots to complete..."
for SNAP_ID in "${SNAPSHOT_IDS[@]}"; do
if ! aws ec2 wait snapshot-completed --snapshot-ids "$SNAP_ID"; then
echo "❌ Snapshot FAILED: $SNAP_ID"
echo "🧹 Rolling back newly created snapshots..."
for S in "${SNAPSHOT_IDS[@]}"; do
echo " β†’ Deleting snapshot $S"
aws ec2 delete-snapshot --snapshot-id "$S" || true
done
echo "❌ Exiting without terminating instance."
return 1
fi
echo " βœ” Snapshot completed: $SNAP_ID"
done
# ---- TERMINATION PROTECTION CHECK ----
TERM_PROTECT=$(aws ec2 describe-instance-attribute \
--instance-id "$INSTANCE_ID" \
--attribute disableApiTermination \
--query "DisableApiTermination.Value" --output text)
echo "πŸ” Termination protection status: $TERM_PROTECT"
# Convert to lowercase for case-insensitive comparison
TERM_PROTECT_LOWER=$(echo "$TERM_PROTECT" | tr '[:upper:]' '[:lower:]')
if [ "$TERM_PROTECT_LOWER" == "true" ]; then
echo "⚠ Termination protection is ENABLED β†’ Disabling..."
if aws ec2 modify-instance-attribute \
--instance-id "$INSTANCE_ID" \
--no-disable-api-termination; then
echo "βœ” Termination protection disable command sent."
# Wait for the change to propagate
echo "⏳ Waiting for change to propagate..."
sleep 5
# Verify it's actually disabled
TERM_PROTECT_CHECK=$(aws ec2 describe-instance-attribute \
--instance-id "$INSTANCE_ID" \
--attribute disableApiTermination \
--query "DisableApiTermination.Value" --output text | tr '[:upper:]' '[:lower:]')
if [ "$TERM_PROTECT_CHECK" == "false" ]; then
echo "βœ” Termination protection successfully disabled."
else
echo "⚠ Warning: Termination protection still showing as enabled: $TERM_PROTECT_CHECK"
fi
else
echo "❌ Failed to disable termination protection!"
return 1
fi
else
echo "βœ” Termination protection already disabled."
fi
# ---- RELEASE EIP ----
EIP_ALLOC=$(aws ec2 describe-addresses \
--filters "Name=instance-id,Values=$INSTANCE_ID" \
--query "Addresses[0].AllocationId" --output text)
# ---- TERMINATE INSTANCE ----
echo "πŸ›‘ Terminating instance..."
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" >/dev/null
aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID"
echo "βœ” Instance terminated."
# ---- RELEASE EIP IF EXISTS ----
if [ -n "$EIP_ALLOC" ] && [ "$EIP_ALLOC" != "None" ]; then
echo "πŸ”— Releasing Elastic IP: $EIP_ALLOC"
aws ec2 release-address --allocation-id "$EIP_ALLOC"
echo "βœ” Elastic IP released."
fi
# ---- VERIFY ALL VOLUMES ARE DELETED ----
echo "πŸ” Verifying EBS volumes have been deleted..."
sleep 3 # Allow time for volume deletion to propagate
for VOL in $VOLUME_IDS; do
if aws ec2 describe-volumes --volume-ids "$VOL" >/dev/null 2>&1; then
echo "❌ Volume still exists: $VOL"
echo "⚠ Possible issue: Volume had DeleteOnTermination=false"
else
echo "βœ” Volume deleted: $VOL"
fi
done
echo "πŸŽ‰ SUCCESS: Backup + Cleanup completed for $INSTANCE_ID"
echo "----------------------------------------------"
}
# ------- LOOP OVER INSTANCES -------
for instance in "$@"; do
snapshot_and_cleanup "$instance"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment