Last active
February 22, 2017 14:04
-
-
Save orome/f813d7947e557dd97987c1886869260f to your computer and use it in GitHub Desktop.
AWS Route 53 script nameserver setting bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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