DEV Community

iapilgrim
iapilgrim

Posted on

How to Delete an AWS Route 53 Hosted Zone in a Cascade Manner (Using AWS CLI)

Here’s your content formatted as a Dev.to technical blog post — structured, practical, and clean for engineers.

Deleting a hosted zone in AWS Route 53 sounds simple…

Until you see this error:

The hosted zone contains non-required resource record sets
Enter fullscreen mode Exit fullscreen mode

AWS won’t let you delete a hosted zone unless all non-default records (everything except NS & SOA) are removed first.

In this guide, I’ll show you how to safely delete a hosted zone in a cascade manner using AWS CLI.


📌 Why Cascade Deletion Is Required

When you create a hosted zone in Amazon Route 53, AWS automatically creates:

  • NS record
  • SOA record

These two are mandatory and cannot be manually deleted.

Before deleting the hosted zone, you must:

  1. Delete all custom records (A, AAAA, CNAME, MX, TXT, ALIAS, etc.)
  2. Keep only NS and SOA
  3. Then delete the hosted zone

Let’s automate that.


🛠️ Prerequisites

Make sure you have:

  • AWS CLI v2 installed
  • jq installed
  • IAM permissions:

    • route53:ListResourceRecordSets
    • route53:ChangeResourceRecordSets
    • route53:DeleteHostedZone
    • route53:GetHostedZone

🚀 The Cascade Delete Script

Create a file:

delete-hosted-zone-cascade.sh
Enter fullscreen mode Exit fullscreen mode

Paste the following:

#!/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
Enter fullscreen mode Exit fullscreen mode

▶️ How to Use It

Make it executable:

chmod +x delete-hosted-zone-cascade.sh
Enter fullscreen mode Exit fullscreen mode

Run it:

./delete-hosted-zone-cascade.sh Z123456ABCDEFG traced
Enter fullscreen mode Exit fullscreen mode

Where:

  • Z123456ABCDEFG → Your Hosted Zone ID
  • traced → Optional AWS CLI profile

🔍 What the Script Does (Step-by-Step)

1️⃣ Gets Hosted Zone Info

aws route53 get-hosted-zone
Enter fullscreen mode Exit fullscreen mode

2️⃣ Lists All Record Sets

aws route53 list-resource-record-sets
Enter fullscreen mode Exit fullscreen mode

3️⃣ Filters Out NS & SOA Using jq

Only deletes records that:

  • Are NOT NS
  • Are NOT SOA

4️⃣ Submits Batch Delete

aws route53 change-resource-record-sets
Enter fullscreen mode Exit fullscreen mode

5️⃣ Deletes the Hosted Zone

aws route53 delete-hosted-zone
Enter fullscreen mode Exit fullscreen mode

Done. Clean and automated.


⚠️ Important Notes

  • ❗ This permanently deletes all DNS records
  • ❗ Cannot be undone
  • ❗ If DNSSEC is enabled, disable it first
  • ❗ For private hosted zones, VPC associations must be handled properly

🧠 When Is This Useful?

  • CI/CD cleanup
  • Destroying ephemeral environments
  • Terraform drift cleanup
  • Multi-account migrations
  • Org account decommissioning

🛡️ Production Hardening Ideas

You may want to improve it by:

  • Adding a confirmation prompt
  • Waiting for change status using:
  aws route53 get-change
Enter fullscreen mode Exit fullscreen mode
  • Logging deletion actions
  • Adding a --dry-run mode
  • Handling pagination for zones with many records

🎯 Final Thoughts

Manually deleting DNS records in Amazon Route 53 is painful.

Automating cascade deletion:

  • Saves time
  • Prevents mistakes
  • Makes account cleanup predictable
  • Works great in automation pipelines

If you're working heavily with AWS infrastructure, this small script will save you a lot of frustration.

Top comments (0)