How to Update a Route53 Record on Instance Boot

I’ve been experimenting with running single instances in an autoscaling group. These single instances tend to be backing services that, while important, contain only ephemeral data that’s okay being lost. A good example would be something like beanstalkd for a queue.

Part of that process is registering the new instance with a Route 53 hosted zone — I tend to use this as a service discovery-ish thing. The strategy outlined below could work just fine to register a server with a real service discovery solution as well.

To make this work you’ll need to a build an AMI with a script that calls ChangeResourceRecordSets and then setup the instance to call the script on boot.

I’m not going to cover building an AMI, but packer is worth looking into on that front.

Updating Record Sets

Each EC2 instance comes with an instance metadata service that can be called to learn things about the instance. One of those things is the IP address of the machine in its VPC. Here’s a snippet to retrieve that value in python 3.

import http.client as http

def _get_local_ipv4():
    conn = http.HTTPConnection('169.254.169.254', 80)
    conn.request('GET', '/latest/meta-data/local-ipv4')
    ipr = conn.getresponse()
    return ipr.read().decode('utf-8')

And you might combine this with the AWS sdk to update the record set.

import boto3 as aws

def _update_dns(ip, zone_id, hostname):
    dns = aws.client('route53')
    dns.change_resource_record_sets(
        HostedZoneId=zone_id,
        ChangeBatch={
            'Comment': 'Update {} record from ASG'.format(args.hostname),
            'Changes': [{
                'Action': 'UPSERT',
                'ResourceRecordSet': {
                    'Name': hostname,
                    'Type': 'A',
                    'TTL': 60,
                    'ResourceRecords': [{
                        'Value': ip
                    }],
                },
            }],
        },
    )

Combine that with a bit of argparse and you have a usable script.

Invoking the DNS Script on Instance Boot

Setting an instances user data to something begining with a hash bang and a shell (like #!/bin/bash) will cause the user data to be invoked as a shell script when the instance boots for the first time.

On the configure instance page you can enter this under advanced details. So say you the script above resides as /usr/local/bin/update_dns:

#!/bin/bash
/usr/local/bin/update_dns "someRoute53ZoneId" "whatever.yourdns.internal"

Working with an Auto Scaling Group

The user data goes into your launch configuration. I do most of this sort of thing with terraform, so here’s a quick example.

resource "aws_launch_configuration" "beanstalkd" {
    name_prefix = "some-launch-config-"
    # ...
    user_data = <<EOF
#!/bin/bash
/usr/local/bin/update_dns "someRoute53ZoneId" "whatever.yourdns.internal"
EOF
}