Create a gist now

Instantly share code, notes, and snippets.

Cloudflare API v4 Dynamic DNS Update in Bash
#!/bin/bash
# CHANGE THESE
auth_email="user@example.com"
auth_key="c2547eb745079dac9320b638f5e225cf483cc5cfdda41" # found in cloudflare account settings
zone_name="example.com"
record_name="www.example.com"
# MAYBE CHANGE THESE
ip=$(curl -s http://ipv4.icanhazip.com)
ip_file="ip.txt"
id_file="cloudflare.ids"
log_file="cloudflare.log"
# LOGGER
log() {
if [ "$1" ]; then
echo -e "[$(date)] - $1" >> $log_file
fi
}
# SCRIPT START
log "Check Initiated"
if [ -f $ip_file ]; then
old_ip=$(cat $ip_file)
if [ $ip == $old_ip ]; then
echo "IP has not changed."
exit 0
fi
fi
if [ -f $id_file ] && [ $(wc -l $id_file | cut -d " " -f 1) == 2 ]; then
zone_identifier=$(head -1 $id_file)
record_identifier=$(tail -1 $id_file)
else
zone_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*' | head -1 )
record_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*')
echo "$zone_identifier" > $id_file
echo "$record_identifier" >> $id_file
fi
update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"name\":\"$record_name\",\"content\":\"$ip\"}")
if [[ $update == *"\"success\":false"* ]]; then
message="API UPDATE FAILED. DUMPING RESULTS:\n$update"
log "$message"
echo -e "$message"
exit 1
else
message="IP changed to: $ip"
echo "$ip" > $ip_file
log "$message"
echo "$message"
fi
@rasmusbe

Thanks for your code!
Since grep -P isn't available on mac I made an alternative version for mac (will probably work for linux as well)
https://gist.github.com/rasmusbe/fc2e270095f1a3b41348

@tomdavidson

If one is tired of the OSX exceptions (such as not pcre with grep), then one could fix their grep and other cmd tools:
https://gist.github.com/tomdavidson/ee37773bc9089476f92d

@b0bcarlson

It still appears to work but I am getting cloudflare-update-record.sh: 45: cloudflare-update-record.sh: [[: not found

@nekoyokoshima

for anyone(@123isme1) getting cloudflare-update-record.sh: 45: cloudflare-update-record.sh: [[: not found
Replace lines 45-55 with

case "$update" in
  *"\"success\":false"*)
    message="API UPDATE FAILED. DUMPING RESULTS:\n$update"
    log "$message"
    echo -e "$message"
    exit 1;;
  *)
      message="IP changed to: $ip"
    echo "$ip" > $ip_file
    log "$message"
    echo "$message";;
esac

A dirty workaround, but hey, it works!

If you want to change to root record of the domain you'll need to set record_name="www.example.com" (Duh!!) and make sure to replace dns_records?name=$record_name with "dns_records?type=A&name=$record_name" making sure the type corresponds with either A or CNAME(cname is not suggested for sec reasons)

@Gurkengewuerz

Thank's for sharing!

@gstuartj
gstuartj commented May 7, 2016

I started modifying this script for my own purposes, then ended up rewriting it. I did, however, use @benkulbertis's grep patterns for parsing the responses, so thanks for the inspiration. My script is POSIX compliant, so it can be used on embedded systems like consumer routers without BASH. I also added more error checking and some other useful actions & ease-of-use stuff. Repo: https://github.com/gstuartj/cf-ddns.sh/

@Noino
Noino commented Aug 10, 2016 edited

Felt like i had to add this starting line 25

if ! [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    message="Fetched IP does not look valid! Quitting"
    log "$message"
    echo -e "$message"
    exit 1 
fi
@thordin9

very useful, thanks for sharing.

@Wohlraj
Wohlraj commented Dec 24, 2016

How about handling IPv6 as well? I can give it a try if not already done

@Shurelia

Thanks for this!

@mitchins

This worked for me, thanks a lot!

@PsychoTea
PsychoTea commented Feb 24, 2017 edited

Script is working great for me, bar one small issue. Say I have the domain s.tech. I'm able to update both a.s.tech, www.s.tech, but simply setting s.tech as the record name in hopes of updating s.tech doesn't work. Any ideas?

Edit: nevermind, it appears to be working now. Not sure how that happened since I have edited anything though O.o

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment