def __init__(self, dns_name=None, **kwargs): self.r53 = Route53Connection(**kwargs) if dns_name is not None: self.zone = self.get_zone(dns_name) if not self.zone: raise IcsR53Exception( "Can't find DNS Zone for '%s'" % (dns_name))
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict(zone=dict(required=True), state=dict(default='present', choices=['present', 'absent']), vpc_id=dict(default=None), vpc_region=dict(default=None), comment=dict(default=''))) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') zone_in = module.params.get('zone').lower() state = module.params.get('state').lower() vpc_id = module.params.get('vpc_id') vpc_region = module.params.get('vpc_region') comment = module.params.get('comment') private_zone = vpc_id is not None and vpc_region is not None _, _, aws_connect_kwargs = get_aws_connection_info(module) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError, e: module.fail_json(msg=e.error_message)
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( state = dict(choices=['present', 'absent'], default='present'), ip_address = dict(), port = dict(type='int'), type = dict(required=True, choices=['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP']), resource_path = dict(), fqdn = dict(), string_match = dict(), request_interval = dict(type='int', choices=[10, 30], default=30), failure_threshold = dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default=3), ) ) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto 2.27.0+ required for this module') state_in = module.params.get('state') ip_addr_in = module.params.get('ip_address') port_in = module.params.get('port') type_in = module.params.get('type') resource_path_in = module.params.get('resource_path') fqdn_in = module.params.get('fqdn') string_match_in = module.params.get('string_match') request_interval_in = module.params.get('request_interval') failure_threshold_in = module.params.get('failure_threshold') if ip_addr_in is None and fqdn_in is None: module.fail_json(msg="parameter 'ip_address' or 'fqdn' is required") # Default port if port_in is None: if type_in in ['HTTP', 'HTTP_STR_MATCH']: port_in = 80 elif type_in in ['HTTPS', 'HTTPS_STR_MATCH']: port_in = 443 else: module.fail_json(msg="parameter 'port' is required for 'type' TCP") # string_match in relation with type if type_in in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: if string_match_in is None: module.fail_json(msg="parameter 'string_match' is required for the HTTP(S)_STR_MATCH types") elif len(string_match_in) > 255: module.fail_json(msg="parameter 'string_match' is limited to 255 characters max") elif string_match_in: module.fail_json(msg="parameter 'string_match' argument is only for the HTTP(S)_STR_MATCH types") region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError, e: module.fail_json(msg = e.error_message)
def get_r53_conn(): """ Return a connection to Route 53. """ try: return Route53Connection() except NoAuthHandlerFound: sys.stderr.write('Could not authenticate with AWS. Have you set ' 'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY?') sys.exit(1)
def connect_route53(aws_access_key_id=None, aws_secret_access_key=None, **kwargs): """ :type aws_access_key_id: string :param aws_access_key_id: Your AWS Access Key ID :type aws_secret_access_key: string :param aws_secret_access_key: Your AWS Secret Access Key :rtype: :class:`boto.dns.Route53Connection` :return: A connection to Amazon's Route53 DNS Service """ from boto.route53 import Route53Connection return Route53Connection(aws_access_key_id, aws_secret_access_key, **kwargs)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict(zone=dict(required=True), state=dict(default='present', choices=['present', 'absent']), vpc_id=dict(default=None), vpc_region=dict(default=None), comment=dict(default=''), hosted_zone_id=dict())) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') zone_in = module.params.get('zone').lower() state = module.params.get('state').lower() vpc_id = module.params.get('vpc_id') vpc_region = module.params.get('vpc_region') if not zone_in.endswith('.'): zone_in += "." private_zone = bool(vpc_id and vpc_region) _, _, aws_connect_kwargs = get_aws_connection_info(module) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError as e: module.fail_json(msg=e.error_message) zones = find_zones(conn, zone_in, private_zone) if state == 'present': changed, result = create(conn, module, matching_zones=zones) elif state == 'absent': changed, result = delete(conn, module, matching_zones=zones) if isinstance(result, dict): module.exit_json(changed=changed, result=result, **result) else: module.exit_json(changed=changed, result=result)
def _initialise_aws_connections(): _log.info('Initialising AWS Connections') _validate_environment() # Even though the Boto lib can use the environment variables we'll import one # for easier re-use in this script _log.info('Loading credentials from Environment') aws_region = os.getenv('AWS_DEFAULT_REGION') _log.info('Initializing Boto resources') resources = { 'vpc': vpc.connect_to_region(aws_region), 'ec2': ec2.connect_to_region(aws_region), 'elb': elb.connect_to_region(aws_region), 'iam': iam.connect_to_region(aws_region), 'route53': Route53Connection(), 'cloudwatch': cloudwatch.connect_to_region(aws_region), 'region': aws_region } return resources
def get_input(environ): ''' ''' github_client_id = environ['GITHUB_CLIENT_ID'] github_client_secret = environ['GITHUB_CLIENT_SECRET'] gdocs_client_id = environ['GDOCS_CLIENT_ID'] gdocs_client_secret = environ['GDOCS_CLIENT_SECRET'] print('--> Enter Github details:') username = raw_input(' Github username: '******' Github password: '******' New Github repository name: ') if not match(r'\w+(-\w+)*$', reponame): raise RuntimeError( 'Repository "{}" does not match "\w+(-\w+)*$"'.format(reponame)) ec2 = EC2Connection() route53 = Route53Connection() return github_client_id, github_client_secret, \ gdocs_client_id, gdocs_client_secret, \ username, password, reponame, ec2, route53
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( state=dict(choices=['present', 'absent'], default='present'), ip_address=dict(), port=dict(type='int'), type=dict(required=True, choices=[ 'HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP' ]), resource_path=dict(), fqdn=dict(), string_match=dict(), request_interval=dict(type='int', choices=[10, 30], default=30), failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default=3), )) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto 2.27.0+ required for this module') state_in = module.params.get('state') ip_addr_in = module.params.get('ip_address') port_in = module.params.get('port') type_in = module.params.get('type') resource_path_in = module.params.get('resource_path') fqdn_in = module.params.get('fqdn') string_match_in = module.params.get('string_match') request_interval_in = module.params.get('request_interval') failure_threshold_in = module.params.get('failure_threshold') if ip_addr_in is None and fqdn_in is None: module.fail_json(msg="parameter 'ip_address' or 'fqdn' is required") # Default port if port_in is None: if type_in in ['HTTP', 'HTTP_STR_MATCH']: port_in = 80 elif type_in in ['HTTPS', 'HTTPS_STR_MATCH']: port_in = 443 else: module.fail_json(msg="parameter 'port' is required for 'type' TCP") # string_match in relation with type if type_in in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: if string_match_in is None: module.fail_json( msg= "parameter 'string_match' is required for the HTTP(S)_STR_MATCH types" ) elif len(string_match_in) > 255: module.fail_json( msg="parameter 'string_match' is limited to 255 characters max" ) elif string_match_in: module.fail_json( msg= "parameter 'string_match' argument is only for the HTTP(S)_STR_MATCH types" ) region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError as e: module.fail_json(msg=e.error_message) changed = False action = None check_id = None wanted_config = HealthCheck(ip_addr_in, port_in, type_in, resource_path_in, fqdn_in, string_match_in, request_interval_in, failure_threshold_in) existing_check = find_health_check(conn, wanted_config) if existing_check: check_id = existing_check.Id existing_config = to_health_check(existing_check.HealthCheckConfig) if state_in == 'present': if existing_check is None: action = "create" check_id = create_health_check(conn, wanted_config).HealthCheck.Id changed = True else: diff = health_check_diff(existing_config, wanted_config) if diff: action = "update" update_health_check(conn, existing_check.Id, int(existing_check.HealthCheckVersion), wanted_config) changed = True elif state_in == 'absent': if check_id: action = "delete" conn.delete_health_check(check_id) changed = True else: module.fail_json(msg="Logic Error: Unknown state") module.exit_json(changed=changed, health_check=dict(id=check_id), action=action)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( state=dict( type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']), zone=dict(type='str'), hosted_zone_id=dict(type='str'), record=dict(type='str', required=True), ttl=dict(type='int', default=3600), type=dict(type='str', required=True, choices=[ 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SPF', 'SRV', 'TXT' ]), alias=dict(type='bool'), alias_hosted_zone_id=dict(type='str'), alias_evaluate_target_health=dict(type='bool', default=False), value=dict(type='list'), overwrite=dict(type='bool'), retry_interval=dict(type='int', default=500), private_zone=dict(type='bool', default=False), identifier=dict(type='str'), weight=dict(type='int'), region=dict(type='str'), health_check=dict(type='str'), failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']), vpc_id=dict(type='str'), wait=dict(type='bool', default=False), wait_timeout=dict(type='int', default=300), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=[['zone', 'hosted_zone_id']], # If alias is True then you must specify alias_hosted_zone as well required_together=[['alias', 'alias_hosted_zone_id']], # state=present, absent, create, delete THEN value is required required_if=( ('state', 'present', ['value']), ('state', 'create', ['value']), ('state', 'absent', ['value']), ('state', 'delete', ['value']), ), # failover, region and weight are mutually exclusive mutually_exclusive=[('failover', 'region', 'weight')], # failover, region and weight require identifier required_by=dict( failover=('identifier', ), region=('identifier', ), weight=('identifier', ), ), ) if not HAS_BOTO: module.fail_json(msg='boto required for this module') if distutils.version.StrictVersion( boto.__version__) < distutils.version.StrictVersion( MINIMUM_BOTO_VERSION): module.fail_json( msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION)) if module.params['state'] in ('present', 'create'): command_in = 'create' elif module.params['state'] in ('absent', 'delete'): command_in = 'delete' elif module.params['state'] == 'get': command_in = 'get' zone_in = (module.params.get('zone') or '').lower() hosted_zone_id_in = module.params.get('hosted_zone_id') ttl_in = module.params.get('ttl') record_in = module.params.get('record').lower() type_in = module.params.get('type') value_in = module.params.get('value') or [] alias_in = module.params.get('alias') alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id') alias_evaluate_target_health_in = module.params.get( 'alias_evaluate_target_health') retry_interval_in = module.params.get('retry_interval') if module.params['vpc_id'] is not None: private_zone_in = True else: private_zone_in = module.params.get('private_zone') identifier_in = module.params.get('identifier') weight_in = module.params.get('weight') region_in = module.params.get('region') health_check_in = module.params.get('health_check') failover_in = module.params.get('failover') vpc_id_in = module.params.get('vpc_id') wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) if zone_in[-1:] != '.': zone_in += "." if record_in[-1:] != '.': record_in += "." if command_in == 'create' or command_in == 'delete': if alias_in and len(value_in) != 1: module.fail_json( msg= "parameter 'value' must contain a single dns name for alias records" ) if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None: module.fail_json( msg= "You have specified identifier which makes sense only if you specify one of: weight, region or failover." ) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError as e: module.fail_json(msg=e.error_message) # Find the named zone ID zone_id = hosted_zone_id_in or get_zone_id_by_name( conn, module, zone_in, private_zone_in, vpc_id_in) # Verify that the requested zone is already defined in Route53 if zone_id is None: errmsg = "Zone %s does not exist in Route53" % (zone_in or hosted_zone_id_in) module.fail_json(msg=errmsg) record = {} found_record = False wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in, identifier=identifier_in, weight=weight_in, region=region_in, health_check=health_check_in, failover=failover_in) for v in value_in: if alias_in: wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in) else: wanted_rset.add_value(v) need_to_sort_records = (type_in == 'CAA') # Sort records for wanted_rset if necessary (keep original list) unsorted_records = wanted_rset.resource_records if need_to_sort_records: wanted_rset.resource_records = sorted(unsorted_records) sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone_id, name=record_in, type=type_in, identifier=identifier_in) sets_iter = iter(sets) while True: try: rset = invoke_with_throttling_retries(next, sets_iter) except StopIteration: break # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round # tripping of things like * and @. decoded_name = rset.name.replace(r'\052', '*') decoded_name = decoded_name.replace(r'\100', '@') # Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block rset.name = decoded_name if identifier_in is not None: identifier_in = str(identifier_in) if rset.type == type_in and decoded_name.lower() == record_in.lower( ) and rset.identifier == identifier_in: if need_to_sort_records: # Sort records rset.resource_records = sorted(rset.resource_records) found_record = True record['zone'] = zone_in record['type'] = rset.type record['record'] = decoded_name record['ttl'] = rset.ttl record['identifier'] = rset.identifier record['weight'] = rset.weight record['region'] = rset.region record['failover'] = rset.failover record['health_check'] = rset.health_check record['hosted_zone_id'] = zone_id if rset.alias_dns_name: record['alias'] = True record['value'] = rset.alias_dns_name record['values'] = [rset.alias_dns_name] record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id record[ 'alias_evaluate_target_health'] = rset.alias_evaluate_target_health else: record['alias'] = False record['value'] = ','.join(sorted(rset.resource_records)) record['values'] = sorted(rset.resource_records) if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml( ): module.exit_json(changed=False) # We need to look only at the first rrset returned by the above call, # so break here. The returned elements begin with the one matching our # requested name, type, and identifier, if such an element exists, # followed by all others that come after it in alphabetical order. # Therefore, if the first set does not match, no subsequent set will # match either. break if command_in == 'get': if type_in == 'NS': ns = record.get('values', []) else: # Retrieve name servers associated to the zone. z = invoke_with_throttling_retries(conn.get_zone, zone_in) ns = invoke_with_throttling_retries(z.get_nameservers) module.exit_json(changed=False, set=record, nameservers=ns) if command_in == 'delete' and not found_record: module.exit_json(changed=False) changes = ResourceRecordSets(conn, zone_id) if command_in == 'create' or command_in == 'delete': if command_in == 'create' and found_record: if not module.params['overwrite']: module.fail_json( msg= "Record already exists with different value. Set 'overwrite' to replace it" ) command = 'UPSERT' else: command = command_in.upper() # Restore original order of records wanted_rset.resource_records = unsorted_records changes.add_change_record(command, wanted_rset) if not module.check_mode: try: invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in) except boto.route53.exception.DNSServerError as e: txt = e.body.split("<Message>")[1] txt = txt.split("</Message>")[0] if "but it already exists" in txt: module.exit_json(changed=False) else: module.fail_json(msg=txt) except TimeoutError: module.fail_json(msg='Timeout waiting for changes to replicate') module.exit_json(changed=True)
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( zone=dict(required=True), state=dict(default='present', choices=['present', 'absent']), vpc_id=dict(default=None), vpc_region=dict(default=None), comment=dict(default=''))) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') zone_in = module.params.get('zone').lower() state = module.params.get('state').lower() vpc_id = module.params.get('vpc_id') vpc_region = module.params.get('vpc_region') comment = module.params.get('comment') if zone_in[-1:] != '.': zone_in += "." private_zone = vpc_id is not None and vpc_region is not None _, _, aws_connect_kwargs = get_aws_connection_info(module) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError as e: module.fail_json(msg=e.error_message) results = conn.get_all_hosted_zones() zones = {} for r53zone in results['ListHostedZonesResponse']['HostedZones']: zone_id = r53zone['Id'].replace('/hostedzone/', '') zone_details = conn.get_hosted_zone(zone_id)['GetHostedZoneResponse'] if vpc_id and 'VPCs' in zone_details: # this is to deal with this boto bug: https://github.com/boto/boto/pull/2882 if isinstance(zone_details['VPCs'], dict): if zone_details['VPCs']['VPC']['VPCId'] == vpc_id: zones[r53zone['Name']] = zone_id else: # Forward compatibility for when boto fixes that bug if vpc_id in [v['VPCId'] for v in zone_details['VPCs']]: zones[r53zone['Name']] = zone_id else: zones[r53zone['Name']] = zone_id record = { 'private_zone': private_zone, 'vpc_id': vpc_id, 'vpc_region': vpc_region, 'comment': comment, } if state == 'present' and zone_in in zones: if private_zone: details = conn.get_hosted_zone(zones[zone_in]) if 'VPCs' not in details['GetHostedZoneResponse']: module.fail_json( msg="Can't change VPC from public to private" ) vpc_details = details['GetHostedZoneResponse']['VPCs']['VPC'] current_vpc_id = vpc_details['VPCId'] current_vpc_region = vpc_details['VPCRegion'] if current_vpc_id != vpc_id: module.fail_json( msg="Can't change VPC ID once a zone has been created" ) if current_vpc_region != vpc_region: module.fail_json( msg="Can't change VPC Region once a zone has been created" ) record['zone_id'] = zones[zone_in] record['name'] = zone_in module.exit_json(changed=False, set=record) elif state == 'present': result = conn.create_hosted_zone(zone_in, **record) hosted_zone = result['CreateHostedZoneResponse']['HostedZone'] zone_id = hosted_zone['Id'].replace('/hostedzone/', '') record['zone_id'] = zone_id record['name'] = zone_in module.exit_json(changed=True, set=record) elif state == 'absent' and zone_in in zones: conn.delete_hosted_zone(zones[zone_in]) module.exit_json(changed=True) elif state == 'absent': module.exit_json(changed=False)
def get_connection(): from route53 import app return Route53Connection( aws_access_key_id=app.config['AWS_ACCESS_KEY_ID'], aws_secret_access_key=app.config['AWS_SECRET_ACCESS_KEY'])
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( command=dict(choices=['get', 'create', 'delete'], required=True), zone=dict(required=True), hosted_zone_id=dict(required=False, default=None), record=dict(required=True), ttl=dict(required=False, type='int', default=3600), type=dict(choices=[ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'SOA' ], required=True), alias=dict(required=False, type='bool'), alias_hosted_zone_id=dict(required=False), alias_evaluate_target_health=dict(required=False, type='bool', default=False), value=dict(required=False), overwrite=dict(required=False, type='bool'), retry_interval=dict(required=False, default=500), private_zone=dict(required=False, type='bool', default=False), identifier=dict(required=False, default=None), weight=dict(required=False, type='int'), region=dict(required=False), health_check=dict(required=False), failover=dict(required=False, choices=['PRIMARY', 'SECONDARY']), vpc_id=dict(required=False), wait=dict(required=False, type='bool', default=False), wait_timeout=dict(required=False, type='int', default=300), )) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') if distutils.version.StrictVersion( boto.__version__) < distutils.version.StrictVersion( MINIMUM_BOTO_VERSION): module.fail_json( msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION)) command_in = module.params.get('command') zone_in = module.params.get('zone').lower() hosted_zone_id_in = module.params.get('hosted_zone_id') ttl_in = module.params.get('ttl') record_in = module.params.get('record').lower() type_in = module.params.get('type') value_in = module.params.get('value') alias_in = module.params.get('alias') alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id') alias_evaluate_target_health_in = module.params.get( 'alias_evaluate_target_health') retry_interval_in = module.params.get('retry_interval') private_zone_in = module.params.get('private_zone') identifier_in = module.params.get('identifier') weight_in = module.params.get('weight') region_in = module.params.get('region') health_check_in = module.params.get('health_check') failover_in = module.params.get('failover') vpc_id_in = module.params.get('vpc_id') wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) value_list = () if type(value_in) is str: if value_in: value_list = sorted([s.strip() for s in value_in.split(',')]) elif type(value_in) is list: value_list = sorted(value_in) if zone_in[-1:] != '.': zone_in += "." if record_in[-1:] != '.': record_in += "." if command_in == 'create' or command_in == 'delete': if not value_in: module.fail_json( msg="parameter 'value' required for create/delete") elif alias_in: if len(value_list) != 1: module.fail_json( msg= "parameter 'value' must contain a single dns name for alias create/delete" ) elif not alias_hosted_zone_id_in: module.fail_json( msg= "parameter 'alias_hosted_zone_id' required for alias create/delete" ) elif (weight_in != None or region_in != None or failover_in != None) and identifier_in == None: module.fail_json( msg= "If you specify failover, region or weight you must also specify identifier" ) if command_in == 'create': if (weight_in != None or region_in != None or failover_in != None) and identifier_in == None: module.fail_json( msg= "If you specify failover, region or weight you must also specify identifier" ) elif (weight_in == None and region_in == None and failover_in == None) and identifier_in != None: module.fail_json( msg= "You have specified identifier which makes sense only if you specify one of: weight, region or failover." ) if vpc_id_in and not private_zone_in: module.fail_json( msg="parameter 'private_zone' must be true when specifying parameter" " 'vpc_id'") # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError, e: module.fail_json(msg=e.error_message)
def main(): parser = argparse.ArgumentParser(description='Push/pull Amazon Route 53 configs.') parser.add_argument('--push', metavar='file_to_push.xml', help="Push the config in this file to R53.") parser.add_argument('--pull', action='store_true', help="Dump current R53 config to stdout.") parser.add_argument('--confirm', action='store_true', help="Do not prompt before push.") parser.add_argument('--dryrun', action='store_true', help="Do not actually apply changes.") parser.add_argument('--verbose', action='store_true') parser.add_argument('--zone', required=True, metavar="foursquare.com", help="Zone to push/pull.") args = parser.parse_args() ch = logging.StreamHandler() if args.verbose: ch.setLevel(logging.DEBUG) else: ch.setLevel(logging.INFO) log.addHandler(ch) if args.push == args.pull: print "You must specify either --push or --pull." sys.exit(1) # confirm wants stdin to itself if args.push == '-': args.confirm = True conn = Route53Connection() log.info('looking up zone for %s' % args.zone) zone_id = lookup_zone(conn, args.zone) log.info('fetching live config for zone %s' % zone_id) live_config = merge_config(fetch_config(zone_id, conn)) if args.pull: print lxml.etree.tostring(live_config, pretty_print=True) if args.push: if args.push == '-': args.push = sys.stdin new_config = lxml.etree.parse(args.push) normalize_xml(live_config) normalize_xml(new_config.getroot()) changeset = generate_changeset(live_config, new_config.getroot()) if changeset is None: print "No changes found; exiting" sys.exit(0) changesetstr = lxml.etree.tostring(changeset, pretty_print=True) print "==CHANGESET==" print changesetstr errs = validate_changeset(changeset) if len(errs) > 0: print "changeset invalid. errors:" print '\n'.join(errs) print "exiting" sys.exit(1) if not args.confirm: ans = raw_input("Push y/N? ") if ans not in ['y', 'Y']: print "Confirmation failed; exiting" sys.exit(0) if args.dryrun: print "Dry run mode: exiting without applying changes" sys.exit(0) else: conn.change_rrsets(zone_id, changesetstr)
def main(): argument_spec = ec2_argument_spec() argument_spec.update( dict( command=dict(choices=['get', 'create', 'delete'], required=True), zone=dict(required=True), record=dict(required=True), ttl=dict(required=False, type='int', default=3600), type=dict(choices=[ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS' ], required=True), alias=dict(required=False, type='bool'), alias_hosted_zone_id=dict(required=False), value=dict(required=False), overwrite=dict(required=False, type='bool'), retry_interval=dict(required=False, default=500), private_zone=dict(required=False, type='bool', default=False), identifier=dict(required=False), weight=dict(required=False, type='int'), region=dict(required=False), health_check=dict(required=False), failover=dict(required=False), )) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') command_in = module.params.get('command') zone_in = module.params.get('zone').lower() ttl_in = module.params.get('ttl') record_in = module.params.get('record').lower() type_in = module.params.get('type') value_in = module.params.get('value') alias_in = module.params.get('alias') alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id') retry_interval_in = module.params.get('retry_interval') private_zone_in = module.params.get('private_zone') identifier_in = module.params.get('identifier') weight_in = module.params.get('weight') region_in = module.params.get('region') health_check_in = module.params.get('health_check') failover_in = module.params.get('failover') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) value_list = () if type(value_in) is str: if value_in: value_list = sorted([s.strip() for s in value_in.split(',')]) elif type(value_in) is list: value_list = sorted(value_in) if zone_in[-1:] != '.': zone_in += "." if record_in[-1:] != '.': record_in += "." if command_in == 'create' or command_in == 'delete': if not value_in: module.fail_json( msg="parameter 'value' required for create/delete") elif alias_in: if len(value_list) != 1: module.fail_json( msg= "parameter 'value' must contain a single dns name for alias create/delete" ) elif not alias_hosted_zone_id_in: module.fail_json( msg= "parameter 'alias_hosted_zone_id' required for alias create/delete" ) # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError, e: module.fail_json(msg=e.error_message)
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( command = dict(choices=['get', 'create', 'delete'], required=True), zone = dict(required=True), hosted_zone_id = dict(required=False, default=None), record = dict(required=True), ttl = dict(required=False, type='int', default=3600), type = dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'SOA'], required=True), alias = dict(required=False, type='bool'), alias_hosted_zone_id = dict(required=False), alias_evaluate_target_health = dict(required=False, type='bool', default=False), value = dict(required=False), overwrite = dict(required=False, type='bool'), retry_interval = dict(required=False, default=500), private_zone = dict(required=False, type='bool', default=False), identifier = dict(required=False, default=None), weight = dict(required=False, type='int'), region = dict(required=False), health_check = dict(required=False), failover = dict(required=False,choices=['PRIMARY','SECONDARY']), vpc_id = dict(required=False), wait = dict(required=False, type='bool', default=False), wait_timeout = dict(required=False, type='int', default=300), ) ) module = AnsibleModule(argument_spec=argument_spec) if not HAS_BOTO: module.fail_json(msg='boto required for this module') if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION): module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION)) command_in = module.params.get('command') zone_in = module.params.get('zone').lower() hosted_zone_id_in = module.params.get('hosted_zone_id') ttl_in = module.params.get('ttl') record_in = module.params.get('record').lower() type_in = module.params.get('type') value_in = module.params.get('value') alias_in = module.params.get('alias') alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id') alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health') retry_interval_in = module.params.get('retry_interval') private_zone_in = module.params.get('private_zone') identifier_in = module.params.get('identifier') weight_in = module.params.get('weight') region_in = module.params.get('region') health_check_in = module.params.get('health_check') failover_in = module.params.get('failover') vpc_id_in = module.params.get('vpc_id') wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) value_list = () if isinstance(value_in, str): if value_in: value_list = sorted([s.strip() for s in value_in.split(',')]) elif isinstance(value_in, list): value_list = sorted(value_in) if zone_in[-1:] != '.': zone_in += "." if record_in[-1:] != '.': record_in += "." if command_in == 'create' or command_in == 'delete': if not value_in: module.fail_json(msg = "parameter 'value' required for create/delete") elif alias_in: if len(value_list) != 1: module.fail_json(msg = "parameter 'value' must contain a single dns name for alias create/delete") elif not alias_hosted_zone_id_in: module.fail_json(msg = "parameter 'alias_hosted_zone_id' required for alias create/delete") elif ( weight_in is not None or region_in is not None or failover_in is not None ) and identifier_in is None: module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier") if command_in == 'create': if ( weight_in is not None or region_in is not None or failover_in is not None ) and identifier_in is None: module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier") elif ( weight_in is None and region_in is None and failover_in is None ) and identifier_in is not None: module.fail_json(msg= "You have specified identifier which makes sense only if you specify one of: weight, region or failover.") if vpc_id_in and not private_zone_in: module.fail_json(msg="parameter 'private_zone' must be true when specifying parameter" " 'vpc_id'") # connect to the route53 endpoint try: conn = Route53Connection(**aws_connect_kwargs) except boto.exception.BotoServerError as e: module.fail_json(msg = e.error_message) # Find the named zone ID zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in) # Verify that the requested zone is already defined in Route53 if zone is None: errmsg = "Zone %s does not exist in Route53" % zone_in module.fail_json(msg = errmsg) record = {} found_record = False wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in, identifier=identifier_in, weight=weight_in, region=region_in, health_check=health_check_in, failover=failover_in) for v in value_list: if alias_in: wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in) else: wanted_rset.add_value(v) sets = conn.get_all_rrsets(zone.id, name=record_in, type=type_in, identifier=identifier_in) for rset in sets: # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round # tripping of things like * and @. decoded_name = rset.name.replace(r'\052', '*') decoded_name = decoded_name.replace(r'\100', '@') #Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block rset.name = decoded_name if identifier_in is not None: identifier_in = str(identifier_in) if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in: found_record = True record['zone'] = zone_in record['type'] = rset.type record['record'] = decoded_name record['ttl'] = rset.ttl record['value'] = ','.join(sorted(rset.resource_records)) record['values'] = sorted(rset.resource_records) if hosted_zone_id_in: record['hosted_zone_id'] = hosted_zone_id_in record['identifier'] = rset.identifier record['weight'] = rset.weight record['region'] = rset.region record['failover'] = rset.failover record['health_check'] = rset.health_check if hosted_zone_id_in: record['hosted_zone_id'] = hosted_zone_id_in if rset.alias_dns_name: record['alias'] = True record['value'] = rset.alias_dns_name record['values'] = [rset.alias_dns_name] record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health else: record['alias'] = False record['value'] = ','.join(sorted(rset.resource_records)) record['values'] = sorted(rset.resource_records) if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml(): module.exit_json(changed=False) break if command_in == 'get': if type_in == 'NS': ns = record['values'] else: # Retrieve name servers associated to the zone. ns = conn.get_zone(zone_in).get_nameservers() module.exit_json(changed=False, set=record, nameservers=ns) if command_in == 'delete' and not found_record: module.exit_json(changed=False) changes = ResourceRecordSets(conn, zone.id) if command_in == 'create' or command_in == 'delete': if command_in == 'create' and found_record: if not module.params['overwrite']: module.fail_json(msg = "Record already exists with different value. Set 'overwrite' to replace it") command = 'UPSERT' else: command = command_in.upper() changes.add_change_record(command, wanted_rset) try: result = invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in) except boto.route53.exception.DNSServerError as e: txt = e.body.split("<Message>")[1] txt = txt.split("</Message>")[0] if "but it already exists" in txt: module.exit_json(changed=False) else: module.fail_json(msg = txt) except TimeoutError: module.fail_json(msg='Timeout waiting for changes to replicate') module.exit_json(changed=True)
def __init__(self): self.route53 = Route53Connection()