Skip to content

Commit edadfd3

Browse files
authored
Merge pull request #326 from aws-samples/enhance-delete
Enhance stack deletion to cleanup dangling resources
2 parents 2fb1e31 + 5c971f3 commit edadfd3

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

PetAdoptions/cdk/pet_stack/resources/destroy_stack.sh

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,168 @@ echo ---------------------------------------------------------------------------
44
echo This script destroys the resources created in the workshop
55
echo ---------------------------------------------------------------------------------------------
66

7+
# Parse command line arguments
8+
FORCE_CLEANUP=false
9+
while [[ $# -gt 0 ]]; do
10+
case $1 in
11+
-f|--force)
12+
FORCE_CLEANUP=true
13+
echo "🚨 Force cleanup mode enabled - will delete dangling resources tagged with Workshop=true"
14+
shift
15+
;;
16+
-h|--help)
17+
echo "Usage: $0 [-f|--force] [-h|--help]"
18+
echo " -f, --force Enable force cleanup of dangling resources (use with caution)"
19+
echo " -h, --help Show this help message"
20+
exit 0
21+
;;
22+
*)
23+
echo "Unknown option $1"
24+
echo "Use -h or --help for usage information"
25+
exit 1
26+
;;
27+
esac
28+
done
29+
730
if [ -z "$AWS_REGION" ]; then
831
echo "Fatal: environment variable AWS_REGION not set. Aborting."
932
exit 1
1033
fi
1134

35+
# Function to check if stack deletion failed
36+
check_stack_deletion_status() {
37+
local stack_name=$1
38+
local status=$(aws cloudformation describe-stacks --stack-name "$stack_name" --query 'Stacks[0].StackStatus' --output text 2>/dev/null)
39+
40+
if [[ "$status" == "DELETE_FAILED" ]]; then
41+
echo "⚠️ Stack $stack_name deletion failed. Status: $status"
42+
return 1
43+
elif [[ "$status" == "DELETE_COMPLETE" ]] || [[ -z "$status" ]]; then
44+
echo "✅ Stack $stack_name deleted successfully or doesn't exist"
45+
return 0
46+
else
47+
echo "ℹ️ Stack $stack_name status: $status"
48+
return 0
49+
fi
50+
}
51+
52+
# Function to force delete EKS cluster
53+
force_delete_eks_cluster() {
54+
echo "🔍 Checking for EKS clusters tagged with Workshop=true..."
55+
56+
local clusters=$(aws eks list-clusters --query 'clusters[]' --output text)
57+
58+
for cluster in $clusters; do
59+
local tags=$(aws eks list-tags-for-resource --resource-arn "arn:aws:eks:$AWS_REGION:$(aws sts get-caller-identity --query Account --output text):cluster/$cluster" --query 'tags.Workshop' --output text 2>/dev/null)
60+
61+
if [[ "$tags" == "true" ]]; then
62+
echo "🗑️ Force deleting EKS cluster: $cluster"
63+
64+
# Delete node groups first
65+
local nodegroups=$(aws eks list-nodegroups --cluster-name "$cluster" --query 'nodegroups[]' --output text 2>/dev/null)
66+
for ng in $nodegroups; do
67+
echo " Deleting node group: $ng"
68+
aws eks delete-nodegroup --cluster-name "$cluster" --nodegroup-name "$ng" 2>/dev/null
69+
done
70+
71+
# Wait for node groups to be deleted
72+
for ng in $nodegroups; do
73+
echo " Waiting for node group $ng to be deleted..."
74+
aws eks wait nodegroup-deleted --cluster-name "$cluster" --nodegroup-name "$ng" 2>/dev/null
75+
done
76+
77+
# Delete the cluster
78+
aws eks delete-cluster --name "$cluster"
79+
echo " Waiting for cluster $cluster to be deleted..."
80+
aws eks wait cluster-deleted --name "$cluster"
81+
echo "✅ EKS cluster $cluster deleted"
82+
fi
83+
done
84+
}
85+
86+
# Function to force delete VPC and related resources
87+
force_delete_vpc_resources() {
88+
echo "🔍 Checking for VPCs tagged with Workshop=true..."
89+
90+
local vpcs=$(aws ec2 describe-vpcs --filters "Name=tag:Workshop,Values=true" --query 'Vpcs[].VpcId' --output text)
91+
92+
for vpc in $vpcs; do
93+
echo "🗑️ Force deleting VPC and related resources: $vpc"
94+
95+
# Delete NAT Gateways
96+
local nat_gateways=$(aws ec2 describe-nat-gateways --filter "Name=vpc-id,Values=$vpc" --query 'NatGateways[?State==`available`].NatGatewayId' --output text)
97+
for nat in $nat_gateways; do
98+
echo " Deleting NAT Gateway: $nat"
99+
aws ec2 delete-nat-gateway --nat-gateway-id "$nat"
100+
done
101+
102+
# Wait for NAT Gateways to be deleted
103+
for nat in $nat_gateways; do
104+
echo " Waiting for NAT Gateway $nat to be deleted..."
105+
aws ec2 wait nat-gateway-deleted --nat-gateway-ids "$nat" 2>/dev/null || true
106+
done
107+
108+
# Delete Internet Gateway
109+
local igws=$(aws ec2 describe-internet-gateways --filters "Name=attachment.vpc-id,Values=$vpc" --query 'InternetGateways[].InternetGatewayId' --output text)
110+
for igw in $igws; do
111+
echo " Detaching and deleting Internet Gateway: $igw"
112+
aws ec2 detach-internet-gateway --internet-gateway-id "$igw" --vpc-id "$vpc" 2>/dev/null
113+
aws ec2 delete-internet-gateway --internet-gateway-id "$igw" 2>/dev/null
114+
done
115+
116+
# Delete subnets
117+
local subnets=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$vpc" --query 'Subnets[].SubnetId' --output text)
118+
for subnet in $subnets; do
119+
echo " Deleting subnet: $subnet"
120+
aws ec2 delete-subnet --subnet-id "$subnet" 2>/dev/null
121+
done
122+
123+
# Delete route tables (except main)
124+
local route_tables=$(aws ec2 describe-route-tables --filters "Name=vpc-id,Values=$vpc" --query 'RouteTables[?Associations[0].Main!=`true`].RouteTableId' --output text)
125+
for rt in $route_tables; do
126+
echo " Deleting route table: $rt"
127+
aws ec2 delete-route-table --route-table-id "$rt" 2>/dev/null
128+
done
129+
130+
# Delete security groups (except default)
131+
local security_groups=$(aws ec2 describe-security-groups --filters "Name=vpc-id,Values=$vpc" --query 'SecurityGroups[?GroupName!=`default`].GroupId' --output text)
132+
for sg in $security_groups; do
133+
echo " Deleting security group: $sg"
134+
aws ec2 delete-security-group --group-id "$sg" 2>/dev/null
135+
done
136+
137+
# Delete VPC
138+
echo " Deleting VPC: $vpc"
139+
aws ec2 delete-vpc --vpc-id "$vpc" 2>/dev/null
140+
echo "✅ VPC $vpc and related resources deleted"
141+
done
142+
}
143+
144+
# Function to delete other tagged resources
145+
force_delete_other_resources() {
146+
echo "🔍 Checking for other resources tagged with Workshop=true..."
147+
148+
# Delete Load Balancers
149+
local elbs=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[].LoadBalancerArn' --output text)
150+
for elb_arn in $elbs; do
151+
local tags=$(aws elbv2 describe-tags --resource-arns "$elb_arn" --query 'TagDescriptions[0].Tags[?Key==`Workshop`].Value' --output text 2>/dev/null)
152+
if [[ "$tags" == "true" ]]; then
153+
echo "🗑️ Deleting Load Balancer: $elb_arn"
154+
aws elbv2 delete-load-balancer --load-balancer-arn "$elb_arn"
155+
fi
156+
done
157+
158+
# Delete RDS instances
159+
local rds_instances=$(aws rds describe-db-instances --query 'DBInstances[].DBInstanceIdentifier' --output text)
160+
for db in $rds_instances; do
161+
local tags=$(aws rds list-tags-for-resource --resource-name "arn:aws:rds:$AWS_REGION:$(aws sts get-caller-identity --query Account --output text):db:$db" --query 'TagList[?Key==`Workshop`].Value' --output text 2>/dev/null)
162+
if [[ "$tags" == "true" ]]; then
163+
echo "🗑️ Deleting RDS instance: $db"
164+
aws rds delete-db-instance --db-instance-identifier "$db" --skip-final-snapshot --delete-automated-backups
165+
fi
166+
done
167+
}
168+
12169
# Disable Contributor Insights
13170
DDB_CONTRIB=$(aws ssm get-parameter --name '/petstore/dynamodbtablename' | jq .Parameter.Value -r)
14171
aws dynamodb update-contributor-insights --table-name $DDB_CONTRIB --contributor-insights-action DISABLE
@@ -43,15 +200,49 @@ kubectl delete namespace keycloak --force
43200
# Sometimes the SqlSeeder doesn't get deleted cleanly. This helps clean up the environment completely including Sqlseeder
44201
aws cloudformation delete-stack --stack-name $STACK_NAME_APP
45202
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME_APP
203+
204+
# Check if Applications stack deletion failed and force cleanup if needed
205+
if ! check_stack_deletion_status "$STACK_NAME_APP"; then
206+
if [ "$FORCE_CLEANUP" = true ]; then
207+
echo "🚨 Applications stack deletion failed. Starting force cleanup..."
208+
force_delete_eks_cluster
209+
force_delete_vpc_resources
210+
force_delete_other_resources
211+
else
212+
echo "⚠️ Applications stack deletion failed. Use -f flag to enable force cleanup of dangling resources."
213+
fi
214+
fi
215+
46216
aws cloudformation delete-stack --stack-name $STACK_NAME
47217
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME
48218

219+
# Check if Services stack deletion failed and force cleanup if needed
220+
if ! check_stack_deletion_status "$STACK_NAME"; then
221+
if [ "$FORCE_CLEANUP" = true ]; then
222+
echo "🚨 Services stack deletion failed. Starting force cleanup..."
223+
force_delete_vpc_resources
224+
force_delete_other_resources
225+
else
226+
echo "⚠️ Services stack deletion failed. Use -f flag to enable force cleanup of dangling resources."
227+
fi
228+
fi
229+
49230
aws cloudwatch delete-dashboards --dashboard-names "EKS_FluentBit_Dashboard"
50231

51232
# delete the code pipeline stack
52233
aws cloudformation delete-stack --stack-name $STACK_NAME_CODEPIPELINE
53234
aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME_CODEPIPELINE
54235

236+
# Check if CodePipeline stack deletion failed and force cleanup if needed
237+
if ! check_stack_deletion_status "$STACK_NAME_CODEPIPELINE"; then
238+
if [ "$FORCE_CLEANUP" = true ]; then
239+
echo "🚨 CodePipeline stack deletion failed. Starting force cleanup..."
240+
force_delete_other_resources
241+
else
242+
echo "⚠️ CodePipeline stack deletion failed. Use -f flag to enable force cleanup of dangling resources."
243+
fi
244+
fi
245+
55246
echo CDK BOOTSTRAP WAS NOT DELETED
56247

57248
echo ----- ✅ DONE --------

0 commit comments

Comments
 (0)