Created
November 13, 2025 15:37
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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