-
-
Save richardnbanks/3385867 to your computer and use it in GitHub Desktop.
Amazon Route 53 Dynamic DNS (CNAME) Updater Script
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 | |
# | |
# This script requires xpath to parse part of the dnscurl.pl output | |
# on CentOS/RedHat/Amazon Linux: | |
# | |
# sudo yum install perl-XML-XPath | |
# | |
# also, dnscurl.pl (from http://aws.amazon.com/code/Amazon-Route-53/9706686376855511) | |
# expects your secrets to be in ~/.aws-secrets | |
# using a file format like this (from http://dmz.us/wp/wp-content/uploads/r53/aws-secrets.txt) | |
# | |
# %awsSecretAccessKeys = ( | |
# "my-aws-account" => { | |
# id => "YOUR_ID_GOES_HERE_KEEP_THE_QUOTES", | |
# key => "YOUR_SECRET_KEY_GOES_HERE_KEEP_THE_QUOTES", | |
# }, | |
# ); | |
### User Settings (things you must set) | |
## Location of the dnscurl.pl script | |
DNSCurl="/path/to/route53DynDNS/dnscurl.pl" | |
## Zone/domain in which host (will) reside(s) | |
myDomainName="example.com" | |
## The host name you wish to update/create | |
myHostName=`hostname -f | sed -e "s/.$myDomainName//"` | |
########################################## | |
### Things you may want to set | |
## set the TTL for the new/updated A record | |
myTTL="60" | |
route53API="https://route53.amazonaws.com/2011-05-05" | |
route53APIDoc="https://route53.amazonaws.com/doc/2011-05-05/" | |
########################################## | |
### Thing you shouldn't change | |
## how long to wait after submitting a change | |
## before checking to see if it's state is | |
## INSYNC | |
sleepDelay=20 | |
XPATH="xpath -e " | |
XPATH="xpath " | |
########################################## | |
### Code... don't change anything below. | |
## Get the Hosted Zone ID from Route 53 | |
hostedZoneIDSearch="ListHostedZonesResponse/HostedZones/HostedZone[Name=\"$myDomainName.\"]/Id" | |
route53HostedZoneID=`$DNSCurl --keyname my-aws-account -- -s -H "Content-Type: text/xml; charset=UTF-8" -X GET $route53API/hostedzone 2>/dev/null | $XPATH $hostedZoneIDSearch 2>/dev/null |awk -F'[<|>]' '/Id/{print $3}' | cut -d/ -f3` | |
if [ -z $route53HostedZoneID ]; then | |
echo "Could not find zone '$myDomainName' in route 53" | |
exit 1 | |
fi | |
## Check to see if the A record already exists | |
currentARecordValueSearch="ListResourceRecordSetsResponse/ResourceRecordSets/ResourceRecordSet[Name=\"$myHostName.$myDomainName.\"]/ResourceRecords/ResourceRecord/Value" | |
if [[ "$myHostName" == "*" ]]; then | |
currentARecordValueSearch="ListResourceRecordSetsResponse/ResourceRecordSets/ResourceRecordSet[Name=\"\\052.$myDomainName.\"]/ResourceRecords/ResourceRecord/Value" | |
fi | |
currentARecordValue=`$DNSCurl --keyname my-aws-account -- -s -H "Content-Type: text/xml; charset=UTF-8" -X GET $route53API/hostedzone/$route53HostedZoneID/rrset 2>/dev/null | $XPATH $currentARecordValueSearch 2>/dev/null | awk -F'[<|>]' '/Value/{print $3}' | cut -d/ -f3` | |
## And if not, set a flag to create the A record | |
if [ -z $currentARecordValue ]; then | |
echo "Could not find CNAME Resource Record for $myHostName.$myDomainName." | |
echo "Creating initial record" | |
CREATE_INITIAL=true | |
fi | |
## Route 53 updates require the A record name, ttl and value for the delete. | |
## Fetch the TTL | |
currentARecordTTLSearch="ListResourceRecordSetsResponse/ResourceRecordSets/ResourceRecordSet[Name=\"$myHostName.$myDomainName.\"]/TTL" | |
if [[ "$myHostName" == "*" ]]; then | |
currentARecordTTLSearch="ListResourceRecordSetsResponse/ResourceRecordSets/ResourceRecordSet[Name=\"\\052.$myDomainName.\"]/TTL" | |
fi | |
currentARecordTTL=`$DNSCurl --keyname my-aws-account -- -s -H "Content-Type: text/xml; charset=UTF-8" -X GET $route53API/hostedzone/$route53HostedZoneID/rrset 2>/dev/null | $XPATH $currentARecordTTLSearch 2>/dev/null | awk -F'[<|>]' '/TTL/{print $3}' | cut -d/ -f3` | |
if [ -z $currentARecordTTL ]; then | |
CREATE_INITIAL=true | |
fi | |
## Get the public Hostname | |
myPublicHostname=`wget -q -O - http://169.254.169.254/latest/meta-data/public-hostname` | |
if [ -z $myPublicHostname ]; then | |
echo "Failed to look up public hostname" | |
exit 1 | |
fi | |
## Generate a timestamp for the update | |
timestamp=`date` | |
## Give some status | |
echo "Public hostname is: $myPublicHostname" | |
## If the DNS A Resource Record matched the public hostname, exit | |
if [[ "$myPublicHostname" == "$currentARecordValue" ]]; then | |
echo "Current CNAME Resource Record address is: $currentARecordValue" | |
echo "No need to update CNAME record." | |
exit 0 | |
fi | |
## Get a temp file | |
tmpxml=`mktemp` | |
#CREATE_INITIAL=false | |
## Determine if the A Resource Record is to be created or updated (deleted/created) | |
if [ $CREATE_INITIAL ]; then | |
echo "Creating CNAME record for $myHostName.$myDomainName. to be $myPublicHostname" | |
cat <<UPDATE-XML > $tmpxml | |
<?xml version="1.0" encoding="UTF-8"?> | |
<ChangeResourceRecordSetsRequest xmlns="$route53APIDoc"> | |
<ChangeBatch> | |
<Comment>Create Record for $myHostName.$myDomainName at $timestamp</Comment> | |
<Changes> | |
<Change> | |
<Action>CREATE</Action> | |
<ResourceRecordSet> | |
<Name>$myHostName.$myDomainName.</Name> | |
<Type>CNAME</Type> | |
<TTL>$myTTL</TTL> | |
<ResourceRecords> | |
<ResourceRecord> | |
<Value>$myPublicHostname</Value> | |
</ResourceRecord> | |
</ResourceRecords> | |
</ResourceRecordSet> | |
</Change> | |
</Changes> | |
</ChangeBatch> | |
</ChangeResourceRecordSetsRequest> | |
UPDATE-XML | |
else | |
echo "A Record for $myHostName.$myDomainName. is $currentARecordValue with ttl: $currentARecordTTL" | |
echo "Updating CNAME record for $myHostName.$myDomainName. to be $myPublicHostname with ttl $myTTL" | |
cat <<UPDATE-XML > $tmpxml | |
<?xml version="1.0" encoding="UTF-8"?> | |
<ChangeResourceRecordSetsRequest xmlns="$route53APIDoc"> | |
<ChangeBatch> | |
<Comment>Update Record for $myHostName.$myDomainName at $timestamp</Comment> | |
<Changes> | |
<Change> | |
<Action>DELETE</Action> | |
<ResourceRecordSet> | |
<Name>$myHostName.$myDomainName.</Name> | |
<Type>CNAME</Type> | |
<TTL>$currentARecordTTL</TTL> | |
<ResourceRecords> | |
<ResourceRecord> | |
<Value>$currentARecordValue</Value> | |
</ResourceRecord> | |
</ResourceRecords> | |
</ResourceRecordSet> | |
</Change> | |
<Change> | |
<Action>CREATE</Action> | |
<ResourceRecordSet> | |
<Name>$myHostName.$myDomainName.</Name> | |
<Type>A</Type> | |
<TTL>$myTTL</TTL> | |
<ResourceRecords> | |
<ResourceRecord> | |
<Value>$myPublicHostname</Value> | |
</ResourceRecord> | |
</ResourceRecords> | |
</ResourceRecordSet> | |
</Change> | |
</Changes> | |
</ChangeBatch> | |
</ChangeResourceRecordSetsRequest> | |
UPDATE-XML | |
fi | |
## Do the actual create/update | |
updateResponse=`$DNSCurl --keyname my-aws-account -- -s -H "Content-Type: text/xml; charset=UTF-8" -X POST --upload-file $tmpxml $route53API/hostedzone/$route53HostedZoneID/rrset 2>/dev/null` | |
## And record the response | |
updateResponseStatus=`echo $updateResponse | $XPATH 'ChangeResourceRecordSetsResponse/ChangeInfo/Status' 2>/dev/null |awk -F'[<|>]' '/Status/{print $3}' | cut -d/ -f3` | |
## Make sure the response is "PENDING" | |
## Otherwise, error | |
if [[ "$updateResponseStatus" != "PENDING" ]]; then | |
echo "Update is not in PENDING status" | |
echo $updateResponse | |
rm -f $tmpxml | |
exit 1 | |
fi | |
## Get the transaction ID | |
updateResponseID=`echo $updateResponse | $XPATH 'ChangeResourceRecordSetsResponse/ChangeInfo/Id' 2>/dev/null | awk -F'[<|>]' '/Id/{print $3}' | cut -d/ -f3` | |
echo "Got update status ID $updateResponseID with status $updateResponseStatus" | |
echo "Pausing $sleepDelay seconds to allow for sync" | |
sleep $sleepDelay | |
## Check for status | |
statusCheckResponse=`$DNSCurl --keyname my-aws-account -- -s -H "Content-Type: text/xml; charset=UTF-8" -X GET $route53API/change/$updateResponseID 2>/dev/null` | |
statusCheckResponseStatus=`echo $statusCheckResponse | $XPATH 'GetChangeResponse/ChangeInfo/Status' 2>/dev/null | awk -F'[<|>]' '/Status/{print $3}' | cut -d/ -f3` | |
if [[ "$statusCheckResponseStatus" != "INSYNC" ]]; then | |
echo "update failed!" | |
echo "Status is $statusCheckResponseStatus" | |
rm -f $tmpxml | |
exit 1 | |
fi | |
## Success, clean up | |
echo "Success!" | |
rm -f $tmpxml | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment