def ensure_regex_pattern_present(self, regex_pattern): name = regex_pattern['name'] pattern_set = self.get_regex_pattern_by_name(name) if not pattern_set: pattern_set = self.client.create_regex_pattern_set( Name=name, ChangeToken=get_change_token(self.client, self.module))['RegexPatternSet'] missing = set(regex_pattern['regex_strings']) - set( pattern_set['RegexPatternStrings']) extra = set(pattern_set['RegexPatternStrings']) - set( regex_pattern['regex_strings']) if not missing and not extra: return pattern_set updates = [{ 'Action': 'INSERT', 'RegexPatternString': pattern } for pattern in missing] updates.extend([{ 'Action': 'DELETE', 'RegexPatternString': pattern } for pattern in extra]) self.client.update_regex_pattern_set( RegexPatternSetId=pattern_set['RegexPatternSetId'], Updates=updates, ChangeToken=get_change_token(self.client, self.module)) return self.get_regex_pattern_set_with_backoff( pattern_set['RegexPatternSetId'])['RegexPatternSet']
def ensure_web_acl_present(client, module): changed = False result = None name = module.params['name'] web_acl_id = get_web_acl_by_name(client, module, name) if web_acl_id: (changed, result) = find_and_update_web_acl(client, module, web_acl_id) else: metric_name = module.params['metric_name'] if not metric_name: metric_name = re.sub(r'[^A-Za-z0-9]', '', module.params['name']) default_action = module.params['default_action'].upper() try: new_web_acl = client.create_web_acl( Name=name, MetricName=metric_name, DefaultAction={'Type': default_action}, ChangeToken=get_change_token(client, module)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not create Web ACL') (changed, result) = find_and_update_web_acl(client, module, new_web_acl['WebACL']['WebACLId']) return changed, result
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) if changed: try: client.update_web_acl(WebACLId=acl['WebACLId'], ChangeToken=get_change_token(client, module), Updates=insertions + deletions, DefaultAction=acl['DefaultAction']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not update Web ACL') acl = get_web_acl(client, module, web_acl_id) return changed, acl
def find_and_delete_condition(self, condition_set_id): current_condition = self.get_condition_by_id(condition_set_id) in_use_rules = self.find_condition_in_rules(condition_set_id) if in_use_rules: rulenames = ', '.join(in_use_rules) self.module.fail_json(msg="Condition %s is in use by %s" % (current_condition['Name'], rulenames)) if current_condition[self.conditiontuples]: # Filters are deleted using update with the DELETE action func = getattr(self.client, 'update_' + self.method_suffix) params = self.format_for_deletion(current_condition) try: func(**params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws( e, msg='Could not delete filters from condition') func = getattr(self.client, 'delete_' + self.method_suffix) params = dict() params[self.conditionsetid] = condition_set_id params['ChangeToken'] = get_change_token(self.client, self.module) try: func(**params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg='Could not delete condition') # tidy up regex patterns if self.type == 'regex': self.tidy_up_regex_patterns(current_condition) return True, {}
def remove_rules_from_web_acl(client, module, web_acl_id): acl = get_web_acl(client, module, web_acl_id) deletions = [format_for_update(rule, 'DELETE') for rule in acl['Rules']] try: client.update_web_acl(WebACLId=acl['WebACLId'], ChangeToken=get_change_token(client, module), Updates=deletions, DefaultAction=acl['DefaultAction']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not remove rule')
def format_for_deletion(self, condition): return { 'ChangeToken': get_change_token(self.client, self.module), 'Updates': [{ 'Action': 'DELETE', self.conditiontuple: current_condition_tuple } for current_condition_tuple in condition[self.conditiontuples]], self.conditionsetid: condition[self.conditionsetid] }
def delete_unused_regex_pattern(self, regex_pattern_set_id): try: regex_pattern_set = self.client.get_regex_pattern_set( RegexPatternSetId=regex_pattern_set_id)['RegexPatternSet'] updates = list() for regex_pattern_string in regex_pattern_set[ 'RegexPatternStrings']: updates.append({ 'Action': 'DELETE', 'RegexPatternString': regex_pattern_string }) self.client.update_regex_pattern_set( RegexPatternSetId=regex_pattern_set_id, Updates=updates, ChangeToken=get_change_token(self.client, self.module)) self.client.delete_regex_pattern_set( RegexPatternSetId=regex_pattern_set_id, ChangeToken=get_change_token(self.client, self.module)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg='Could not delete regex pattern')
def remove_rule_conditions(client, module, rule_id): conditions = get_rule(client, module, rule_id)['Predicates'] updates = [ format_for_deletion(camel_dict_to_snake_dict(condition)) for condition in conditions ] try: client.update_rule(RuleId=rule_id, ChangeToken=get_change_token(client, module), Updates=updates) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not remove rule conditions')
def ensure_web_acl_absent(client, module): web_acl_id = get_web_acl_by_name(client, module, module.params['name']) if web_acl_id: web_acl = get_web_acl(client, module, web_acl_id) if web_acl['Rules']: remove_rules_from_web_acl(client, module, web_acl_id) try: client.delete_web_acl(WebACLId=web_acl_id, ChangeToken=get_change_token(client, module)) return True, {} except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not delete Web ACL') return False, {}
def ensure_condition_present(self): name = self.module.params['name'] condition_set_id = self.get_condition_by_name(name) if condition_set_id: return self.find_and_update_condition(condition_set_id) else: params = dict() params['Name'] = name params['ChangeToken'] = get_change_token(self.client, self.module) func = getattr(self.client, 'create_' + self.method_suffix) try: condition = func(**params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg='Could not create condition') return self.find_and_update_condition( condition[self.conditionset][self.conditionsetid])
def ensure_rule_absent(client, module): rule_id = get_rule_by_name(client, module, module.params['name']) in_use_web_acls = find_rule_in_web_acls(client, module, rule_id) if in_use_web_acls: web_acl_names = ', '.join(in_use_web_acls) module.fail_json(msg="Rule %s is in use by Web ACL(s) %s" % (module.params['name'], web_acl_names)) if rule_id: remove_rule_conditions(client, module, rule_id) try: return True, client.delete_rule(RuleId=rule_id, ChangeToken=get_change_token( client, module)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not delete rule') return False, {}
def ensure_rule_present(client, module): name = module.params['name'] rule_id = get_rule_by_name(client, module, name) params = dict() if rule_id: return find_and_update_rule(client, module, rule_id) else: params['Name'] = module.params['name'] metric_name = module.params['metric_name'] if not metric_name: metric_name = re.sub(r'[^a-zA-Z0-9]', '', module.params['name']) params['MetricName'] = metric_name params['ChangeToken'] = get_change_token(client, module) try: new_rule = client.create_rule(**params)['Rule'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not create rule') return find_and_update_rule(client, module, new_rule['RuleId'])
def format_for_update(self, condition_set_id): # Prep kwargs kwargs = dict() kwargs['Updates'] = list() for filtr in self.module.params.get('filters'): # Only for ip_set if self.type == 'ip': # there might be a better way of detecting an IPv6 address if ':' in filtr.get('ip_address'): ip_type = 'IPV6' else: ip_type = 'IPV4' condition_insert = { 'Type': ip_type, 'Value': filtr.get('ip_address') } # Specific for geo_match_set if self.type == 'geo': condition_insert = dict(Type='Country', Value=filtr.get('country')) # Common For everything but ip_set and geo_match_set if self.type not in ('ip', 'geo'): condition_insert = dict(FieldToMatch=dict( Type=filtr.get('field_to_match').upper()), TextTransformation=filtr.get( 'transformation', 'none').upper()) if filtr.get('field_to_match').upper() == "HEADER": if filtr.get('header'): condition_insert['FieldToMatch']['Data'] = filtr.get( 'header').lower() else: self.module.fail_json( msg=str("DATA required when HEADER requested")) # Specific for byte_match_set if self.type == 'byte': condition_insert['TargetString'] = filtr.get('target_string') condition_insert['PositionalConstraint'] = filtr.get( 'position') # Specific for size_constraint_set if self.type == 'size': condition_insert['ComparisonOperator'] = filtr.get( 'comparison') condition_insert['Size'] = filtr.get('size') # Specific for regex_match_set if self.type == 'regex': condition_insert[ 'RegexPatternSetId'] = self.ensure_regex_pattern_present( filtr.get('regex_pattern'))['RegexPatternSetId'] kwargs['Updates'].append({ 'Action': 'INSERT', self.conditiontuple: condition_insert }) kwargs[self.conditionsetid] = condition_set_id kwargs['ChangeToken'] = get_change_token(self.client, self.module) return kwargs
def find_and_update_rule(client, module, rule_id): rule = get_rule(client, module, rule_id) rule_id = rule['RuleId'] existing_conditions = dict( (condition_type, dict()) for condition_type in MATCH_LOOKUP) desired_conditions = dict( (condition_type, dict()) for condition_type in MATCH_LOOKUP) all_conditions = dict() for condition_type in MATCH_LOOKUP: method = 'list_' + MATCH_LOOKUP[condition_type]['method'] + 's' all_conditions[condition_type] = dict() try: paginator = client.get_paginator(method) func = paginator.paginate().build_full_result except (KeyError, botocore.exceptions.OperationNotPageableError): # list_geo_match_sets and list_regex_match_sets do not have a paginator # and throw different exceptions func = getattr(client, method) try: pred_results = func()[MATCH_LOOKUP[condition_type]['conditionset'] + 's'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not list %s conditions' % condition_type) for pred in pred_results: pred['DataId'] = pred[MATCH_LOOKUP[condition_type]['conditionset'] + 'Id'] all_conditions[condition_type][ pred['Name']] = camel_dict_to_snake_dict(pred) all_conditions[condition_type][ pred['DataId']] = camel_dict_to_snake_dict(pred) for condition in module.params['conditions']: desired_conditions[condition['type']][condition['name']] = condition reverse_condition_types = dict( (v['type'], k) for (k, v) in MATCH_LOOKUP.items()) for condition in rule['Predicates']: existing_conditions[reverse_condition_types[condition['Type']]][ condition['DataId']] = camel_dict_to_snake_dict(condition) insertions = list() deletions = list() for condition_type in desired_conditions: for (condition_name, condition) in desired_conditions[condition_type].items(): if condition_name not in all_conditions[condition_type]: module.fail_json(msg="Condition %s of type %s does not exist" % (condition_name, condition_type)) condition['data_id'] = all_conditions[condition_type][ condition_name]['data_id'] if condition['data_id'] not in existing_conditions[condition_type]: insertions.append(format_for_insertion(condition)) if module.params['purge_conditions']: for condition_type in existing_conditions: deletions.extend([ format_for_deletion(condition) for condition in existing_conditions[condition_type].values() if not all_conditions[condition_type][condition['data_id']] ['name'] in desired_conditions[condition_type] ]) changed = bool(insertions or deletions) if changed: try: client.update_rule(RuleId=rule_id, ChangeToken=get_change_token(client, module), Updates=insertions + deletions) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Could not update rule conditions') return changed, get_rule(client, module, rule_id)