Initial commit

master
Paco Hope 2017-02-06 09:43:06 +00:00
commit 93081cd6fc
3 changed files with 168 additions and 0 deletions

BIN
Dynamic DNS blog.docx Normal file

Binary file not shown.

142
auto-update-r53.py Normal file
View File

@ -0,0 +1,142 @@
#!/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 )

26
dns-update-policy.json Normal file
View File

@ -0,0 +1,26 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"route53:ChangeResourceRecordSets",
"route53:GetHostedZone",
"route53:ListResourceRecordSets"
],
"Effect": "Allow",
"Resource": [
"arn:aws:route53:::hostedzone/[ZONEID]",
]
},
{
"Action": [
"route53:ListHostedZones",
"ec2:DescribeInstances"
],
"Effect": "Allow",
"Resource": [
"*"
]
}
]
}