Update resource record in Route53 hosted zone on EC2 instance boot

Florian | Mar 14, 2018

Using a jump host to access resources within a private subnet is common setup in a VPC and the recommendation given by AWS. By design this jump host should not always be up and running and only be started if someone needs access to private resources e.g. a database.

If you do not assign an Elastic IP to this host its IP and automatically assigned external hostname will change every time the machine is started. To avoid looking up this new hostname every time the machine boots or communicating the new hostname / IP to other team members it is possible to have the machine automatically update a DNS record in Route53.

First of all you will need to create your EC2 instance that should be used as the jump host with an IAM machine role that allows the machine to update the Route53 resource record.

The policy to allow this looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["route53:ChangeResourceRecordSets", "route53:GetHostedZone", "route53:ListResourceRecordSets"],
      "Resource": ["arn:aws:route53:::hostedzone/your-hosted-zone-id"]
    },
    {
      "Effect": "Allow",
      "Action": ["route53:ListHostedZones"],
      "Resource": ["*"]
    }
  ]
}

Replace your-hosted-zone-id with the ID of your hosted zone. You can find this ID in the Route53 console.

Once you created your EC2 instance with an IAM machine role that has the above policy attached to it you should create the resource record you want automatically updated everytime the EC2 instance boots. Let’s assume your hosted zone is example.com and your jump host should be reachable with jumphost.example.com. Create a new CNAME ressource and point it to your current EC2 instance hostname e.g. ec2-1-2-3-4.eu-central-1.compute.amazonaws.com.

Now SSH into your jump host and create the file /usr/local/bin/update-route53-record.sh with the following contents:

#!/bin/bash

HOSTED_ZONE_ID=FOOBAR
HOSTNAME=jumphost.example.com.
EC2_HOSTNAME=$(/usr/bin/ec2metadata | grep 'public-hostname:' | cut -d ' ' -f 2)

/usr/bin/aws route53 change-resource-record-sets --hosted-zone-id=$HOSTED_ZONE_ID --change-batch="{\"Changes\":[{\"Action\":\"UPSERT\",\"ResourceRecordSet\":{\"Name\":\"${HOSTNAME}\",\"Type\":\"CNAME\",\"TTL\":60,\"ResourceRecords\":[{\"Value\":\"${EC2_HOSTNAME}\"}]}}]}"

Again replace the values for HOSTED_ZONE_ID and HOSTNAME with the values for your setup. Be aware that the hostname requires the “.” at the end.

Make the script executable by running $ chmod +x /usr/local/bin/update-route53-record.sh. Now you can test it by running the script with /usr/local/bin/update-route53-record.sh. If everything is works correctly the output should look similar to this:

{
  "ChangeInfo": {
    "Status": "PENDING",
    "SubmittedAt": "2018-03-14T20:37:25.573Z",
    "Id": "/change/XXXXXXXXXXX"
  }
}

This means that your DNS hostname was successfully updated with the hostname of the EC2 instance.

The last step is to ensure this script runs every time the machine starts up. Create a systemd service by creating the file /lib/systemd/system/update-route53-record.service with the following content:

[Unit]
After=networking.service

[Service]
ExecStart=/usr/local/bin/update-route53-record.sh

[Install]
WantedBy=default.target

And finally inform systemd about the new service and enable it.

$ systemctl daemon-reload
$ systemctl enable update-route53-record.service

You now can shutdown your EC2 instance and after you start it up again the record will be automatically updated. Please note since the TTL for the record is 60 seconds it may take this amount of time until you can connect after the record was updated.