#!/usr/bin/python # # Dynamically update DNS records when the EC2 instance boots. # Requires boto3 installed on the instance. # Requries an IAM policy attached to an instance role. # import urllib2 from boto3.session import Session import boto3 import datetime def getRegion(): url = "http://169.254.169.254/2009-04-04/meta-data/placement/availability-zone" try: resp = urllib2.urlopen(urllib2.Request(url)) # Region will be a string like eu-central-1a. This chops the last character off. region = resp.read()[:-1] except urllib2.HTTPError as e: if e.code == 404: return None if e.code == 500: return None raise return region def getInstanceId(): url = "http://169.254.169.254/2009-04-04/meta-data/instance-id" try: resp = urllib2.urlopen(urllib2.Request(url)) instanceid = resp.read() except urllib2.HTTPError as e: if e.code == 404: return None if e.code == 500: return None raise return instanceid def getPublicIP(): url = "http://169.254.169.254/2009-04-04/meta-data/public-ipv4" try: resp = urllib2.urlopen(urllib2.Request(url)) value = resp.read() except urllib2.HTTPError as e: if e.code == 404: return None if e.code == 500: return None raise return value # Get region and instance ID myregion = getRegion() myinstance = getInstanceId() myipv4 = getPublicIP() if( myregion == None or myinstance == None or myipv4 == None): print "ERROR: Could not get instance metadata. Abort." exit( -1 ) # Connect to EC2 and get the instance handle for this instance ec2 = boto3.resource('ec2', region_name=myregion) instance = ec2.Instance(myinstance) # Grab the tags associated with this instance mytags = instance.tags dnsname = None # Get host tag or bail for tag in instance.tags: if( tag['Key'] == 'dnsname'): dnsname = tag['Value'] if (dnsname == None): print "Did not find dnsname tag. " exit( -1 ) # Get just the domain part of dnsname myzone = dnsname.partition(".")[2] # Get zoneid for zone or bail session = Session(region_name=myregion) client = session.client('route53') # Note: if you have more than 100 hosted zones, there's a chance that # the zone you need won't be returned in response to the first call # to list_hosted_zones(). The next loop will fail to find your zone. # # If this applies to you, you have to rewrite this section to use # pagination to iterate over the collection of returned domains, # looking for the one you want. That extension is left as an # exercise for the reader. myzoneid = None zones = client.list_hosted_zones() # Route53 will return zones in canonical form with the trailing dot. # e.g.: "ec2.example.com." # It's likely that people will enter dnsnames in the tags without the # trailing dot, so this helps make the match either way. for zone in zones['HostedZones']: if( (zone['Name'] == myzone) or (zone['Name'] == myzone + ".")): myzoneid = zone['Id'] if( myzoneid == '' ): # didn't find the zone print "ERROR: Did not find " + myzone + " among the hosted zones." exit( -1 ) # Create record set update print "setting " + dnsname + " to " + myipv4 now = datetime.datetime.now() response = client.change_resource_record_sets( HostedZoneId=myzoneid, ChangeBatch={ 'Comment': 'Instance ' + myinstance + ' boot at ' + now.strftime("%Y-%m-%d %H:%M"), 'Changes': [ { 'Action': 'UPSERT', 'ResourceRecordSet': { 'TTL': 300, 'Name': dnsname, 'Type': 'A', 'ResourceRecords': [ { 'Value': myipv4 }, ] } }, ] } ) # Check for error. if( response['ResponseMetadata']['HTTPStatusCode'] != 200 ): print "ERROR updating Route53" print response exit( -1 )