#!/usr/bin/env bash # dns_gcloud # Add/Del/List TXT record using the Google Cloud DNS gcloud command # ver 2025-09-23 # shellcheck has read, even I'll say eveything was so nice # org. version: # https://github.com/kshji/gitssl_gcloud # # Main reason to make was to support getssl using DNS validation with Google Cloud DNS # You can use this script to any host setting TXT records, default is _acme-challenge # # dns_gloud -c command domain token # # get help: # dns_gloud -? | --help # # dns_gloud -c add example.com "testN" # dns_gloud -c list example.com # dns_gloud -c del example.com "testN" # dns_gloud -c add example.com "test1" "test2" # dns_gloud -c list example.com # dns_gloud -c del example.com "test1" "test2" # # options: # -d 0|1 # debug on/off to the file /var/tmp/getssl/...log # -s 10 # sleeptime after gcloud add/del process, default 10 # -h hostname # default is "_acme-challenge." # # if like to use domain without host, set host empty string !!! # -t ttlvalue # set ttl, default 60 = 1 min # # PRG="$0" BINDIR="${PRG%/*}" [ "$PRG" = "$BINDIR" ] && BINDIR="." # - same dir as program PRG="${PRG##*/}" ####################################################################################### usage() { cat </dev/null chmod 1777 "$tmpd" 2>/dev/null cnt=0 # save only last execute info { date echo "GCLOUD_PROJECTID:$GCLOUD_PROJECTID" echo "GCLOUD_ZONE:$GCLOUD_ZONE" echo "GCLOUD_ACCOUNT:$GCLOUD_ACCOUNT" echo "GCLOUD_KEYFILE:$GCLOUD_KEYFILE" env } > "$tmpf" for var in $all do ((cnt++)) echo "$cnt:<$var>" >> "$tmpf" done } ####################################################################################### check_end_dot() { # gcloud need fulldomain ending dot = absolut domain path # set the last dot if missing Xorg="$1" Xnodot="${Xorg%.}" # remove last dot if there is echo "$Xnodot." # add dot allways } ####################################################################################### list_txt() { Xname="$1" list=$(gcloud dns record-sets list --zone="$GCLOUD_ZONE" --name="$Xname" --type="TXT" ) stat=$? (( stat > 0 )) && err "gcloud error to list TXT record" && exit 2 variables=variables # Some shell checkers (ex.shellcheck) like to do next different way. I'll say both works # you can read 1st line to var and then use {$var?} on reading # in this case you'll get same result. This read 1st loop varianle variables, look value of variables # on the second loop it read variables, which was on the 1st line ... command line process is so nice # this do exactly what we need to do ... (not that what https://www.shellcheck.net/wiki/SC2229 explain) oifs="$IFS" cnt=0 echo "$list" | while read $variables do (( cnt++ )) # 1st line is header, read next line (( cnt == 1 )) && continue echo "name:$NAME type:$TYPE ttl:$TTL data:$DATA" # next line works just what we need, shellcheck not like this ... # Wiki's last part: it's okay https://www.shellcheck.net/wiki/SC2206 IFS="," values=($DATA) IFS="$oifs" numOfvalues=${#values[@]} for (( var=0; var" #exit # start transaction gcloud dns record-sets transaction start --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" stat=$? (( stat > 0 )) && err "gcloud start transaction error" && exit 2 # del TXT dbgstr gcloud dns record-sets transaction remove --name="$Xname" --ttl="$ttl" --type="TXT" \ --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" "$Xtoken" gcloud dns record-sets transaction remove --name="$Xname" --ttl="$ttl" --type="TXT" \ --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" "$Xtoken" stat=$? if (( stat > 0 )) ; then err "gcloud remove error" gcloud dns record-sets transaction abort --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" exit 2 fi gcloud dns record-sets transaction execute --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" stat=$? (( stat > 0 )) && err "gcloud transaction execute error" && exit 2 # if not sleep, get error ??? sleep "$sleepafter" exit 0 } ####################################################################################### add_txt() { Xname="$1" shift # could be 1-n values Xtoken="" while [ $# -gt 0 ] do Xtoken="$Xtoken \"$1\"" shift done dbgstr "<$Xtoken>" # start transaction gcloud dns record-sets transaction start --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" stat=$? if (( stat > 0 )) ; then err "gcloud start transaction error" gcloud dns record-sets transaction abort --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" exit 2 fi # add TXT dbgstr gcloud dns record-sets transaction add --name="$Xname" --ttl="$ttl" --type="TXT" \ --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" "$Xtoken" gcloud dns record-sets transaction add --name="$Xname" --ttl="$ttl" --type="TXT" \ --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" "$Xtoken" stat=$? if (( stat > 0 )) ; then err "gcloud add error" gcloud dns record-sets transaction abort --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" exit 2 fi gcloud dns record-sets transaction execute --zone="$GCLOUD_ZONE" --project="$GCLOUD_PROJECTID" stat=$? #echo "execute stat:$stat" (( stat > 0 )) && err "gcloud transaction execute error" && exit 2 # if not sleep, get error ??? sleep "$sleepafter" exit 0 } ####################################################################################### # MAIN ####################################################################################### DEBUG=0 command="" sleepafter=10 # default host to manipulate host="_acme-challenge." ttl=60 while [ $# -gt 0 ] do arg="$1" case "$arg" in -c|--command|--cmd) command="$2"; shift ;; -d|--debug) DEBUG="$2" ; shift ;; -s|--sleep) sleepafter="$2"; shift ;; -h|--host) host="$2" [ $# -lt 2 ] && usage && exit 2 host="$2" shift ;; -t|--ttl) ttl="$2"; shift ;; -?|--help) usage; exit 2 ;; -*) # unknown option err "unknown option $arg" usage exit 2 ;; *) # arguments, stop the option parser break ;; esac shift done [ "$GCLOUD_PROJECTID" = "" ] && err "GCLOUD_PROJECTID is not set. Unable to set TXT records." && exit 2 [ "$GCLOUD_ZONE" = "" ] && err "GCLOUD_ZONE is not set. Unable to set TXT records." && exit 2 [ "$GCLOUD_ACCOUNT" = "" ] && err "GCLOUD_ACCOUNT is not set. Unable to set TXT records." && exit 2 [ "$GCLOUD_KEYFILE" = "" ] && err "GCLOUD_KEYFILE is not set. Unable to set TXT records." && exit 2 [ ! -f "$GCLOUD_KEYFILE" ] && err "file not usable:$GCLOUD_KEYFILE" && exit 2 all="$*" fulldomain="$1" shift token="$*" # could be 1-n tokens if del case "$command" in add) ;; del) ;; list) ;; *) command="" ;; esac [ "$command" = "" ] && err "need option -c add | -c del | -c list" && exit 2 [ "$fulldomain" = "" ] && err "need fulldomain argument." && exit 2 [ "$token" = "" ] && [ "$command" != "list" ] && err "need token argument." && exit 2 # dbg info to the program tmp dir (( DEBUG>0)) && dbg "$fulldomain" "$command" # host check ending of dot [ "$host" != "" ] && host=$(check_end_dot "$host") # host fullname gname=$(check_end_dot "$host$fulldomain") #echo "gname: $gname" # activate google cloud account gcloud auth activate-service-account "$GCLOUD_ACCOUNT" --key-file="$GCLOUD_KEYFILE" --project="$GCLOUD_PROJECTID" stat=$? (( stat > 0 )) && err "gcloud activate account error" && exit 2 case "$command" in add) add_txt "$gname" "$token" ;; del) del_txt "$gname" "$token" ;; list) list_txt "$gname" ;; *) err "unknown command" exit 2 ;; esac