Skip to content

Instantly share code, notes, and snippets.

@22phuber
Last active October 5, 2022 03:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 22phuber/20bba64b2ffca42595d3dece454c74ab to your computer and use it in GitHub Desktop.
Save 22phuber/20bba64b2ffca42595d3dece454c74ab to your computer and use it in GitHub Desktop.
Dynamic DNS update for AWS Route 35
#!/usr/bin/env bash
# Refactored by: Patrick Huber (22phuber@gmail.com)
# Description: Digs the current external Ipv4 and compares it to a Ipv4 locally stored in a file.
# If the ips differ, aws cli is used to update a route 53 dns record with the new Ipv4.
# This script only supports Ipv4.
# Original script source:
# - https://willwarren.com/2014/07/03/roll-dynamic-dns-service-using-amazon-route53/
# Dependencies:
# - awscli # install AWS cli: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html
# - dig
# - mktemp
# - grep
#
set -e
set -o pipefail
# INITIALIZE
{
echo "[$(date)] Starting \"${BASH_SOURCE[0]}\"" \
&& trap finish EXIT \
&& trap 'Error in line: ${LINENO}' ERR \
&& cd "$(dirname "${BASH_SOURCE[0]}")";
} || exit 1
# VARIABLES
# AWS profile
aws_profile='default'
# AWS cli path
aws_binary_path='/usr/local/bin'
# set new PATH for AWS binary
if [[ ! ${PATH} == *"${aws_binary_path}"* ]]; then
PATH=${PATH}:${aws_binary_path}
fi
# ipfile (caches last runs ip)
declare -r IPFILE="update-route53.ip"
# Hosted Zone ID e.g. BJBK35SKMM9OE
declare -r ZONEID="<your zone id>"
# The recordset you want to update e.g. example.com or www.example.com
declare -r RECORDSET="<you recordset>"
# The Time-To-Live of this recordset
declare -r TTL=300
# Use type A when using IPv4 address
declare -r TYPE="A"
# Change this if you want
COMMENT="Auto updating @ $(date)"
# FUNCTIONS
# desc: prints the date and "script finished" text
# usage: checkAwsCli
function finish () {
echo -e "[$(date)] Script finished\n"
}
# desc: check if aws cli is present
# usage: checkAwsCli
# returns:
# 0 => if aws cli exists
# 1 => if aws cli could not be found
function checkAwsCli () {
if [[ -x "$(command -v aws)" ]]; then # True if file exists and is executable.
return 0
else
echo "Function [${FUNCNAME[0]}]: AWS cli is missing."
return 1
fi
}
# desc: simple validation for a given ipv4 address
# checks:
# - correct ip parts
# - part values between 0 and 255 (inclusive)
# usage: validate_ipv4 "<ipv4>"
# returns:
# 0 => if validation was successful
# 1 => if validation has failed
function validate_ipv4() {
if [[ $# -lt 1 ]] ; then
echo "Function [${FUNCNAME[0]}]: Wrong argument count. Nothing validated"
return 1
fi
local __ip="${1}"
declare -a __ip_arr=${__ip//./ }
if [[ ${__ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
for ip_part in ${__ip_arr[@]}; do
if [[ ! ${ip_part} -le 255 ]]; then return 1; fi
done
return 0
fi
return 1
}
# desc: chcek if given string is present in the given file
# usage: find_string_in_file "<string>" file
# returns:
# 0 => if string has been found
# 1 => if string has NOT been found
function find_string_in_file() {
if [[ $# -lt 2 ]] ; then
echo "Function [${FUNCNAME[0]}]: Wrong argument count. Nothing done"
return 1
fi
local __string_to_find="${1}"
shift 1
local __file="${1}"
grep -Fxq "${__string_to_find}" "${__file}" && return 0
return 1
}
# desc: caches given ipv4 in given file
# usage: cache_ipv4 "<ipv4>" file
# 0 => if caching was successful
# 1 => if caching failed
function cache_ipv4 () {
if [[ $# -lt 2 ]] ; then
echo "Function [${FUNCNAME[0]}]: Wrong argument count. Nothing cached"
return 1
fi
local __ip="${1}"
shift 1
local __file="${1}"
validate_ipv4 "${__ip}" \
&& echo "${__ip}" > "${__file}" \
&& find_string_in_file "${__ip}" "${__file}" \
&& return 0
return 1
}
# desc: update route 53 record
# usage: update_route53_record "<zoneid>" "<ipv4>" "<recordset>" "<type>" "<ttl>" "<comment>" "<aws-profile>"
# 0 => if route 53 record update was successful
# 1 => if route 53 record update failed
function update_route53_record () {
if [[ $# -lt 7 ]] ; then
echo "Function [${FUNCNAME[0]}]: Wrong argument count. Nothing updated"
return 1
fi
local __zoneid="${1}"; shift
local __ip="${1}"; shift
local __recordset="${1}"; shift
local __type="${1}"; shift
local __ttl="${1}"; shift
local __comment="${1}"; shift
local __aws_profile="${1}";
local __tmpFile;
__tmpFile=$(mktemp /var/tmp/aws_route53_change-resource-record-set.json.XXXXXXXX)
# trap temp file deletion on RETURN signal
trap 'rm -rf "${__tmpFile}"' RETURN
# heredoc section
cat << EOF > "${__tmpFile}"
{
"Comment":"${__comment}",
"Changes":[
{
"Action":"UPSERT",
"ResourceRecordSet":{
"ResourceRecords":[
{
"Value":"${__ip}"
}
],
"Name":"${__recordset}",
"Type":"${__type}",
"TTL":${__ttl}
}
}
]
}
EOF
# Update the route 53 record
aws --profile "${__aws_profile}" route53 change-resource-record-sets \
--hosted-zone-id "${__zoneid}" \
--change-batch "file://${__tmpFile}" \
&& return 0
return 1
}
# MAIN
checkAwsCli || exit 1
# Get the external IP address
# https://unix.stackexchange.com/questions/22615/how-can-i-get-my-external-ip-address-in-a-shell-script
# example1: dig @resolver1.opendns.com A myip.opendns.com +short -4
# example2: dig @ns1-1.akamaitech.net ANY whoami.akamai.net +short -4
# example3: dig @ns1.google.com TXT o-o.myaddr.l.google.com +short -4 | sed -e 's#"##g'
IPv4=$(dig @resolver1.opendns.com A myip.opendns.com +short -4)
{
# validate ipv4
validate_ipv4 "${IPv4}" \
&& touch "${IPFILE}"; # touching ipfile (creates it, if it does not exist)
} || exit 1
# Check current ip with previously save ip from the ipfile
if (find_string_in_file "${IPv4}" "${IPFILE}" ); then
echo "External IPv4 is still the same."
exit 0
else
echo "External Ipv4 has changed to ${IPv4}"
# Update the record when the ip has changed
if (update_route53_record "${ZONEID}" "${IPv4}" "${RECORDSET}" "${TYPE}" "${TTL}" "${COMMENT}" "${aws_profile}" ); then
echo "AWS Route 53 record \"${RECORDSET}\" with type \"${TYPE}\" has been updated with value \"${IPv4}\" in zone ${ZONEID}"
cache_ipv4 "${IPv4}" "${IPFILE}"
exit 0
else
echo "Failed to update AWS Route 53 record: ${RECORDSET}"
exit 1
fi
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment