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

Published: (February 28, 2026 at 12:12 AM EST)
4 min read
Source: Dev.to

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:

  • NS record
  • SOA record

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 NS and SOA
  • Then delete the hosted zone

Prerequisites

  • AWS CLI v2 installed
  • jq installed
  • IAM permissions:
    • route53:ListResourceRecordSets
    • route53:ChangeResourceRecordSets
    • route53:DeleteHostedZone
    • route53: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 ID
  • traced → Optional AWS CLI profile (defaults to default)

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-run mode 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.

0 views
Back to Blog

Related posts

Read more »