Skip to content

Instantly share code, notes, and snippets.

@orome
Last active February 22, 2017 14:04
Show Gist options
  • Save orome/f813d7947e557dd97987c1886869260f to your computer and use it in GitHub Desktop.
Save orome/f813d7947e557dd97987c1886869260f to your computer and use it in GitHub Desktop.
AWS Route 53 script nameserver setting bug
from __future__ import (absolute_import, print_function, division, unicode_literals)
import boto.beanstalk
import boto.exception
import boto.ec2.elb
import boto.route53.connection
import boto.route53.record
import boto.route53.domains.layer1
MY_ENV = 'my-env'
MY_REGION = 'us-west-1'
_ERROR_EXCEPTIONS = False
def exit_error(message, exception):
"""Handle exceptions that are not bugs, e.g. records that may just not be present (yet)"""
print(message)
if __name__ == '__main__' and not _ERROR_EXCEPTIONS:
raise SystemExit
else:
raise exception
def get_eip_or_elb(envname, region):
"""Get the EIP or ELB for this EB environment and determine whether it is load balanced"""
try:
env = boto.beanstalk.connect_to_region(region).describe_environment_resources(environment_name=envname)
except boto.exception.BotoServerError as e:
exit_error('ERROR: Environment not found: {}'.format(envname), e)
for resource in (env['DescribeEnvironmentResourcesResponse']['DescribeEnvironmentResourcesResult']
['EnvironmentResources']['Resources']):
if resource['LogicalResourceId'] == 'AWSEBLoadBalancer':
lbname = resource['PhysicalResourceId']
elif resource['LogicalResourceId'] == 'AWSEBEIP':
eip = resource['PhysicalResourceId']
lb = None
try:
lb = boto.ec2.elb.connect_to_region(region).get_all_load_balancers(load_balancer_names=(lbname))[0]
dns = lb.canonical_hosted_zone_name
host_is_elb = True
except NameError:
dns = eip
host_is_elb = False
return dns, lb, host_is_elb
def set_a_record(hz, dns, lb, ttl, host_is_elb, verbose):
"""Set the A Record for a given Route 53 Hosted Zone"""
add_change_args_upsert = {
'action': 'UPSERT',
'name': hz.name[:-1],
'type': 'A'
}
records = hz.get_records()
if host_is_elb:
if verbose:
print('Creating ALIAS Record: {}'.format(dns))
add_change_args_upsert['alias_hosted_zone_id'] = lb.canonical_hosted_zone_name_id
add_change_args_upsert['alias_dns_name'] = dns
add_change_args_upsert['alias_evaluate_target_health'] = False
change = records.add_change(**add_change_args_upsert)
else:
if verbose:
print('Creating A Record: {}'.format(dns))
add_change_args_upsert['ttl'] = ttl
change = records.add_change(**add_change_args_upsert)
change.add_value(dns)
records.commit()
def hzcreate(domain=None, verbose=False):
"""Create a Hosted Zone for the specified domain and update nameservers if Route 53 Registered Domain exists"""
domain = domain.lower()
r53 = boto.route53.connection.Route53Connection()
if r53.get_hosted_zone_by_name(domain + '.'):
print('WARNING: Hosted Zone for {} already exists.'.format(domain))
hz = r53.get_zone(domain + '.')
else:
if verbose:
print('Creating Hosted Zone for {}.'.format(domain))
hz = r53.create_zone(domain + '.')
nameservers = hz.get_nameservers()
if verbose:
print('Hosted Zone has nameservers:')
for ns in nameservers:
print(' {}'.format(ns))
return
def cname(envname=MY_ENV, domain=None, region=MY_REGION, ttl=60, verbose=False, subdomain='www', cname=None):
"""Set a CNAME record for a Route 53 hosted domain"""
domain = domain.lower()
name = '.'.join([subdomain, domain])
cname = domain if cname is None else cname
try:
env = boto.beanstalk.connect_to_region(region).describe_environment_resources(environment_name=envname)
except boto.exception.BotoServerError:
return 'Environment not found: {}'.format(envname)
r53 = boto.route53.connection.Route53Connection()
try:
hz = r53.get_zone(domain)
if hz is None:
print('ERROR: No Route 53 Hosted Zone for {}.'.format(domain))
except Exception as e:
print('ERROR: There was a problem locating the 53 Hosted Zone for: {}'.format(domain))
if __name__ == '__main__':
raise SystemExit
else:
raise e
add_change_args_upsert = {
'action': 'UPSERT',
'name': name,
'type': 'CNAME',
'ttl': ttl
}
records = hz.get_records()
if verbose:
print('Creating CNAME Record: {} -> {}'.format(name, cname))
change = records.add_change(**add_change_args_upsert)
change.add_value(cname)
records.commit()
return
# BUG - This only puts one nameserver in the NS record, leaving the remaining three out
def host(envname=MY_ENV, domain=None, region=MY_REGION, ttl=60, verbose=False, comment=None):
"""Set DNS records to host a domain at an EB environment, creating Route 53 Hosted Zone for domain if none exists"""
domain = domain.lower()
if verbose:
print("Setting hosting for {} at '{}'".format(domain, envname))
dns, lb, host_is_elb = get_eip_or_elb(envname, region)
r53 = boto.route53.connection.Route53Connection()
try:
hz = r53.get_zone(domain)
if hz is None:
if verbose:
print('Creating a new Route 53 Hosted Zone with hzcreate')
hzcreate(domain, verbose)
except Exception as e:
exit_error('ERROR: There was a problem locating the 53 Hosted Zone for: {}'.format(domain), e)
hz = r53.get_zone(domain)
change_set = boto.route53.record.ResourceRecordSets(r53, hz.id)
for rrset in r53.get_all_rrsets(hz.id):
u = change_set.add_change(str('UPSERT'), rrset.name, rrset.type, ttl=ttl)
u.add_value(rrset.resource_records[0])
results = change_set.commit()
# Set up the A record to point th the EIP/ELB
set_a_record(hz, dns, lb, ttl, host_is_elb, verbose)
# Create CNAME for www by default
cname(envname, domain, region, ttl, verbose, 'www')
return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment