Esempio n. 1
0
def find_and_update_web_acl(client, module, web_acl_id):
    acl = get_web_acl(client, module, web_acl_id)
    rule_lookup = create_rule_lookup(client, module)
    existing_rules = acl['Rules']
    desired_rules = [{
        'RuleId': rule_lookup[rule['name']]['RuleId'],
        'Priority': rule['priority'],
        'Action': {
            'Type': rule['action'].upper()
        },
        'Type': rule.get('type', 'regular').upper()
    } for rule in module.params['rules']]
    missing = [rule for rule in desired_rules if rule not in existing_rules]
    extras = []
    if module.params['purge_rules']:
        extras = [rule for rule in existing_rules if rule not in desired_rules]

    insertions = [format_for_update(rule, 'INSERT') for rule in missing]
    deletions = [format_for_update(rule, 'DELETE') for rule in extras]
    changed = bool(insertions + deletions)

    # Purge rules before adding new ones in case a deletion shares the same
    # priority as an insertion.
    params = {
        'WebACLId': acl['WebACLId'],
        'DefaultAction': acl['DefaultAction']
    }
    change_tokens = []
    if deletions:
        try:
            params['Updates'] = deletions
            result = run_func_with_change_token_backoff(
                client, module, params, client.update_web_acl)
            change_tokens.append(result['ChangeToken'])
            get_waiter(
                client,
                'change_token_in_sync',
            ).wait(ChangeToken=result['ChangeToken'])
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg='Could not update Web ACL')
    if insertions:
        try:
            params['Updates'] = insertions
            result = run_func_with_change_token_backoff(
                client, module, params, client.update_web_acl)
            change_tokens.append(result['ChangeToken'])
        except (botocore.exceptions.ClientError,
                botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg='Could not update Web ACL')
    if change_tokens:
        for token in change_tokens:
            get_waiter(
                client,
                'change_token_in_sync',
            ).wait(ChangeToken=token)
    if changed:
        acl = get_web_acl(client, module, web_acl_id)
    return changed, acl
Esempio n. 2
0
def handle_waiter(conn, module, waiter_name, params, start_time):
    try:
        get_waiter(conn, waiter_name).wait(
            **waiter_params(module, params, start_time)
        )
    except botocore.exceptions.WaiterError as e:
        module.fail_json_aws(e, "Failed to wait for updates to complete")
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, "An exception happened while trying to wait for updates")
Esempio n. 3
0
def handle_waiter(conn, module, waiter_name, params, start_time):
    try:
        get_waiter(conn, waiter_name).wait(
            **waiter_params(module, params, start_time)
        )
    except botocore.exceptions.WaiterError as e:
        module.fail_json_aws(e, "Failed to wait for updates to complete")
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, "An exception happened while trying to wait for updates")
Esempio n. 4
0
def run_func_with_change_token_backoff(client, module, params, func, wait=False):
    params['ChangeToken'] = get_change_token(client, module)
    result = func(**params)
    if wait:
        get_waiter(
            client, 'change_token_in_sync',
        ).wait(
            ChangeToken=result['ChangeToken']
        )
    return result
Esempio n. 5
0
def run_func_with_change_token_backoff(client, module, params, func, wait=False):
    params['ChangeToken'] = get_change_token(client, module)
    result = func(**params)
    if wait:
        get_waiter(
            client, 'change_token_in_sync',
        ).wait(
            ChangeToken=result['ChangeToken']
        )
    return result
Esempio n. 6
0
def find_and_update_web_acl(client, module, web_acl_id):
    acl = get_web_acl(client, module, web_acl_id)
    rule_lookup = create_rule_lookup(client, module)
    existing_rules = acl['Rules']
    desired_rules = [{'RuleId': rule_lookup[rule['name']]['RuleId'],
                      'Priority': rule['priority'],
                      'Action': {'Type': rule['action'].upper()},
                      'Type': rule.get('type', 'regular').upper()}
                     for rule in module.params['rules']]
    missing = [rule for rule in desired_rules if rule not in existing_rules]
    extras = []
    if module.params['purge_rules']:
        extras = [rule for rule in existing_rules if rule not in desired_rules]

    insertions = [format_for_update(rule, 'INSERT') for rule in missing]
    deletions = [format_for_update(rule, 'DELETE') for rule in extras]
    changed = bool(insertions + deletions)

    # Purge rules before adding new ones in case a deletion shares the same
    # priority as an insertion.
    params = {
        'WebACLId': acl['WebACLId'],
        'DefaultAction': acl['DefaultAction']
    }
    change_tokens = []
    if deletions:
        try:
            params['Updates'] = deletions
            result = run_func_with_change_token_backoff(client, module, params, client.update_web_acl)
            change_tokens.append(result['ChangeToken'])
            get_waiter(
                client, 'change_token_in_sync',
            ).wait(
                ChangeToken=result['ChangeToken']
            )
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg='Could not update Web ACL')
    if insertions:
        try:
            params['Updates'] = insertions
            result = run_func_with_change_token_backoff(client, module, params, client.update_web_acl)
            change_tokens.append(result['ChangeToken'])
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg='Could not update Web ACL')
    if change_tokens:
        for token in change_tokens:
            get_waiter(
                client, 'change_token_in_sync',
            ).wait(
                ChangeToken=token
            )
    if changed:
        acl = get_web_acl(client, module, web_acl_id)
    return changed, acl
Esempio n. 7
0
def wait_until_cluster_active(client, module):
    name = module.params.get('name')
    wait_timeout = module.params.get('wait_timeout')

    waiter = get_waiter(client, 'cluster_active')
    attempts = 1 + int(wait_timeout / waiter.config.delay)
    waiter.wait(name=name, WaiterConfig={'MaxAttempts': attempts})
Esempio n. 8
0
 def wait(client, db_instance_id, waiter_name, extra_retry_codes):
     retry = AWSRetry.jittered_backoff(catch_extra_error_codes=extra_retry_codes)
     try:
         waiter = client.get_waiter(waiter_name)
     except ValueError:
         # using a waiter in ansible.module_utils.aws.waiters
         waiter = get_waiter(client, waiter_name)
     waiter.wait(WaiterConfig={'Delay': 60, 'MaxAttempts': 60}, DBInstanceIdentifier=db_instance_id)
Esempio n. 9
0
def create_vgw(client, module):
    params = dict()
    params['Type'] = module.params.get('type')

    try:
        response = client.create_vpn_gateway(Type=params['Type'])
        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 botocore.exceptions.ClientError as e:
        module.fail_json(msg=to_native(e), exception=traceback.format_exc())

    result = response
    return result
Esempio n. 10
0
def create_vgw(client, module):
    params = dict()
    params['Type'] = module.params.get('type')

    try:
        response = client.create_vpn_gateway(Type=params['Type'])
        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 botocore.exceptions.ClientError as e:
        module.fail_json(msg=to_native(e), exception=traceback.format_exc())

    result = response
    return result
Esempio n. 11
0
def wait_for_cluster_status(client, module, db_cluster_id, waiter_name):
    try:
        waiter = get_waiter(client, waiter_name).wait(DBClusterIdentifier=db_cluster_id)
    except WaiterError as e:
        if waiter_name == 'cluster_deleted':
            msg = "Failed to wait for DB cluster {0} to be deleted".format(db_cluster_id)
        else:
            msg = "Failed to wait for DB cluster {0} to be available".format(db_cluster_id)
        module.fail_json_aws(e, msg=msg)
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB cluster {0}".format(db_cluster_id))
Esempio n. 12
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
Esempio n. 13
0
def get_target_from_rule(module, client, rule, name, group, groups, vpc_id):
    """
    Returns tuple of (target_type, target, group_created) after validating rule params.

    rule: Dict describing a rule.
    name: Name of the security group being managed.
    groups: Dict of all available security groups.

    AWS accepts an ip range or a security group as target of a rule. This
    function validate the rule specification and return either a non-None
    group_id or a non-None ip range.
    """
    FOREIGN_SECURITY_GROUP_REGEX = r'^([^/]+)/?(sg-\S+)?/(\S+)'
    group_id = None
    group_name = None
    target_group_created = False

    validate_rule(module, rule)
    if rule.get('group_id') and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
        # this is a foreign Security Group. Since you can't fetch it you must create an instance of it
        owner_id, group_id, group_name = re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']).groups()
        group_instance = dict(UserId=owner_id, GroupId=group_id, GroupName=group_name)
        groups[group_id] = group_instance
        groups[group_name] = group_instance
        # group_id/group_name are mutually exclusive - give group_id more precedence as it is more specific
        if group_id and group_name:
            group_name = None
        return 'group', (owner_id, group_id, group_name), False
    elif 'group_id' in rule:
        return 'group', rule['group_id'], False
    elif 'group_name' in rule:
        group_name = rule['group_name']
        if group_name == name:
            group_id = group['GroupId']
            groups[group_id] = group
            groups[group_name] = group
        elif group_name in groups and group.get('VpcId') and groups[group_name].get('VpcId'):
            # both are VPC groups, this is ok
            group_id = groups[group_name]['GroupId']
        elif group_name in groups and not (group.get('VpcId') or groups[group_name].get('VpcId')):
            # both are EC2 classic, this is ok
            group_id = groups[group_name]['GroupId']
        else:
            auto_group = None
            filters = {'group-name': group_name}
            if vpc_id:
                filters['vpc-id'] = vpc_id
            # if we got here, either the target group does not exist, or there
            # is a mix of EC2 classic + VPC groups. Mixing of EC2 classic + VPC
            # is bad, so we have to create a new SG because no compatible group
            # exists
            if not rule.get('group_desc', '').strip():
                # retry describing the group once
                try:
                    auto_group = get_security_groups_with_backoff(client, Filters=ansible_dict_to_boto3_filter_list(filters)).get('SecurityGroups', [])[0]
                except (is_boto3_error_code('InvalidGroup.NotFound'), IndexError):
                    module.fail_json(msg="group %s will be automatically created by rule %s but "
                                         "no description was provided" % (group_name, rule))
                except ClientError as e:  # pylint: disable=duplicate-except
                    module.fail_json_aws(e)
            elif not module.check_mode:
                params = dict(GroupName=group_name, Description=rule['group_desc'])
                if vpc_id:
                    params['VpcId'] = vpc_id
                try:
                    auto_group = client.create_security_group(**params)
                    get_waiter(
                        client, 'security_group_exists',
                    ).wait(
                        GroupIds=[auto_group['GroupId']],
                    )
                except is_boto3_error_code('InvalidGroup.Duplicate'):
                    # The group exists, but didn't show up in any of our describe-security-groups calls
                    # Try searching on a filter for the name, and allow a retry window for AWS to update
                    # the model on their end.
                    try:
                        auto_group = get_security_groups_with_backoff(client, Filters=ansible_dict_to_boto3_filter_list(filters)).get('SecurityGroups', [])[0]
                    except IndexError as e:
                        module.fail_json(msg="Could not create or use existing group '{0}' in rule. Make sure the group exists".format(group_name))
                    except ClientError as e:
                        module.fail_json_aws(
                            e,
                            msg="Could not create or use existing group '{0}' in rule. Make sure the group exists".format(group_name))
            if auto_group is not None:
                group_id = auto_group['GroupId']
                groups[group_id] = auto_group
                groups[group_name] = auto_group
            target_group_created = True
        return 'group', group_id, target_group_created
    elif 'cidr_ip' in rule:
        return 'ipv4', validate_ip(module, rule['cidr_ip']), False
    elif 'cidr_ipv6' in rule:
        return 'ipv6', validate_ip(module, rule['cidr_ipv6']), False
    elif 'ip_prefix' in rule:
        return 'ip_prefix', rule['ip_prefix'], False

    module.fail_json(msg="Could not match target for rule {0}".format(rule), failed_rule=rule)
Esempio n. 14
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
Esempio n. 15
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))
Esempio n. 16
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))