Created
October 21, 2025 09:23
-
-
Save nh4ttruong/c4d1d1aaa81ec1f1423c31241f914705 to your computer and use it in GitHub Desktop.
Quick Update DNS On CloudFlare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # A script to manage 'A' or 'CNAME' DNS records in Cloudflare via API. | |
| # | |
| # Usage: | |
| # ./cloudflare_dns.sh (Interactive add mode) | |
| # ./cloudflare_dns.sh -l [-t TYPE] (List records, optionally filter by TYPE: A or CNAME) | |
| # ./cloudflare_dns.sh -u (Interactive update mode) | |
| # ./cloudflare_dns.sh -r (Interactive remove mode) | |
| # --- User-configurable variables --- | |
| # You can pre-fill these or leave them empty to be prompted. | |
| CLOUDFLARE_API_TOKEN="xxx" | |
| ZONE_ID="xxx" | |
| # --- Script Configuration --- | |
| ACTION="add" | |
| RECORD_TYPE_FILTER="" | |
| # --- Color Codes for Output --- | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| # --- Functions --- | |
| usage() { | |
| echo "Usage: $0 [-l] [-u] [-r] [-t type]" | |
| echo " -l List DNS records." | |
| echo " -u Update an existing DNS record." | |
| echo " -r Remove an existing DNS record." | |
| echo " -t TYPE Filter list by record type ('A' or 'CNAME'). Used with -l." | |
| echo " (no args) Add a new DNS record (default)." | |
| exit 1 | |
| } | |
| # Function to prompt for user input if a variable is empty | |
| prompt_if_empty() { | |
| local var_name="$1" | |
| local prompt_text="$2" | |
| local is_secret="${3:-false}" | |
| if [ -z "${!var_name}" ]; then | |
| if [ "$is_secret" = true ]; then | |
| read -sp "$prompt_text: " "$var_name" | |
| echo | |
| else | |
| read -p "$prompt_text: " "$var_name" | |
| fi | |
| fi | |
| if [ -z "${!var_name}" ]; then | |
| echo -e "${RED}Error: $prompt_text cannot be empty.${NC}" | |
| exit 1 | |
| fi | |
| } | |
| # Function to list DNS records | |
| list_records() { | |
| local filter_url="" | |
| if [ -n "$RECORD_TYPE_FILTER" ]; then | |
| filter_url="?type=$RECORD_TYPE_FILTER" | |
| fi | |
| echo "Fetching DNS records..." | |
| API_URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records$filter_url" | |
| response=$(curl -s -X GET "$API_URL" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json") | |
| if ! command -v jq &> /dev/null; then | |
| echo -e "${YELLOW}Warning: 'jq' is not installed. Output will be raw JSON.${NC}" | |
| echo "$response" | |
| return | |
| fi | |
| if ! echo "$response" | jq -e '.success' &> /dev/null; then | |
| echo -e "${RED}Error fetching records.${NC}" | |
| echo "$response" | jq . | |
| exit 1 | |
| fi | |
| echo -e "${CYAN}ID TYPE NAME CONTENT PROXIED${NC}" | |
| echo "------------------------------------ ------- ----------------------------- ----------------------------- -------" | |
| echo "$response" | jq -r '.result[] | [.id, .type, .name, .content, .proxied] | @tsv' | while IFS=$'\t' read -r id type name content proxied; do | |
| printf "%-36s %-7s %-29s %-29s %-7s\n" "$id" "$type" "$name" "$content" "$proxied" | |
| done | |
| } | |
| # Function to add a DNS record | |
| add_record() { | |
| echo "--- Add New DNS Record ---" | |
| # 3. Get Record Type | |
| while true; do | |
| read -p "Enter DNS record type (A or CNAME): " record_type | |
| record_type=$(echo "$record_type" | tr '[:lower:]' '[:upper:]') | |
| if [ "$record_type" == "A" ] || [ "$record_type" == "CNAME" ]; then | |
| break | |
| else | |
| echo -e "${RED}Invalid record type. Please enter 'A' or 'CNAME'.${NC}" | |
| fi | |
| done | |
| # 4. Get Record Name | |
| read -p "Enter DNS record name (e.g., 'sub.domain.com'): " record_name | |
| if [ -z "$record_name" ]; then echo -e "${RED}Error: Record name cannot be empty.${NC}"; exit 1; fi | |
| # 5. Get Record Content | |
| if [ "$record_type" == "A" ]; then | |
| read -p "Enter the IP address (IPv4) for the 'A' record: " record_content | |
| if ! [[ $record_content =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo -e "${RED}Error: Invalid IPv4 address format.${NC}"; exit 1; | |
| fi | |
| else # CNAME | |
| read -p "Enter the target domain for the 'CNAME' record: " record_content | |
| fi | |
| if [ -z "$record_content" ]; then echo -e "${RED}Error: Record content cannot be empty.${NC}"; exit 1; fi | |
| # 6. Get Proxy Status | |
| while true; do | |
| read -p "Enable Cloudflare proxy (proxied)? (true/false): " proxied_status | |
| proxied_status=$(echo "$proxied_status" | tr '[:upper:]' '[:lower:]') | |
| if [ "$proxied_status" == "true" ] || [ "$proxied_status" == "false" ]; then | |
| break | |
| else echo -e "${RED}Invalid input. Please enter 'true' or 'false'.${NC}"; fi | |
| done | |
| json_payload=$(cat <<EOF | |
| {"type":"$record_type","name":"$record_name","content":"$record_content","ttl":1,"proxied":$proxied_status} | |
| EOF | |
| ) | |
| API_URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" | |
| echo "Sending request to Cloudflare API..." | |
| response=$(curl -s -X POST "$API_URL" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" --data "$json_payload") | |
| if command -v jq &> /dev/null && echo "$response" | jq -e '.success' &> /dev/null; then | |
| echo -e "${GREEN}Successfully created DNS record!${NC}" | |
| echo "$response" | jq . | |
| else | |
| echo -e "${RED}Failed to create DNS record.${NC}" | |
| if command -v jq &> /dev/null; then echo "$response" | jq .; else echo "$response"; fi | |
| exit 1 | |
| fi | |
| } | |
| # Function to remove a DNS record | |
| remove_record() { | |
| echo "--- Remove DNS Record ---" | |
| list_records | |
| echo "--------------------------" | |
| read -p "Enter the ID of the record to REMOVE: " record_id | |
| if [ -z "$record_id" ]; then echo -e "${RED}Error: Record ID cannot be empty.${NC}"; exit 1; fi | |
| read -p "Are you sure you want to delete record $record_id? (y/n): " confirm | |
| if [ "$confirm" != "y" ]; then echo "Operation cancelled."; exit 0; fi | |
| API_URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$record_id" | |
| echo "Sending delete request..." | |
| response=$(curl -s -X DELETE "$API_URL" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json") | |
| if command -v jq &> /dev/null && echo "$response" | jq -e '.success' &> /dev/null; then | |
| echo -e "${GREEN}Successfully removed DNS record!${NC}" | |
| echo "$response" | jq . | |
| else | |
| echo -e "${RED}Failed to remove DNS record.${NC}" | |
| if command -v jq &> /dev/null; then echo "$response" | jq .; else echo "$response"; fi | |
| exit 1 | |
| fi | |
| } | |
| # Function to update a DNS record | |
| update_record() { | |
| echo "--- Update DNS Record ---" | |
| list_records | |
| echo "--------------------------" | |
| read -p "Enter the ID of the record to UPDATE: " record_id | |
| if [ -z "$record_id" ]; then echo -e "${RED}Error: Record ID cannot be empty.${NC}"; exit 1; fi | |
| # Fetch existing record to get its type | |
| API_URL_GET="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$record_id" | |
| existing_record=$(curl -s -X GET "$API_URL_GET" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json") | |
| if ! echo "$existing_record" | jq -e '.success' &> /dev/null; then | |
| echo -e "${RED}Could not fetch record with ID $record_id.${NC}"; exit 1; | |
| fi | |
| record_type=$(echo "$existing_record" | jq -r '.result.type') | |
| echo "Updating a '$record_type' record." | |
| read -p "Enter new record name [$(echo "$existing_record" | jq -r '.result.name')]: " record_name | |
| if [ -z "$record_name" ]; then record_name=$(echo "$existing_record" | jq -r '.result.name'); fi | |
| read -p "Enter new record content [$(echo "$existing_record" | jq -r '.result.content')]: " record_content | |
| if [ -z "$record_content" ]; then record_content=$(echo "$existing_record" | jq -r '.result.content'); fi | |
| read -p "Enable Cloudflare proxy? [$(echo "$existing_record" | jq -r '.result.proxied')] (true/false): " proxied_status | |
| if [ -z "$proxied_status" ]; then proxied_status=$(echo "$existing_record" | jq -r '.result.proxied'); fi | |
| json_payload=$(cat <<EOF | |
| {"type":"$record_type","name":"$record_name","content":"$record_content","ttl":1,"proxied":$proxied_status} | |
| EOF | |
| ) | |
| API_URL_PUT="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$record_id" | |
| echo "Sending update request..." | |
| response=$(curl -s -X PUT "$API_URL_PUT" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" --data "$json_payload") | |
| if command -v jq &> /dev/null && echo "$response" | jq -e '.success' &> /dev/null; then | |
| echo -e "${GREEN}Successfully updated DNS record!${NC}" | |
| echo "$response" | jq . | |
| else | |
| echo -e "${RED}Failed to update DNS record.${NC}" | |
| if command -v jq &> /dev/null; then echo "$response" | jq .; else echo "$response"; fi | |
| exit 1 | |
| fi | |
| } | |
| # --- Main Script --- | |
| # Parse command-line options | |
| while getopts "lurt:" opt; do | |
| case ${opt} in | |
| l ) ACTION="list" ;; | |
| u ) ACTION="update" ;; | |
| r ) ACTION="remove" ;; | |
| t ) RECORD_TYPE_FILTER=$(echo "$OPTARG" | tr '[:lower:]' '[:upper:]') ;; | |
| \? ) usage ;; | |
| esac | |
| done | |
| echo -e "${YELLOW}Cloudflare DNS Record Manager${NC}" | |
| echo "------------------------------------------------------------" | |
| # Get credentials | |
| prompt_if_empty "CLOUDFLARE_API_TOKEN" "Enter your Cloudflare API Token" true | |
| prompt_if_empty "ZONE_ID" "Enter your Cloudflare Zone ID" | |
| # Execute action | |
| case $ACTION in | |
| add) add_record ;; | |
| list) list_records ;; | |
| remove) remove_record ;; | |
| update) update_record ;; | |
| *) usage ;; | |
| esac | |
| exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment