Skip to content

Instantly share code, notes, and snippets.

@sebassdc
Created June 12, 2025 15:43
Show Gist options
  • Save sebassdc/cf7ee579c1a30c1961d8286d49f4ee43 to your computer and use it in GitHub Desktop.
Save sebassdc/cf7ee579c1a30c1961d8286d49f4ee43 to your computer and use it in GitHub Desktop.
AWS VPC nuke script (nuke-vpc.sh)
#!/usr/bin/env bash
# Bash-3-friendly VPC-nuker with SG-reference discovery
set -euo pipefail
export AWS_PAGER=""
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <vpc-id>" >&2
exit 1
fi
VPC_ID="$1"
echo "πŸ”¨ Attempting to delete VPC $VPC_ID …"
if aws ec2 delete-vpc --vpc-id "$VPC_ID" --no-cli-pager --output text 2>/dev/null; then
echo "βœ… VPC $VPC_ID deleted successfully."
exit 0
fi
echo "⚠️ delete-vpc failed – dependencies remain."
# ─────────────────────────────────────────────────────────────
# Gather blockers
# ─────────────────────────────────────────────────────────────
IGWS=$(aws ec2 describe-internet-gateways \
--filters Name=attachment.vpc-id,Values="$VPC_ID" \
--query 'InternetGateways[].InternetGatewayId' --output text)
NAT_GWS=$(aws ec2 describe-nat-gateways \
--filter Name=vpc-id,Values="$VPC_ID" \
--query 'NatGateways[].NatGatewayId' --output text)
ENDPOINTS=$(aws ec2 describe-vpc-endpoints \
--filters Name=vpc-id,Values="$VPC_ID" \
--query 'VpcEndpoints[].VpcEndpointId' --output text)
RTBLS=$(aws ec2 describe-route-tables \
--filters Name=vpc-id,Values="$VPC_ID" \
--query 'RouteTables[?Associations[?Main==`false`]].RouteTableId' \
--output text)
SUBNETS=$(aws ec2 describe-subnets \
--filters Name=vpc-id,Values="$VPC_ID" \
--query 'Subnets[].SubnetId' --output text)
# All SGs in the VPC, *including* the default one (needed for reference checks)
ALL_SGS=$(aws ec2 describe-security-groups \
--filters Name=vpc-id,Values="$VPC_ID" \
--query 'SecurityGroups[].GroupId' --output text)
# Non-default SGs (the only ones you can delete)
NON_DEFAULT_SGS=$(aws ec2 describe-security-groups \
--filters Name=vpc-id,Values="$VPC_ID" \
--query 'SecurityGroups[?GroupName!=`default`].GroupId' \
--output text)
# ─────────────────────────────────────────────────────────────
# Print dependency tree
# ─────────────────────────────────────────────────────────────
echo
echo "🧩 Dependency tree for $VPC_ID"
echo "$VPC_ID"
# Internet Gateways
if [[ -n "$IGWS" ]]; then
echo "└─┬ InternetGateways"
for igw in $IGWS; do echo " └── $igw"; done
fi
# NAT Gateways
if [[ -n "$NAT_GWS" ]]; then
echo "└─┬ NatGateways"
for nat in $NAT_GWS; do echo " └── $nat"; done
fi
# VPC Endpoints
if [[ -n "$ENDPOINTS" ]]; then
echo "└─┬ VpcEndpoints"
for vpce in $ENDPOINTS; do echo " └── $vpce"; done
fi
# Route Tables
if [[ -n "$RTBLS" ]]; then
echo "└─┬ RouteTables (non-main)"
for rt in $RTBLS; do echo " └── $rt"; done
fi
# Subnets β†’ ENIs
if [[ -n "$SUBNETS" ]]; then
echo "└─┬ Subnets"
for s in $SUBNETS; do
echo " └─┬ $s"
ENIS=$(aws ec2 describe-network-interfaces \
--filters Name=subnet-id,Values="$s" \
--query 'NetworkInterfaces[].NetworkInterfaceId' --output text)
if [[ -n "$ENIS" ]]; then
for eni in $ENIS; do echo " └── ENI: $eni"; done
else
echo " └── (no ENIs)"
fi
done
fi
# Security Groups β†’ ENIs + Referenced-By
if [[ -n "$NON_DEFAULT_SGS" ]]; then
echo "└─┬ SecurityGroups (non-default)"
for sg in $NON_DEFAULT_SGS; do
echo " └─┬ $sg"
# Attached ENIs
SG_ENIS=$(aws ec2 describe-network-interfaces \
--filters Name=group-id,Values="$sg" \
--query 'NetworkInterfaces[].NetworkInterfaceId' --output text)
if [[ -n "$SG_ENIS" ]]; then
for eni in $SG_ENIS; do echo " β”‚ └── ENI: $eni"; done
else
echo " β”‚ └── (no attached ENIs)"
fi
# Other SGs whose rules reference this SG (ingress + egress)
REF_ING=$(aws ec2 describe-security-groups \
--filters Name=ip-permission.group-id,Values="$sg" \
--query 'SecurityGroups[].GroupId' --output text)
REF_EGR=$(aws ec2 describe-security-groups \
--filters Name=egress.ip-permission.group-id,Values="$sg" \
--query 'SecurityGroups[].GroupId' --output text)
REFS=$(echo "$REF_ING $REF_EGR" | tr ' ' '\n' | grep -v "^$" | sort -u)
if [[ -n "$REFS" ]]; then
echo " β”‚ └─┬ ReferencedBy"
for ref in $REFS; do
# Show whether the reference is ingress/egress/both
ING=$(echo "$REF_ING" | grep -q "$ref" && echo "ingress")
EGR=$(echo "$REF_EGR" | grep -q "$ref" && echo "${ING:+,}egress")
[[ -z "$EGR" ]] && [[ -n "$ING" ]] && EGR="$ING"
echo " β”‚ └── $ref ($EGR)"
done
fi
done
fi
echo
cat <<EOF
πŸ’‘ To fully delete the VPC:
β€’ Detach / delete each resource above (including SG references).
β€’ Then rerun: aws ec2 delete-vpc --vpc-id $VPC_ID --no-cli-pager
Tip for revoking SG references:
aws ec2 describe-security-group-rules \\
--filters Name=group-id,Values=<referencing-sg> \\
--query 'SecurityGroupRules[?ReferencedGroupInfo && ReferencedGroupInfo.GroupId==\`<blocked-sg>\`].SecurityGroupRuleId' \\
--output text --no-cli-pager | \\
xargs -I{} aws ec2 revoke-security-group-ingress \\
--group-id <referencing-sg> --security-group-rule-ids {} --no-cli-pager
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment