Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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.")
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
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))
Ejemplo n.º 6
0
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))
Ejemplo n.º 7
0
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,
        ),
    )
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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