Files
linux-scripts/contabo-dns-manager.sh
T
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

649 lines
26 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#########################################################################################
#### contabo-dns-manager.sh — Manage DNS zones and records via the Contabo DNS API ####
#### List zones, add/update/delete records, audit, bulk operations ####
#### Requires: bash 4+, curl, jq ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.01 ####
#### ####
#### Usage: ####
#### ./contabo-dns-manager.sh --zones ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Colors (pre-initialized) ─────────────────────────────────────────
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
setup_colors() {
if [[ "${COLOR:-auto}" == "never" ]]; then
return
fi
if [[ "${COLOR:-auto}" == "always" ]] || [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
fi
}
# ── Logging ───────────────────────────────────────────────────────────
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${DIM}[DEBUG]${RESET} $*"; fi; }
die() { err "$*"; exit 1; }
section_header() {
echo ""
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
echo ""
}
field() {
printf " ${BOLD}%-22s${RESET} %s\n" "$1" "$2"
}
field_color() {
printf " ${BOLD}%-22s${RESET} %b\n" "$1" "$2"
}
elapsed() {
local end_time
end_time=$(date +%s)
echo "$(( end_time - START_TIME ))s"
}
# ── Defaults ──────────────────────────────────────────────────────────
RUN_MODE=""
ZONE_NAME=""
RECORD_ID=""
RECORD_TYPE=""
RECORD_NAME=""
RECORD_CONTENT=""
RECORD_TTL="3600"
RECORD_PRIO=""
CSV_FILE=""
OUTPUT_FORMAT="${CDM_FORMAT:-table}"
FORCE="false"
VERBOSE="${VERBOSE:-false}"
COLOR="${COLOR:-auto}"
# ── Credentials ───────────────────────────────────────────────────────
CONTABO_CLIENT_ID="${CONTABO_CLIENT_ID:-}"
CONTABO_CLIENT_SECRET="${CONTABO_CLIENT_SECRET:-}"
CONTABO_API_USER="${CONTABO_API_USER:-}"
CONTABO_API_PASS="${CONTABO_API_PASS:-}"
# ── State ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
START_TIME=""
ACTION_OK=0
ACTION_FAIL=0
# ── API helpers ──────────────────────────────────────────────────────
contabo_token() {
local resp
resp=$(curl -s -d "client_id=${CONTABO_CLIENT_ID}" \
-d "client_secret=${CONTABO_CLIENT_SECRET}" \
--data-urlencode "username=${CONTABO_API_USER}" \
--data-urlencode "password=${CONTABO_API_PASS}" \
-d "grant_type=password" \
"https://auth.contabo.com/auth/realms/contabo/protocol/openid-connect/token")
local token
token=$(echo "$resp" | jq -r '.access_token // empty' 2>/dev/null)
if [[ -z "$token" ]]; then
die "Failed to obtain access token — check credentials"
fi
echo "$token"
}
contabo_api() {
local method="$1" endpoint="$2"
shift 2
local attempt=0 max_attempts=3
while (( attempt < max_attempts )); do
local http_code
http_code=$(curl -s -o /tmp/cdm_resp.json -w "%{http_code}" \
-X "$method" \
-H "Authorization: Bearer $(contabo_token)" \
-H "Content-Type: application/json" \
-H "x-request-id: $(cat /proc/sys/kernel/random/uuid 2>/dev/null || date +%s%N)" \
"https://api.contabo.com/v1${endpoint}" "$@")
verbose "API ${method} ${endpoint} → HTTP ${http_code}"
if [[ "$http_code" == "429" ]]; then
((attempt++)) || true
local wait=$(( attempt * 5 ))
warn "Rate limited — retrying in ${wait}s (attempt ${attempt}/${max_attempts})"
sleep "$wait"
continue
fi
cat /tmp/cdm_resp.json
return 0
done
err "API request failed after ${max_attempts} attempts: ${method} ${endpoint}"
return 1
}
check_credentials() {
[[ -z "$CONTABO_CLIENT_ID" ]] && die "CONTABO_CLIENT_ID not set"
[[ -z "$CONTABO_CLIENT_SECRET" ]] && die "CONTABO_CLIENT_SECRET not set"
[[ -z "$CONTABO_API_USER" ]] && die "CONTABO_API_USER not set"
[[ -z "$CONTABO_API_PASS" ]] && die "CONTABO_API_PASS not set"
}
check_deps() {
command -v curl &>/dev/null || die "curl is required"
command -v jq &>/dev/null || die "jq is required"
}
# ══════════════════════════════════════════════════════════════════════
# ZONES
# ══════════════════════════════════════════════════════════════════════
do_zones() {
local page=1 size=100 all_data="[]"
while true; do
local resp
resp=$(contabo_api GET "/dns/zones?page=${page}&size=${size}")
local page_data
page_data=$(echo "$resp" | jq '.data // []' 2>/dev/null)
local page_count
page_count=$(echo "$page_data" | jq 'length' 2>/dev/null || echo 0)
[[ "$page_count" -eq 0 ]] && break
all_data=$(echo -e "${all_data}\n${page_data}" | jq -s 'add' 2>/dev/null)
(( page_count < size )) && break
((page++)) || true
done
local total
total=$(echo "$all_data" | jq 'length' 2>/dev/null || echo 0)
[[ "$total" -eq 0 ]] && die "No zones found"
case "$OUTPUT_FORMAT" in
json)
echo "$all_data" | jq '.'
;;
prometheus)
cat <<EOF
# HELP contabo_dns_zones_total Total DNS zones
# TYPE contabo_dns_zones_total gauge
contabo_dns_zones_total ${total}
EOF
;;
*)
section_header "DNS Zones"
printf " ${BOLD}%-25s %-10s %-36s %-8s${RESET}\n" \
"ZONE_NAME" "STATUS" "DNS_ZONE_ID" "RECORDS"
printf " %s\n" "$(printf '%.0s─' {1..81})"
echo "$all_data" | jq -r \
'.[] | "\(.dnsZoneName // .name // "unknown")\t\(.status // "—")\t\(.dnsZoneId // .id // "—")\t\(.records // 0)"' \
2>/dev/null \
| while IFS=$'\t' read -r name status zid rcount; do
printf " %-25s %-10s %-36s %-8s\n" \
"${name:0:23}" "$status" "${zid:0:34}" "$rcount"
done
echo ""
field "Zones:" "$total"
;;
esac
}
# ══════════════════════════════════════════════════════════════════════
# RECORDS
# ══════════════════════════════════════════════════════════════════════
do_records() {
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
local resp
resp=$(contabo_api GET "/dns/zones/${ZONE_NAME}/records")
local records
records=$(echo "$resp" | jq '.data // []' 2>/dev/null)
local total
total=$(echo "$records" | jq 'length' 2>/dev/null || echo 0)
case "$OUTPUT_FORMAT" in
json)
echo "$records" | jq '.'
;;
prometheus)
cat <<EOF
# HELP contabo_dns_records_total Total DNS records
# TYPE contabo_dns_records_total gauge
contabo_dns_records_total ${total}
# HELP contabo_dns_zone_records DNS records per zone
# TYPE contabo_dns_zone_records gauge
contabo_dns_zone_records{zone="${ZONE_NAME}"} ${total}
EOF
;;
*)
section_header "DNS Records — ${ZONE_NAME}"
printf " ${BOLD}%-36s %-6s %-10s %-26s %-6s %-5s${RESET}\n" \
"RECORD_ID" "TYPE" "NAME" "CONTENT" "TTL" "PRIO"
printf " %s\n" "$(printf '%.0s─' {1..91})"
echo "$records" | jq -r \
'.[] | "\(.recordId // .id // "—")\t\(.type // "—")\t\(.name // "@")\t\(.content // "—")\t\(.ttl // 0)\t\(.prio // "—")"' \
2>/dev/null \
| while IFS=$'\t' read -r rid rtype rname rcontent rttl rprio; do
printf " %-36s %-6s %-10s %-26s %-6s %-5s\n" \
"${rid:0:34}" "$rtype" "${rname:0:8}" "${rcontent:0:24}" "$rttl" "$rprio"
done
echo ""
field "Records:" "$total"
;;
esac
}
# ══════════════════════════════════════════════════════════════════════
# ADD
# ══════════════════════════════════════════════════════════════════════
do_add() {
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
[[ -z "$RECORD_TYPE" ]] && die "Specify --type TYPE"
[[ -z "$RECORD_NAME" ]] && die "Specify --name NAME"
[[ -z "$RECORD_CONTENT" ]] && die "Specify --content CONTENT"
local payload
payload=$(jq -n \
--arg type "$RECORD_TYPE" \
--arg name "$RECORD_NAME" \
--arg content "$RECORD_CONTENT" \
--argjson ttl "$RECORD_TTL" \
'{type: $type, name: $name, content: $content, ttl: $ttl}')
if [[ -n "$RECORD_PRIO" ]]; then
payload=$(echo "$payload" | jq --argjson prio "$RECORD_PRIO" '. + {prio: $prio}')
fi
local resp
resp=$(contabo_api POST "/dns/zones/${ZONE_NAME}/records" -d "$payload")
local rid
rid=$(echo "$resp" | jq -r '.data[0].recordId // .data[0].id // empty' 2>/dev/null)
if [[ -n "$rid" ]]; then
echo -e " ${GREEN}${RESET} Record created: ${RECORD_TYPE} ${RECORD_NAME}${RECORD_CONTENT} (ID: ${rid})"
((ACTION_OK++)) || true
else
local errmsg
errmsg=$(echo "$resp" | jq -r '.message // "unknown error"' 2>/dev/null)
echo -e " ${RED}${RESET} Failed to create record: ${errmsg}"
((ACTION_FAIL++)) || true
fi
}
# ══════════════════════════════════════════════════════════════════════
# UPDATE
# ══════════════════════════════════════════════════════════════════════
do_update() {
[[ -z "$RECORD_ID" ]] && die "Specify --record-id ID"
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
[[ -z "$RECORD_TYPE" ]] && die "Specify --type TYPE"
[[ -z "$RECORD_NAME" ]] && die "Specify --name NAME"
[[ -z "$RECORD_CONTENT" ]] && die "Specify --content CONTENT"
local payload
payload=$(jq -n \
--arg type "$RECORD_TYPE" \
--arg name "$RECORD_NAME" \
--arg content "$RECORD_CONTENT" \
--argjson ttl "$RECORD_TTL" \
'{type: $type, name: $name, content: $content, ttl: $ttl}')
if [[ -n "$RECORD_PRIO" ]]; then
payload=$(echo "$payload" | jq --argjson prio "$RECORD_PRIO" '. + {prio: $prio}')
fi
local resp
resp=$(contabo_api PUT "/dns/zones/${ZONE_NAME}/records/${RECORD_ID}" -d "$payload")
local rid
rid=$(echo "$resp" | jq -r '.data[0].recordId // .data[0].id // empty' 2>/dev/null)
if [[ -n "$rid" ]]; then
echo -e " ${GREEN}${RESET} Record updated: ${RECORD_TYPE} ${RECORD_NAME}${RECORD_CONTENT} (ID: ${rid})"
((ACTION_OK++)) || true
else
local errmsg
errmsg=$(echo "$resp" | jq -r '.message // "unknown error"' 2>/dev/null)
echo -e " ${RED}${RESET} Failed to update record: ${errmsg}"
((ACTION_FAIL++)) || true
fi
}
# ══════════════════════════════════════════════════════════════════════
# DELETE
# ══════════════════════════════════════════════════════════════════════
do_delete() {
[[ -z "$RECORD_ID" ]] && die "Specify --record-id ID"
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
[[ "$FORCE" != "true" ]] && die "Delete is destructive — use --force to confirm"
local resp
resp=$(contabo_api DELETE "/dns/zones/${ZONE_NAME}/records/${RECORD_ID}")
echo -e " ${GREEN}${RESET} Record deleted: ${RECORD_ID}"
((ACTION_OK++)) || true
}
# ══════════════════════════════════════════════════════════════════════
# BULK ADD
# ══════════════════════════════════════════════════════════════════════
do_bulk_add() {
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
[[ -z "$CSV_FILE" ]] && die "Specify --csv FILE"
[[ ! -f "$CSV_FILE" ]] && die "CSV file not found: ${CSV_FILE}"
section_header "Bulk Add — ${ZONE_NAME}"
local line_num=0
while IFS=',' read -r rtype rname rcontent rttl rprio; do
((line_num++)) || true
[[ -z "$rtype" || "$rtype" =~ ^# ]] && continue
rtype=$(echo "$rtype" | xargs)
rname=$(echo "$rname" | xargs)
rcontent=$(echo "$rcontent" | xargs)
rttl=$(echo "${rttl:-3600}" | xargs)
rprio=$(echo "${rprio:-}" | xargs)
local payload
payload=$(jq -n \
--arg type "$rtype" \
--arg name "$rname" \
--arg content "$rcontent" \
--argjson ttl "$rttl" \
'{type: $type, name: $name, content: $content, ttl: $ttl}')
if [[ -n "$rprio" ]]; then
payload=$(echo "$payload" | jq --argjson prio "$rprio" '. + {prio: $prio}')
fi
local resp
resp=$(contabo_api POST "/dns/zones/${ZONE_NAME}/records" -d "$payload")
local rid
rid=$(echo "$resp" | jq -r '.data[0].recordId // .data[0].id // empty' 2>/dev/null)
if [[ -n "$rid" ]]; then
echo -e " ${GREEN}${RESET} ${rtype} ${rname}${rcontent} (line ${line_num})"
((ACTION_OK++)) || true
else
echo -e " ${RED}${RESET} ${rtype} ${rname}${rcontent} (line ${line_num})"
((ACTION_FAIL++)) || true
fi
sleep 0.5
done < "$CSV_FILE"
echo ""
field_color "Succeeded:" "${GREEN}${ACTION_OK}${RESET}"
if [[ "$ACTION_FAIL" -gt 0 ]]; then
field_color "Failed:" "${RED}${ACTION_FAIL}${RESET}"
fi
}
# ══════════════════════════════════════════════════════════════════════
# AUDIT
# ══════════════════════════════════════════════════════════════════════
do_audit() {
[[ -z "$ZONE_NAME" ]] && die "Specify --zone DOMAIN"
local resp
resp=$(contabo_api GET "/dns/zones/${ZONE_NAME}/records")
local records
records=$(echo "$resp" | jq '.data // []' 2>/dev/null)
local total
total=$(echo "$records" | jq 'length' 2>/dev/null || echo 0)
local warnings=0
if [[ "$OUTPUT_FORMAT" != "prometheus" ]]; then
section_header "DNS Audit — ${ZONE_NAME}"
field "Records:" "$total"
echo ""
fi
# Check SOA
local soa_count
soa_count=$(echo "$records" | jq '[.[] | select(.type == "SOA")] | length' 2>/dev/null || echo 0)
if [[ "$soa_count" -eq 0 ]]; then
((warnings++)) || true
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${YELLOW}${RESET} No SOA record found"
else
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${GREEN}${RESET} SOA record present"
fi
# Check NS
local ns_count
ns_count=$(echo "$records" | jq '[.[] | select(.type == "NS")] | length' 2>/dev/null || echo 0)
if [[ "$ns_count" -eq 0 ]]; then
((warnings++)) || true
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${RED}${RESET} No NS records found"
elif [[ "$ns_count" -lt 2 ]]; then
((warnings++)) || true
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${YELLOW}${RESET} Only ${ns_count} NS record(s) — recommend at least 2"
else
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${GREEN}${RESET} ${ns_count} NS records"
fi
# Check common types
for rtype in A AAAA MX TXT; do
local rcount
rcount=$(echo "$records" | jq --arg t "$rtype" '[.[] | select(.type == $t)] | length' 2>/dev/null || echo 0)
if [[ "$rcount" -eq 0 ]]; then
((warnings++)) || true
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${YELLOW}${RESET} No ${rtype} records found"
else
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${GREEN}${RESET} ${rcount} ${rtype} record(s)"
fi
done
# Check low TTLs
local low_ttl
low_ttl=$(echo "$records" | jq '[.[] | select(.ttl < 300 and .ttl > 0)] | length' 2>/dev/null || echo 0)
if [[ "$low_ttl" -gt 0 ]]; then
((warnings++)) || true
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${YELLOW}${RESET} ${low_ttl} record(s) with TTL < 300s"
else
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${GREEN}${RESET} All TTLs ≥ 300s"
fi
# Check wildcards
local wildcard
wildcard=$(echo "$records" | jq '[.[] | select(.name | startswith("*"))] | length' 2>/dev/null || echo 0)
if [[ "$wildcard" -gt 0 ]]; then
[[ "$OUTPUT_FORMAT" != "prometheus" ]] && echo -e " ${CYAN}${RESET} ${wildcard} wildcard record(s)"
fi
if [[ "$OUTPUT_FORMAT" == "prometheus" ]]; then
cat <<EOF
# HELP contabo_dns_audit_warnings DNS audit warnings
# TYPE contabo_dns_audit_warnings gauge
contabo_dns_audit_warnings{zone="${ZONE_NAME}"} ${warnings}
EOF
return
fi
echo ""
if [[ "$warnings" -eq 0 ]]; then
field_color "Warnings:" "${GREEN}0${RESET}"
else
field_color "Warnings:" "${YELLOW}${warnings}${RESET}"
fi
}
# ══════════════════════════════════════════════════════════════════════
# HELP
# ══════════════════════════════════════════════════════════════════════
show_help() {
cat <<EOF
${BOLD}${SCRIPT_NAME}${RESET} — Contabo DNS Manager
Manage DNS zones and records via the Contabo DNS API.
${BOLD}MODES${RESET}
--zones List all DNS zones
--records List records for a zone (requires --zone)
--add Add a DNS record
--update Update a DNS record (requires --record-id)
--delete Delete a DNS record (requires --record-id, --force)
--bulk-add Bulk add records from CSV (requires --csv)
--audit Audit zone for common issues
${BOLD}TARGETING${RESET}
--zone DOMAIN Target zone by domain name (used as URL path parameter)
--record-id ID Target a specific record by ID
${BOLD}RECORD FIELDS${RESET}
--type TYPE Record type (A, AAAA, CNAME, MX, TXT, SRV, etc.)
--name NAME Record name (subdomain or @ for apex)
--content VALUE Record content (IP address, hostname, etc.)
--ttl TTL TTL in seconds (default: 3600)
--prio PRIO Priority (for MX records)
${BOLD}OPTIONS${RESET}
--format FMT Output: table, json, prometheus (default: table)
--csv FILE CSV file for bulk add (type,name,content,ttl,prio)
--force Required for delete operations
--verbose Debug output
--no-color Disable colored output
--help Show this help message
${BOLD}ENVIRONMENT VARIABLES${RESET}
CONTABO_CLIENT_ID OAuth2 Client ID (required)
CONTABO_CLIENT_SECRET OAuth2 Client Secret (required)
CONTABO_API_USER API username / email (required)
CONTABO_API_PASS API password (required)
CDM_FORMAT Default output format
VERBOSE Enable verbose output (true/false)
COLOR Color mode: auto, always, never
${BOLD}EXAMPLES${RESET}
# List all zones
${SCRIPT_NAME} --zones
# List records for a zone
${SCRIPT_NAME} --records --zone example.com
# Add an A record
${SCRIPT_NAME} --add --zone example.com --type A --name www --content 203.0.113.10
# Add MX record with priority
${SCRIPT_NAME} --add --zone example.com --type MX --name @ --content mail.example.com --prio 10
# Update a record
${SCRIPT_NAME} --update --record-id a1b2c3d4 --zone example.com --type A --name www --content 203.0.113.11
# Delete a record
${SCRIPT_NAME} --delete --record-id a1b2c3d4 --zone example.com --force
# Bulk add from CSV
${SCRIPT_NAME} --bulk-add --zone example.com --csv records.csv
# Audit zone
${SCRIPT_NAME} --audit --zone example.com
# Prometheus metrics
${SCRIPT_NAME} --zones --format prometheus
${BOLD}NOTES${RESET}
Contabo DNS does NOT support BIND zone file export/import.
Use --content (not --value) for record data to match Contabo API.
${BOLD}EXIT CODES${RESET}
0 Success
1 Runtime error
EOF
}
# ══════════════════════════════════════════════════════════════════════
# PARSE ARGS
# ══════════════════════════════════════════════════════════════════════
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--zones) RUN_MODE="zones"; shift ;;
--records) RUN_MODE="records"; shift ;;
--add) RUN_MODE="add"; shift ;;
--update) RUN_MODE="update"; shift ;;
--delete) RUN_MODE="delete"; shift ;;
--bulk-add) RUN_MODE="bulk-add"; shift ;;
--audit) RUN_MODE="audit"; shift ;;
--zone) ZONE_NAME="${2:?--zone requires a DOMAIN}"; shift 2 ;;
--record-id) RECORD_ID="${2:?--record-id requires an ID}"; shift 2 ;;
--type) RECORD_TYPE="${2:?--type requires a TYPE}"; shift 2 ;;
--name) RECORD_NAME="${2:?--name requires a NAME}"; shift 2 ;;
--content) RECORD_CONTENT="${2:?--content requires a VALUE}"; shift 2 ;;
--ttl) RECORD_TTL="${2:?--ttl requires a TTL}"; shift 2 ;;
--prio) RECORD_PRIO="${2:?--prio requires a PRIO}"; shift 2 ;;
--csv) CSV_FILE="${2:?--csv requires a FILE}"; shift 2 ;;
--format) OUTPUT_FORMAT="${2:?--format requires a value}"; shift 2 ;;
--force) FORCE="true"; shift ;;
--verbose) VERBOSE="true"; shift ;;
--no-color) COLOR="never"; shift ;;
--help|-h) setup_colors; show_help; exit 0 ;;
*) die "Unknown option: $1 (see --help)" ;;
esac
done
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
main() {
parse_args "$@"
setup_colors
if [[ -z "$RUN_MODE" ]]; then
err "No mode specified"
echo ""
show_help
exit 1
fi
check_deps
check_credentials
START_TIME=$(date +%s)
case "$RUN_MODE" in
zones) do_zones ;;
records) do_records ;;
add) do_add ;;
update) do_update ;;
delete) do_delete ;;
bulk-add) do_bulk_add ;;
audit) do_audit ;;
*) die "Unknown mode: ${RUN_MODE}" ;;
esac
if [[ "$OUTPUT_FORMAT" != "prometheus" ]]; then
echo ""
field "Duration:" "$(elapsed)"
fi
}
main "$@"