How can I automate AWS Route53 DNS update based on EC2 instance tag names?

Viewed 1388

I want to implement an automatic way of updating Route53 DNS based on EC2 tags of the instances.

Imagine that I can have a tag name named dns_name which is supposed to document the expected dns name of the machine.

If I shutdown the machine, upgrade it and start it again, I do want to get the DNS updated without having to change anything.

I don't want to install a DNS update service on the machine as this would require me to put AWS credentials on all the VMs, and to deploy this to them.

It seems much easier to use AWS EC2 tags to declare the expected names. A cron job could easily check for changes and update the DNS.

Did anyone implement this?

4 Answers

I was trying to do exactly what I think your attempting to do.

What I've found is this script from this link which is a good starting point.

https://medium.com/@dbclin/a-bash-script-to-update-an-existing-aws-security-group-with-my-new-public-ip-address-d0c965d67f28

#!/bin/bash
# Extract information about the Instance
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id/)
AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone/)
MY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/)

# Extract tags associated with instance
ZONE_TAG=$(aws ec2 describe-tags --region ${AZ::-1} --filters "Name=resource-id,Values=${INSTANCE_ID}" --query 'Tags[?Key==`AUTO_DNS_ZONE`].Value' --output text)
NAME_TAG=$(aws ec2 describe-tags --region ${AZ::-1} --filters "Name=resource-id,Values=${INSTANCE_ID}" --query 'Tags[?Key==`AUTO_DNS_NAME`].Value' --output text)

# Update Route 53 Record Set based on the Name tag to the current Public IP address of the Instance
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_TAG --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"'$NAME_TAG'","Type":"A","TTL":300,"ResourceRecords":[{"Value":"'$MY_IP'"}]}}]}'

This has details of the solution suggested by Mark B. Elastic IP doesn't work if you don't have any available and Amazon allows you only 5.

I used user14441273's pasted script as a starting point. The provided link for that is dead (redirects you to an article nothing to do with Route53), and the post gives no clue how to set up the required tags ZONE_TAG or NAME_TAG.

Regardless how you do it, you will need to allow the actions to the instance profile role.

Here's the productonlized working script. I get the instance name from local file system, which is much faster and more reliable than using a network service. Change the domain to the domain you want to use:

#!/usr/bin/env bash

set -e
shopt -s xpg_echo
set +u

DNS_DOMAIN=yourdomain.com
TTL_S=30  # You want this low or you will have to wait up to this long for changes to propagate

PATH="$PATH:/usr/local/bin"

# Extract information about the Instance
INSTNAME=$(ls -ld /var/lib/cloud/instance | sed 's@.*/@@;')
[ -n "$INSTNAME" ] || {
    echo 'Failed to determine Instance name' 1>&2
    false
}
[ -f /tmp/date.txt ] && {
      echo "date present" 1>&2
    false
}
AWS_REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone  | sed 's/.$//')
[ -n "$AWS_REGION" ] || {
    echo 'Failed to determine AWS Region' 1>&2
    false
}
HOSTNAME=$(aws ec2 describe-tags --region $AWS_REGION --filters Name=resource-type,Values=instance Name=resource-id,Values=$INSTNAME Name=key,Values=Name --query 'Tags[0].Value' --output text) 
[ -n "$HOSTNAME" ] || {
    echo 'Failed to determine DNS Hostname to update' 1>&2
    false
}
MY_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4/)
[ -n "$MY_IP" ] || {
    echo 'Failed to determine my public IP address' 1>&2
    false
}
ZONE_ID=$(aws route53 --region $AWS_REGION list-hosted-zones-by-name --region $AWS_REGION --dns-name $DNS_DOMAIN --query 'HostedZones[0].Id' --output text)
[ -n "$ZONE_ID" ] || {
    echo 'Failed to determine my Route53 Zone ID' 1>&2
    false
}

aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID --region $AWS_REGION --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"'${HOSTNAME}.$DNS_DOMAIN'","Type":"A","TTL":'$TTL_S',"ResourceRecords":[{"Value":"'$MY_IP'"}]}}]}'
enter code here

Assuming that you're running a late-model Linux variant, here's a working service script. N.b. I have it configured to run the shell script from /usr/local/sbin which I have NFS mounted:

[Unit]
Description=Update Route53 DNS Service with instance's current public IP addr
RequiresMountsFor=/usr/local

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/updateRoute53.bash
RemainAfterExit=no
StandardOutput=truncate:/var/log/updateRoute53.log

[Install]
WantedBy=multi-user.target
Related