Skip to content

Instantly share code, notes, and snippets.

@ShoGinn
Last active July 9, 2025 14:00
Show Gist options
  • Save ShoGinn/61d051d03711671c8c6d7856e47392ff to your computer and use it in GitHub Desktop.
Save ShoGinn/61d051d03711671c8c6d7856e47392ff to your computer and use it in GitHub Desktop.
Cleanup Resources on AWS
#!/bin/bash
# AWS Resource Cleanup Script
# This script removes AWS resources containing a selected pattern in their names
# USE WITH CAUTION - This will permanently delete resources
set -e # Exit on any error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
SEARCH_PATTERN=""
DRY_RUN=false
REGION="us-east-1"
# Function to show usage
show_usage() {
cat <<EOF
AWS Resource Cleanup Script
DESCRIPTION:
This script removes AWS resources containing a specific pattern in their names.
It can clean up S3 buckets, IAM roles, IAM policies, and IAM instance profiles.
⚠️ WARNING: This will PERMANENTLY DELETE AWS resources! ⚠️
USAGE:
$0 --pattern PATTERN [OPTIONS]
REQUIRED:
--pattern PATTERN Search pattern to match resource names (e.g., "myproject-", "dev-")
OPTIONS:
--dry-run Show what would be deleted without actually deleting
--region REGION AWS region (default: us-east-1)
--help Show this help message
EXAMPLES:
# Dry run to see what would be deleted
$0 --pattern "myproject-" --dry-run
# Clean up all resources with "dev-" prefix in us-west-2
$0 --pattern "dev-" --region us-west-2
# Clean up test environment resources
$0 --pattern "test-env-" --dry-run
PREREQUISITES:
- AWS CLI installed and configured
- jq installed (brew install jq)
- Appropriate AWS permissions for the resources you want to delete
WHAT IT CLEANS:
✓ S3 buckets (including versioned objects)
✓ IAM roles (detaches policies first)
✓ IAM policies (customer managed only)
✓ IAM instance profiles
EOF
}
# Function to print colored output
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
# Function to validate required parameters
validate_parameters() {
if [ -z "$SEARCH_PATTERN" ]; then
print_error "Search pattern is required!"
echo
show_usage
exit 1
fi
if [ ${#SEARCH_PATTERN} -lt 2 ]; then
print_error "Search pattern must be at least 2 characters long for safety"
exit 1
fi
print_info "Search pattern: '$SEARCH_PATTERN'"
print_info "AWS Region: $REGION"
if [ "$DRY_RUN" = true ]; then
print_warning "DRY RUN MODE - No resources will be deleted"
fi
}
confirm_deletion() {
if [ "$DRY_RUN" = true ]; then
return 0
fi
read -r -p "Are you sure you want to delete these resources? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
print_info "Skipping deletion..."
return 1
fi
return 0
}
# 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"
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
# Check for jq
if ! command -v jq &>/dev/null; then
print_error "jq is not installed. Please install it with: brew install jq"
exit 1
fi
print_info "AWS CLI is configured for account: $(aws sts get-caller-identity --query Account --output text)"
}
# Remove S3 buckets
cleanup_s3_buckets() {
print_info "Finding S3 buckets containing '$SEARCH_PATTERN'..."
buckets=$(aws s3api list-buckets --query "Buckets[?contains(Name, '$SEARCH_PATTERN')].Name" --output text)
if [ -z "$buckets" ]; then
print_info "No S3 buckets found containing '$SEARCH_PATTERN'"
return
fi
echo "Found S3 buckets:"
for bucket in $buckets; do
echo " - $bucket"
done
if confirm_deletion; then
for bucket in $buckets; do
if [ "$DRY_RUN" = true ]; then
print_info "[DRY RUN] Would delete S3 bucket: $bucket"
else
print_info "Emptying and deleting S3 bucket: $bucket"
# Delete all object versions and delete markers
print_info "Deleting all object versions in bucket: $bucket"
aws s3api list-object-versions --bucket "$bucket" --output json |
jq -r '.Versions[]?, .DeleteMarkers[]? | select(.Key != null) | "\(.Key) \(.VersionId)"' |
while read -r key version; do
if [ -n "$key" ] && [ -n "$version" ]; then
aws s3api delete-object --bucket "$bucket" --key "$key" --version-id "$version" >/dev/null
fi
done
# Also try the simple recursive delete for any remaining objects
aws s3 rm s3://"$bucket" --recursive >/dev/null 2>&1 || true
# Delete bucket
if aws s3api delete-bucket --bucket "$bucket"; then
print_success "Successfully deleted bucket: $bucket"
else
print_error "Failed to delete bucket $bucket"
fi
fi
done
fi
}
# Remove IAM roles
cleanup_iam_roles() {
print_info "Finding IAM roles containing '$SEARCH_PATTERN'..."
roles=$(aws iam list-roles --query "Roles[?contains(RoleName, '$SEARCH_PATTERN')].RoleName" --output text)
if [ -z "$roles" ]; then
print_info "No IAM roles found containing '$SEARCH_PATTERN'"
return
fi
echo "Found IAM roles:"
for role in $roles; do
echo " - $role"
done
if confirm_deletion; then
for role in $roles; do
if [ "$DRY_RUN" = true ]; then
print_info "[DRY RUN] Would delete IAM role: $role"
else
print_info "Deleting IAM role: $role"
# Detach managed policies
attached_policies=$(aws iam list-attached-role-policies --role-name "$role" --query 'AttachedPolicies[].PolicyArn' --output text)
for policy in $attached_policies; do
aws iam detach-role-policy --role-name "$role" --policy-arn "$policy"
done
# Delete inline policies
inline_policies=$(aws iam list-role-policies --role-name "$role" --query 'PolicyNames' --output text)
for policy in $inline_policies; do
aws iam delete-role-policy --role-name "$role" --policy-name "$policy"
done
# Remove role from instance profiles
instance_profiles=$(aws iam list-instance-profiles-for-role --role-name "$role" --query 'InstanceProfiles[].InstanceProfileName' --output text)
for profile in $instance_profiles; do
aws iam remove-role-from-instance-profile --instance-profile-name "$profile" --role-name "$role"
done
# Delete the role
if aws iam delete-role --role-name "$role"; then
print_success "Successfully deleted role: $role"
else
print_error "Failed to delete role $role"
fi
fi
done
fi
}
# Remove IAM policies
cleanup_iam_policies() {
print_info "Finding IAM policies containing '$SEARCH_PATTERN'..."
# Get policies in a more reliable format
policies=$(aws iam list-policies --scope Local --query "Policies[?contains(PolicyName, '$SEARCH_PATTERN')]" --output json)
if [ "$policies" = "[]" ] || [ -z "$policies" ]; then
print_info "No IAM policies found containing '$SEARCH_PATTERN'"
return
fi
echo "Found IAM policies:"
echo "$policies" | jq -r '.[] | " - \(.PolicyName) (\(.Arn))"'
if confirm_deletion; then
echo "$policies" | jq -r '.[] | "\(.PolicyName) \(.Arn)"' | while read -r name arn; do
if [ "$DRY_RUN" = true ]; then
print_info "[DRY RUN] Would delete IAM policy: $name"
else
print_info "Deleting IAM policy: $name"
# Check if policy is attached to any entities before deleting
entities=$(aws iam list-entities-for-policy --policy-arn "$arn" --output json)
# Detach from roles
echo "$entities" | jq -r '.PolicyRoles[]?.RoleName // empty' | while read -r role; do
[ -n "$role" ] && aws iam detach-role-policy --role-name "$role" --policy-arn "$arn"
done
# Detach from users
echo "$entities" | jq -r '.PolicyUsers[]?.UserName // empty' | while read -r user; do
[ -n "$user" ] && aws iam detach-user-policy --user-name "$user" --policy-arn "$arn"
done
# Detach from groups
echo "$entities" | jq -r '.PolicyGroups[]?.GroupName // empty' | while read -r group; do
[ -n "$group" ] && aws iam detach-group-policy --group-name "$group" --policy-arn "$arn"
done
# Delete the policy
if aws iam delete-policy --policy-arn "$arn"; then
print_success "Successfully deleted policy: $name"
else
print_error "Failed to delete policy $name"
fi
fi
done
fi
}
# Remove IAM instance profiles
cleanup_iam_instance_profiles() {
print_info "Finding IAM instance profiles containing '$SEARCH_PATTERN'..."
profiles=$(aws iam list-instance-profiles --query "InstanceProfiles[?contains(InstanceProfileName, '$SEARCH_PATTERN')].InstanceProfileName" --output text)
if [ -z "$profiles" ]; then
print_info "No IAM instance profiles found containing '$SEARCH_PATTERN'"
return
fi
echo "Found IAM instance profiles:"
for profile in $profiles; do
echo " - $profile"
done
if confirm_deletion; then
for profile in $profiles; do
if [ "$DRY_RUN" = true ]; then
print_info "[DRY RUN] Would delete IAM instance profile: $profile"
else
print_info "Deleting IAM instance profile: $profile"
if aws iam delete-instance-profile --instance-profile-name "$profile"; then
print_success "Successfully deleted instance profile: $profile"
else
print_error "Failed to delete instance profile $profile"
fi
fi
done
fi
}
# Main function
main() {
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
--region)
if [ -z "$2" ]; then
print_error "Region value is required"
exit 1
fi
REGION="$2"
shift 2
;;
--pattern)
if [ -z "$2" ]; then
print_error "Pattern value is required"
exit 1
fi
SEARCH_PATTERN="$2"
shift 2
;;
--help | -h)
show_usage
exit 0
;;
*)
print_error "Unknown option: $1"
echo
show_usage
exit 1
;;
esac
done
# Validate required parameters
validate_parameters
print_info "Starting AWS cleanup for pattern: '$SEARCH_PATTERN' in region: $REGION"
# Set AWS region
export AWS_DEFAULT_REGION=$REGION
# Check AWS CLI and dependencies
check_aws_cli
# Warning and confirmation for non-dry-run
if [ "$DRY_RUN" = false ]; then
echo
print_warning "⚠️ DANGER ZONE ⚠️"
print_warning "This script will PERMANENTLY DELETE AWS resources!"
print_warning "Resources matching pattern: '$SEARCH_PATTERN'"
print_warning "In region: $REGION"
print_warning "Make sure you have backups and understand the consequences."
echo
read -r -p "Type 'DELETE' to confirm you want to proceed: " continue_confirm
if [ "$continue_confirm" != "DELETE" ]; then
print_info "Aborted by user - safety first!"
exit 0
fi
echo
fi
# Run cleanup functions
print_info "=== Starting resource cleanup ==="
cleanup_s3_buckets
echo
cleanup_iam_roles
echo
cleanup_iam_policies
echo
cleanup_iam_instance_profiles
echo
if [ "$DRY_RUN" = true ]; then
print_info "=== Dry run completed - no resources were deleted ==="
print_info "Run without --dry-run to actually delete these resources"
else
print_success "=== Cleanup completed! ==="
fi
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment