Cloudflare DNS Updater
A Bash script that automatically updates Cloudflare DNS A records with your current public IP address. This script demonstrates various advanced Bash programming features while providing a practical tool for dynamic DNS management.
Features
- Automatic IP Detection: Retrieves current public IP using external services
- Multi-Domain Support: Manages DNS records for multiple domains simultaneously
- Smart Updates: Only updates records when IP changes to avoid unnecessary API calls
- Orphaned Record Cleanup: Automatically removes unmanaged DNS records
- Protected Records: Prevents deletion of critical DNS records
- Dry Run Mode: Test changes without actually modifying DNS records
- Comprehensive Logging: Detailed output showing all operations performed
Bash Features Demonstrated
This script showcases the following Bash programming features:
1. Shebang and Script Structure
#!/bin/bash
Proper script initialization with bash interpreter specification.
2. Variable Declarations and Constants
TOKEN=""
AUTO_DELETE_ORPHANS=true
DRY_RUN=false
Use of string constants and boolean flags for configuration.
3. Associative Arrays
declare -A d0 d1 # Declare associative arrays for domain configuration
d0=(
["domain"]=""
["zone_id"]=""
["subdomains"]="@,"
["protected_records"]=""
)
Key-value data structures for complex domain configurations.
4. Indexed Arrays
declare -a domains # Declare indexed array to hold domain references
domains=( d0 d1 )
Ordered collections for managing multiple domains.
5. Array References (Namerefs)
declare -n domain="$item" # Create reference to associative array
echo "${domain[domain]}" # Access array elements through reference
Dynamic array referencing for flexible data access.
6. Functions
get_current_ip() {
curl -s https://ifconfig.me/ip
}
create_dns_record() {
local zone_id="$1"
local data="$2"
# Function implementation
}
Modular code organization with local variables and parameters.
7. Advanced Conditional Statements
if [[ "$DRY_RUN" == true ]]; then
echo "⚠️ DRY RUN MODE ENABLED"
fi
if [[ -n "${existing_a_records[$full_name]}" ]]; then
# Record exists logic
fi
String comparison, variable existence checks, and complex conditions.
8. Loop Constructs
for item in "${domains[@]}"; do
# Process each domain
done
for subdomain in "${managed_subdomains[@]}"; do
# Process each subdomain
done
Iteration over arrays with proper quoting.
9. String Manipulation and Parsing
IFS=',' read -ra protected_records <<< "${domain[protected_records]}"
IFS=',' read -ra managed_subdomains <<< "${domain[subdomains]}"
Field splitting and array population from comma-separated strings.
10. Command and Process Substitution
current_ip=$(get_current_ip) # Command substitution
done < <(echo "$response" | jq -c '.result[] | select(.type == "A")') # Process substitution
Capturing command output and feeding output to loops.
11. JSON Processing with jq
if echo "$response" | jq -e '.success == false' > /dev/null 2>&1; then
echo "Error: Failed to get DNS records"
fi
create_data=$(jq -n \
--arg name "$full_name" \
--arg ip "$current_ip" \
'{
"type": "A",
"name": $name,
"content": $ip,
"ttl": 1,
"proxied": true
}')
Parsing JSON responses and constructing JSON payloads.
12. Error Handling and Exit Codes
if [ -z "$current_ip" ]; then
echo "Error: Could not retrieve current IP"
exit 1
fi
Input validation and controlled script termination.
13. Arithmetic Operations
((records_created++))
((records_updated++))
((records_skipped++))
((records_deleted++))
Counter variables for operation statistics.
14. API Integration with curl
curl -s "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records" \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d "$data"
REST API calls with proper headers and data payloads.
15. Dynamic Variable Management
unset existing_a_records # Clean up associative arrays
unset domain # Clean up nameref
Memory management and variable cleanup.
Usage
- Configure Domains: Edit the domain arrays at the top of the script
- Set API Token: Update the
TOKENvariable with your Cloudflare API token - Dry Run First: Test with
DRY_RUN=trueto see what would change - Run Updates: Set
DRY_RUN=falseto apply actual changes
# Dry run (safe testing)
./update_domain.sh
# Apply changes
# Edit script: DRY_RUN=false
./update_domain.sh
Configuration
Domain Configuration Structure
Each domain is configured as an associative array with:
domain: The root domain namezone_id: Cloudflare zone identifiersubdomains: Comma-separated list of subdomains to manageprotected_records: Records that should never be deleted
Script Parameters
TOKEN: Cloudflare API bearer tokenAUTO_DELETE_ORPHANS: Whether to remove unmanaged recordsDRY_RUN: Test mode without making actual changes
Requirements
- Bash 4.0+
- curl
- jq
- Valid Cloudflare API token with DNS edit permissions
Security Notes
- Store API tokens securely (consider environment variables)
- Use dry run mode when testing
- Review protected records configuration carefully
- Monitor script output for any API errors
Output Example
⚠️ DRY RUN MODE ENABLED - No actual changes will be made
=========================================
Current Public IP: 203.0.113.1
=========================================
Processing domain: my-dev.pro
Zone ID: 5c9a19af6aeababb78b294844290a7d2
Managed subdomains: @ git
----------------------------------------
Existing A records:
- my-dev.pro → 203.0.113.1 (ID: abc123)
- git.my-dev.pro → 203.0.113.1 (ID: def456)
----------------------------------------
Checking: my-dev.pro
✓ SKIPPED: IP unchanged (203.0.113.1 = 203.0.113.1)
Checking: git.my-dev.pro
✓ SKIPPED: IP unchanged (203.0.113.1 = 203.0.113.1)
=========================================
SUMMARY for my-dev.pro:
✓ Created: 0
✓ Updated: 0
✓ Skipped (IP unchanged): 2
✓ Deleted: 0
=========================================
⚠️ DRY RUN COMPLETED - No actual changes were made
Set DRY_RUN=false to apply changes