def create_vgw(client, module): params = dict() params['Type'] = module.params.get('type') if module.params.get('asn'): params['AmazonSideAsn'] = module.params.get('asn') try: response = client.create_vpn_gateway(**params) get_waiter(client, 'vpn_gateway_exists').wait( VpnGatewayIds=[response['VpnGateway']['VpnGatewayId']]) except botocore.exceptions.WaiterError as e: module.fail_json( msg="Failed to wait for Vpn Gateway {0} to be available".format( response['VpnGateway']['VpnGatewayId']), exception=traceback.format_exc()) except is_boto3_error_code('VpnGatewayLimitExceeded'): module.fail_json(msg="Too many VPN gateways exist in this account.", exception=traceback.format_exc()) except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json(msg=to_native(e), exception=traceback.format_exc()) result = response return result
def wait_for_status(client, module, waiter_name, nat_gateway_id): wait_timeout = module.params.get('wait_timeout') try: waiter = get_waiter(client, waiter_name) attempts = 1 + int(wait_timeout / waiter.config.delay) waiter.wait(NatGatewayIds=[nat_gateway_id], WaiterConfig={'MaxAttempts': attempts}) except botocore.exceptions.WaiterError as e: module.fail_json_aws(e, msg="NAT gateway failed to reach expected state.") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Unable to wait for NAT gateway state to update.")
def create_vgw(client, module): params = dict() params['Type'] = module.params.get('type') if module.params.get('asn'): params['AmazonSideAsn'] = module.params.get('asn') try: response = client.create_vpn_gateway(aws_retry=True, **params) get_waiter(client, 'vpn_gateway_exists').wait( VpnGatewayIds=[response['VpnGateway']['VpnGatewayId']]) except botocore.exceptions.WaiterError as e: module.fail_json_aws( e, msg="Failed to wait for Vpn Gateway {0} to be available".format( response['VpnGateway']['VpnGatewayId'])) except is_boto3_error_code('VpnGatewayLimitExceeded') as e: module.fail_json_aws( e, msg="Too many VPN gateways exist in this account.") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failed to create gateway') result = response return result
def ensure_igw_present(self, vpc_id, tags, purge_tags): igw = self.get_matching_igw(vpc_id) if igw is None: if self._check_mode: self._results['changed'] = True self._results['gateway_id'] = None return self._results try: response = self._connection.create_internet_gateway( aws_retry=True) # Ensure the gateway exists before trying to attach it or add tags waiter = get_waiter(self._connection, 'internet_gateway_exists') waiter.wait(InternetGatewayIds=[ response['InternetGateway']['InternetGatewayId'] ]) igw = camel_dict_to_snake_dict(response['InternetGateway']) self._connection.attach_internet_gateway( aws_retry=True, InternetGatewayId=igw['internet_gateway_id'], VpcId=vpc_id) self._results['changed'] = True except botocore.exceptions.WaiterError as e: self._module.fail_json_aws(e, msg="No Internet Gateway exists.") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self._module.fail_json_aws( e, msg='Unable to create Internet Gateway') igw['vpc_id'] = vpc_id igw['tags'] = self.ensure_tags(igw_id=igw['internet_gateway_id'], tags=tags, purge_tags=purge_tags) igw_info = self.get_igw_info(igw) self._results.update(igw_info) return self._results
def main(): argument_spec = dict( client=dict(required=True, type='str'), waiter_name=dict(required=True, type='str'), with_decorator=dict(required=False, type='bool', default=False), ) module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, ) decorator = None if module.params.get('with_decorator'): decorator = AWSRetry.jittered_backoff() client = module.client(module.params.get('client'), retry_decorator=decorator) waiter = get_waiter(client, module.params.get('waiter_name')) module.exit_json(changed=False, waiter_attributes=dir(waiter))
def ensure_route_table_present(connection, module): lookup = module.params.get('lookup') propagating_vgw_ids = module.params.get('propagating_vgw_ids') purge_routes = module.params.get('purge_routes') purge_subnets = module.params.get('purge_subnets') purge_tags = module.params.get('purge_tags') route_table_id = module.params.get('route_table_id') subnets = module.params.get('subnets') tags = module.params.get('tags') vpc_id = module.params.get('vpc_id') routes = create_route_spec(connection, module, vpc_id) changed = False tags_valid = False if lookup == 'tag': if tags is not None: try: route_table = get_route_table_by_tags(connection, module, vpc_id, tags) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Error finding route table with lookup 'tag'") else: route_table = None elif lookup == 'id': try: route_table = get_route_table_by_id(connection, module, route_table_id) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Error finding route table with lookup 'id'") # If no route table returned then create new route table if route_table is None: changed = True if not module.check_mode: try: route_table = connection.create_route_table(VpcId=vpc_id)['RouteTable'] # try to wait for route table to be present before moving on get_waiter( connection, 'route_table_exists' ).wait( RouteTableIds=[route_table['RouteTableId']], ) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Error creating route table") else: route_table = {"id": "rtb-xxxxxxxx", "route_table_id": "rtb-xxxxxxxx", "vpc_id": vpc_id} module.exit_json(changed=changed, route_table=route_table) if routes is not None: result = ensure_routes(connection=connection, module=module, route_table=route_table, route_specs=routes, propagating_vgw_ids=propagating_vgw_ids, check_mode=module.check_mode, purge_routes=purge_routes) changed = changed or result['changed'] if propagating_vgw_ids is not None: result = ensure_propagation(connection=connection, module=module, route_table=route_table, propagating_vgw_ids=propagating_vgw_ids, check_mode=module.check_mode) changed = changed or result['changed'] if not tags_valid and tags is not None: result = ensure_tags(connection=connection, module=module, resource_id=route_table['RouteTableId'], tags=tags, purge_tags=purge_tags, check_mode=module.check_mode) route_table['Tags'] = result['tags'] changed = changed or result['changed'] if subnets is not None: associated_subnets = find_subnets(connection, module, vpc_id, subnets) result = ensure_subnet_associations(connection=connection, module=module, route_table=route_table, subnets=associated_subnets, check_mode=module.check_mode, purge_subnets=purge_subnets) changed = changed or result['changed'] if changed: # pause to allow route table routes/subnets/associations to be updated before exiting with final state sleep(5) module.exit_json(changed=changed, route_table=get_route_table_info(connection, module, route_table))
def main(): argument_spec = 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', elements='str'), 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 = AnsibleAWSModule( 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'), ('alias', 'ttl'), ], # failover, region and weight require identifier required_by=dict( failover=('identifier', ), region=('identifier', ), weight=('identifier', ), ), ) 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') 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." ) retry_decorator = AWSRetry.jittered_backoff( retries=MAX_AWS_RETRIES, delay=retry_interval_in, catch_extra_error_codes=['PriorRequestNotComplete'], max_delay=max(60, retry_interval_in), ) # connect to the route53 endpoint try: route53 = module.client('route53', retry_decorator=retry_decorator) except botocore.exceptions.HTTPClientError as e: module.fail_json_aws(e, msg='Failed to connect to AWS') # Find the named zone ID zone_id = hosted_zone_id_in or get_zone_id_by_name( route53, 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) aws_record = get_record(route53, zone_id, record_in, type_in, identifier_in) resource_record_set = scrub_none_parameters({ 'Name': record_in, 'Type': type_in, 'Weight': weight_in, 'Region': region_in, 'Failover': failover_in, 'TTL': ttl_in, 'ResourceRecords': [dict(Value=value) for value in value_in], 'HealthCheckId': health_check_in, }) if alias_in: resource_record_set['AliasTarget'] = dict( HostedZoneId=alias_hosted_zone_id_in, DNSName=value_in[0], EvaluateTargetHealth=alias_evaluate_target_health_in) if 'ResourceRecords' in resource_record_set: del resource_record_set['ResourceRecords'] if 'TTL' in resource_record_set: del resource_record_set['TTL'] # On CAA records order doesn't matter if type_in == 'CAA': resource_record_set['ResourceRecords'] = sorted( resource_record_set['ResourceRecords'], key=itemgetter('Value')) if aws_record: aws_record['ResourceRecords'] = sorted( aws_record['ResourceRecords'], key=itemgetter('Value')) if command_in == 'create' and aws_record == resource_record_set: rr_sets = [camel_dict_to_snake_dict(resource_record_set)] module.exit_json(changed=False, resource_records_sets=rr_sets) if command_in == 'get': if type_in == 'NS': ns = aws_record.get('values', []) else: # Retrieve name servers associated to the zone. ns = get_hosted_zone_nameservers(route53, zone_id) formatted_aws = format_record(aws_record, zone_in, zone_id) rr_sets = [camel_dict_to_snake_dict(aws_record)] module.exit_json(changed=False, set=formatted_aws, nameservers=ns, resource_record_sets=rr_sets) if command_in == 'delete' and not aws_record: module.exit_json(changed=False) if command_in == 'create' or command_in == 'delete': if command_in == 'create' and aws_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() if not module.check_mode: try: change_resource_record_sets = route53.change_resource_record_sets( aws_retry=True, HostedZoneId=zone_id, ChangeBatch=dict(Changes=[ dict(Action=command, ResourceRecordSet=resource_record_set) ])) if wait_in: waiter = get_waiter(route53, 'resource_record_sets_changed') waiter.wait(Id=change_resource_record_sets['ChangeInfo']['Id'], WaiterConfig=dict( Delay=WAIT_RETRY, MaxAttempts=wait_timeout_in // WAIT_RETRY, )) except is_boto3_error_message('but it already exists'): module.exit_json(changed=False) except botocore.exceptions.WaiterError as e: module.fail_json_aws( e, msg='Timeout waiting for resource records changes to be applied' ) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failed to update records') except Exception as e: module.fail_json(msg='Unhandled exception. (%s)' % to_native(e)) rr_sets = [camel_dict_to_snake_dict(resource_record_set)] formatted_aws = format_record(aws_record, zone_in, zone_id) formatted_record = format_record(resource_record_set, zone_in, zone_id) module.exit_json( changed=True, diff=dict( before=formatted_aws, after=formatted_record if command != 'delete' else {}, resource_record_sets=rr_sets, ), )
def ensure_vgw_present(client, module): # If an existing vgw name and type matches our args, then a match is considered to have been # found and we will not create another vgw. changed = False params = dict() result = dict() params['Name'] = module.params.get('name') params['VpcId'] = module.params.get('vpc_id') params['Type'] = module.params.get('type') params['Tags'] = module.params.get('tags') params['VpnGatewayIds'] = module.params.get('vpn_gateway_id') # check that the vpc_id exists. If not, an exception is thrown if params['VpcId']: vpc = find_vpc(client, module) # check if a gateway matching our module args already exists existing_vgw = find_vgw(client, module) if existing_vgw != []: vpn_gateway_id = existing_vgw[0]['VpnGatewayId'] vgw, changed = check_tags(client, module, existing_vgw, vpn_gateway_id) # if a vpc_id was provided, check if it exists and if it's attached if params['VpcId']: current_vpc_attachments = existing_vgw[0]['VpcAttachments'] if current_vpc_attachments != [] and current_vpc_attachments[0]['State'] == 'attached': if current_vpc_attachments[0]['VpcId'] != params['VpcId'] or current_vpc_attachments[0]['State'] != 'attached': # detach the existing vpc from the virtual gateway vpc_to_detach = current_vpc_attachments[0]['VpcId'] detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) get_waiter(client, 'vpn_gateway_detached').wait(VpnGatewayIds=[vpn_gateway_id]) attached_vgw = attach_vgw(client, module, vpn_gateway_id) changed = True else: # attach the vgw to the supplied vpc attached_vgw = attach_vgw(client, module, vpn_gateway_id) changed = True # if params['VpcId'] is not provided, check the vgw is attached to a vpc. if so, detach it. else: existing_vgw = find_vgw(client, module, [vpn_gateway_id]) if existing_vgw[0]['VpcAttachments'] != []: if existing_vgw[0]['VpcAttachments'][0]['State'] == 'attached': # detach the vpc from the vgw vpc_to_detach = existing_vgw[0]['VpcAttachments'][0]['VpcId'] detach_vgw(client, module, vpn_gateway_id, vpc_to_detach) changed = True else: # create a new vgw new_vgw = create_vgw(client, module) changed = True vpn_gateway_id = new_vgw['VpnGateway']['VpnGatewayId'] # tag the new virtual gateway create_tags(client, module, vpn_gateway_id) # if a vpc-id was supplied, attempt to attach it to the vgw if params['VpcId']: attached_vgw = attach_vgw(client, module, vpn_gateway_id) changed = True # return current state of the vgw vgw = find_vgw(client, module, [vpn_gateway_id]) result = get_vgw_info(vgw) return changed, result
def create_vpc_endpoint(client, module): params = dict() changed = False token_provided = False params['VpcId'] = module.params.get('vpc_id') params['VpcEndpointType'] = module.params.get('vpc_endpoint_type') params['ServiceName'] = module.params.get('service') if module.check_mode: changed = True result = 'Would have created VPC Endpoint if not in check mode' module.exit_json(changed=changed, result=result) if module.params.get('route_table_ids'): params['RouteTableIds'] = module.params.get('route_table_ids') if module.params.get('client_token'): token_provided = True request_time = datetime.datetime.utcnow() params['ClientToken'] = module.params.get('client_token') policy = None if module.params.get('policy'): try: policy = json.loads(module.params.get('policy')) except ValueError as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) elif module.params.get('policy_file'): try: with open(module.params.get('policy_file'), 'r') as json_data: policy = json.load(json_data) except Exception as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) if policy: params['PolicyDocument'] = json.dumps(policy) try: changed = True result = client.create_vpc_endpoint(aws_retry=True, **params)['VpcEndpoint'] if token_provided and ( request_time > result['creation_timestamp'].replace(tzinfo=None)): changed = False elif module.params.get('wait') and not module.check_mode: try: waiter = get_waiter(client, 'vpc_endpoint_exists') waiter.wait( VpcEndpointIds=[result['VpcEndpointId']], WaiterConfig=dict( Delay=15, MaxAttempts=module.params.get('wait_timeout') // 15)) except botocore.exceptions.WaiterError as e: module.fail_json_aws( msg= 'Error waiting for vpc endpoint to become available - please check the AWS console' ) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failure while waiting for status') except is_boto3_error_code('IdempotentParameterMismatch'): # pylint: disable=duplicate-except module.fail_json( msg= "IdempotentParameterMismatch - updates of endpoints are not allowed by the API" ) except is_boto3_error_code('RouteAlreadyExists'): # pylint: disable=duplicate-except module.fail_json( msg= "RouteAlreadyExists for one of the route tables - update is not allowed by the API" ) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to create VPC.") if module.params.get('tags'): ensure_tags(client, module, result['VpcEndpointId']) # describe and normalize iso datetime fields in result after adding tags normalized_result = get_endpoints( client, module, endpoint_id=result['VpcEndpointId'])['VpcEndpoints'][0] return changed, normalized_result