Merakit-Deploy/scripts/cloudflare-remove.sh

328 lines
8.2 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Cloudflare API credentials
CF_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}"
CF_ZONE_ID="${CLOUDFLARE_ZONE_ID:-}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
usage() {
echo "Usage: $0 --hostname <hostname>"
echo " $0 --record-id <record_id>"
echo " $0 --all-matching <pattern>"
echo ""
echo "Options:"
echo " --hostname Remove DNS record by hostname (e.g., test.example.com)"
echo " --record-id Remove DNS record by Cloudflare record ID"
echo " --all-matching Remove all DNS records matching pattern (e.g., '*.example.com')"
echo ""
echo "Environment variables required:"
echo " CLOUDFLARE_API_TOKEN"
echo " CLOUDFLARE_ZONE_ID"
exit 1
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
}
log_info() {
echo -e "${YELLOW}[INFO]${NC} $1" >&2
}
check_requirements() {
if [[ -z "$CF_API_TOKEN" ]]; then
log_error "CLOUDFLARE_API_TOKEN environment variable not set"
exit 1
fi
if [[ -z "$CF_ZONE_ID" ]]; then
log_error "CLOUDFLARE_ZONE_ID environment variable not set"
exit 1
fi
if ! command -v curl &> /dev/null; then
log_error "curl is required but not installed"
exit 1
fi
if ! command -v jq &> /dev/null; then
log_error "jq is required but not installed"
exit 1
fi
}
get_dns_records_by_hostname() {
local hostname=$1
log_info "Looking up DNS records for: $hostname"
local response=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?name=${hostname}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
local success=$(echo "$response" | jq -r '.success')
if [[ "$success" != "true" ]]; then
log_error "Cloudflare API request failed"
echo "$response" | jq '.'
exit 1
fi
echo "$response"
}
get_all_dns_records() {
log_info "Fetching all DNS records in zone"
local response=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?per_page=1000" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
local success=$(echo "$response" | jq -r '.success')
if [[ "$success" != "true" ]]; then
log_error "Cloudflare API request failed"
echo "$response" | jq '.'
exit 1
fi
echo "$response"
}
delete_dns_record() {
local record_id=$1
local hostname=$2
log_info "Deleting DNS record: $hostname (ID: $record_id)"
local response=$(curl -s -X DELETE \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${record_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
local success=$(echo "$response" | jq -r '.success')
if [[ "$success" == "true" ]]; then
log_success "DNS record deleted successfully: $hostname (ID: $record_id)"
return 0
else
log_error "Failed to delete DNS record: $hostname (ID: $record_id)"
echo "$response" | jq '.'
return 1
fi
}
delete_by_hostname() {
local hostname=$1
local response=$(get_dns_records_by_hostname "$hostname")
local count=$(echo "$response" | jq -r '.result | length')
if [[ "$count" -eq 0 ]]; then
log_error "No DNS records found for: $hostname"
exit 1
fi
log_info "Found $count record(s) for: $hostname"
local deleted=0
local failed=0
while IFS= read -r record; do
local record_id=$(echo "$record" | jq -r '.id')
local record_name=$(echo "$record" | jq -r '.name')
local record_type=$(echo "$record" | jq -r '.type')
local record_content=$(echo "$record" | jq -r '.content')
log_info "Found: $record_name ($record_type) -> $record_content"
if delete_dns_record "$record_id" "$record_name"; then
deleted=$((deleted + 1))
else
failed=$((failed + 1))
fi
done < <(echo "$response" | jq -c '.result[]')
log_info "Summary: $deleted deleted, $failed failed"
if [[ $failed -gt 0 ]]; then
exit 1
fi
}
delete_by_record_id() {
local record_id=$1
# First, get the record details
log_info "Fetching record details for ID: $record_id"
local response=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${record_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
local success=$(echo "$response" | jq -r '.success')
if [[ "$success" != "true" ]]; then
log_error "Record not found or API request failed"
echo "$response" | jq '.'
exit 1
fi
local hostname=$(echo "$response" | jq -r '.result.name')
local record_type=$(echo "$response" | jq -r '.result.type')
local content=$(echo "$response" | jq -r '.result.content')
log_info "Record found: $hostname ($record_type) -> $content"
delete_dns_record "$record_id" "$hostname"
}
delete_all_matching() {
local pattern=$1
log_info "Searching for records matching pattern: $pattern"
local response=$(get_all_dns_records)
local all_records=$(echo "$response" | jq -c '.result[]')
local matching_records=()
while IFS= read -r record; do
local record_name=$(echo "$record" | jq -r '.name')
# Simple pattern matching (supports * wildcard)
if [[ "$pattern" == *"*"* ]]; then
# Convert pattern to regex
local regex="${pattern//\*/.*}"
if [[ "$record_name" =~ ^${regex}$ ]]; then
matching_records+=("$record")
fi
else
# Exact match
if [[ "$record_name" == "$pattern" ]]; then
matching_records+=("$record")
fi
fi
done < <(echo "$all_records")
local count=${#matching_records[@]}
if [[ $count -eq 0 ]]; then
log_error "No DNS records found matching pattern: $pattern"
exit 1
fi
log_info "Found $count record(s) matching pattern: $pattern"
# List matching records
for record in "${matching_records[@]}"; do
local record_name=$(echo "$record" | jq -r '.name')
local record_type=$(echo "$record" | jq -r '.type')
local content=$(echo "$record" | jq -r '.content')
log_info " - $record_name ($record_type) -> $content"
done
# Confirm deletion
echo ""
read -p "Delete all $count record(s)? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Deletion cancelled"
exit 0
fi
local deleted=0
local failed=0
for record in "${matching_records[@]}"; do
local record_id=$(echo "$record" | jq -r '.id')
local record_name=$(echo "$record" | jq -r '.name')
if delete_dns_record "$record_id" "$record_name"; then
deleted=$((deleted + 1))
else
failed=$((failed + 1))
fi
done
log_info "Summary: $deleted deleted, $failed failed"
if [[ $failed -gt 0 ]]; then
exit 1
fi
}
# Parse arguments
HOSTNAME=""
RECORD_ID=""
PATTERN=""
MODE=""
while [[ $# -gt 0 ]]; do
case $1 in
--hostname)
HOSTNAME="$2"
MODE="hostname"
shift 2
;;
--record-id)
RECORD_ID="$2"
MODE="record-id"
shift 2
;;
--all-matching)
PATTERN="$2"
MODE="pattern"
shift 2
;;
-h|--help)
usage
;;
*)
log_error "Unknown option: $1"
usage
;;
esac
done
# Validate arguments
if [[ -z "$MODE" ]]; then
log_error "No deletion mode specified"
usage
fi
# Check requirements
check_requirements
# Execute based on mode
case $MODE in
hostname)
delete_by_hostname "$HOSTNAME"
;;
record-id)
delete_by_record_id "$RECORD_ID"
;;
pattern)
delete_all_matching "$PATTERN"
;;
*)
log_error "Invalid mode: $MODE"
exit 1
;;
esac