How to Delete an AWS Route 53 Hosted Zone in a Cascade Manner (Using AWS CLI)
Source: Dev.to
Deleting a hosted zone in AWS Route 53 sounds simple… until you encounter the error:
The hosted zone contains non‑required resource record sets
AWS won’t let you delete a hosted zone unless all non‑default records (everything except NS & SOA) are removed first. This guide shows how to safely delete a hosted zone in a cascade manner using the AWS CLI.
Why Cascade Deletion Is Required
When you create a hosted zone, Route 53 automatically creates:
NSrecordSOArecord
These two are mandatory and cannot be manually deleted. Before deleting the hosted zone you must:
- Delete all custom records (
A,AAAA,CNAME,MX,TXT,ALIAS, etc.) - Keep only
NSandSOA - Then delete the hosted zone
Prerequisites
- AWS CLI v2 installed
jqinstalled- IAM permissions:
route53:ListResourceRecordSetsroute53:ChangeResourceRecordSetsroute53:DeleteHostedZoneroute53:GetHostedZone
The Cascade Delete Script
Create a file named delete-hosted-zone-cascade.sh and paste the following script:
#!/bin/bash
# ==========================================
# Script: delete-hosted-zone-cascade.sh
# Description:
# Deletes all record sets in a Route53
# hosted zone and then deletes the zone.
# ==========================================
set -euo pipefail
HOSTED_ZONE_ID="$1"
AWS_PROFILE="${2:-default}"
if [[ -z "$HOSTED_ZONE_ID" ]]; then
echo "Usage: $0 <hosted_zone_id> [aws_profile]"
exit 1
fi
echo "Using AWS Profile: $AWS_PROFILE"
echo "Target Hosted Zone: $HOSTED_ZONE_ID"
# Step 1: Get hosted zone name
ZONE_NAME=$(aws route53 get-hosted-zone \
--id "$HOSTED_ZONE_ID" \
--profile "$AWS_PROFILE" \
--query 'HostedZone.Name' \
--output text)
echo "Hosted Zone Name: $ZONE_NAME"
# Step 2: Fetch all record sets except default NS & SOA
echo "Fetching record sets..."
aws route53 list-resource-record-sets \
--hosted-zone-id "$HOSTED_ZONE_ID" \
--profile "$AWS_PROFILE" \
--output json \
| jq '
.ResourceRecordSets
| map(select(.Type != "NS" and .Type != "SOA"))
| map({
Action: "DELETE",
ResourceRecordSet: .
})
| {Changes: .}
' > changes.json
COUNT=$(jq '.Changes | length' changes.json)
if [[ "$COUNT" -eq 0 ]]; then
echo "No non‑default records found."
else
echo "Deleting $COUNT record sets..."
aws route53 change-resource-record-sets \
--hosted-zone-id "$HOSTED_ZONE_ID" \
--change-batch file://changes.json \
--profile "$AWS_PROFILE"
sleep 10
fi
# Step 3: Delete hosted zone
echo "Deleting hosted zone..."
aws route53 delete-hosted-zone \
--id "$HOSTED_ZONE_ID" \
--profile "$AWS_PROFILE"
echo "Hosted zone $HOSTED_ZONE_ID deleted successfully."
rm -f changes.json
How to Use It
Make the script executable:
chmod +x delete-hosted-zone-cascade.sh
Run it:
./delete-hosted-zone-cascade.sh Z123456ABCDEFG traced
Z123456ABCDEFG→ Your Hosted Zone IDtraced→ Optional AWS CLI profile (defaults todefault)
What the Script Does (Step‑by‑Step)
1️⃣ Get Hosted Zone Info
aws route53 get-hosted-zone --id "$HOSTED_ZONE_ID"
2️⃣ List All Record Sets
aws route53 list-resource-record-sets --hosted-zone-id "$HOSTED_ZONE_ID"
3️⃣ Filter Out NS & SOA Using jq
Only records that are not NS and not SOA are turned into delete actions.
4️⃣ Submit Batch Delete
aws route53 change-resource-record-sets \
--hosted-zone-id "$HOSTED_ZONE_ID" \
--change-batch file://changes.json
5️⃣ Delete the Hosted Zone
aws route53 delete-hosted-zone --id "$HOSTED_ZONE_ID"
Important Notes
- This permanently deletes all DNS records; the operation cannot be undone.
- If DNSSEC is enabled, disable it before running the script.
- For private hosted zones, ensure VPC associations are handled appropriately.
When Is This Useful?
- CI/CD cleanup of temporary environments
- Destroying ephemeral test stacks
- Terraform drift remediation
- Multi‑account migrations
- Decommissioning an entire AWS organization account
Production Hardening Ideas
- Add a confirmation prompt before proceeding.
- Wait for change status using
aws route53 get-change. - Log deletion actions to a file or monitoring system.
- Implement a
--dry-runmode to preview deletions. - Handle pagination for zones with a large number of records.
Final Thoughts
Manually deleting DNS records in Amazon Route 53 is tedious and error‑prone. Automating cascade deletion:
- Saves time
- Prevents mistakes
- Makes account cleanup predictable
- Fits naturally into automation pipelines
If you work heavily with AWS infrastructure, this small script will save you a lot of frustration.