diff --git a/changelogs/fragments/20240924-refactor-ec2_vpc_nacl-modules.yaml b/changelogs/fragments/20240924-refactor-ec2_vpc_nacl-modules.yaml new file mode 100644 index 00000000000..475d00cb9b9 --- /dev/null +++ b/changelogs/fragments/20240924-refactor-ec2_vpc_nacl-modules.yaml @@ -0,0 +1,4 @@ +--- +minor_changes: + - ec2_vpc_nacl_info - Refactor module to use shared code from `amazon.aws.plugins.module_utils.ec2` (https://github.com/ansible-collections/community.aws/pull/2159). + - ec2_vpc_nacl - Refactor module to use shared code from `amazon.aws.plugins.module_utils.ec2` (https://github.com/ansible-collections/community.aws/pull/2159). diff --git a/plugins/modules/ec2_vpc_nacl.py b/plugins/modules/ec2_vpc_nacl.py index cf109de1c8b..bc92003d17c 100644 --- a/plugins/modules/ec2_vpc_nacl.py +++ b/plugins/modules/ec2_vpc_nacl.py @@ -15,13 +15,15 @@ name: description: - Tagged name identifying a network ACL. - - One and only one of the I(name) or I(nacl_id) is required. + - One and only one of the O(name) or O(nacl_id) is required. + - Mutually exclusive with O(nacl_id). required: false type: str nacl_id: description: - NACL id identifying a network ACL. - - One and only one of the I(name) or I(nacl_id) is required. + - One and only one of the O(name) or O(nacl_id) is required. + - Mutually exclusive with O(name). required: false type: str vpc_id: @@ -142,25 +144,27 @@ """ RETURN = r""" -task: - description: The result of the create, or delete action. - returned: success - type: dict nacl_id: - description: The id of the NACL (when creating or updating an ACL) + description: The id of the NACL (when creating or updating an ACL). returned: success type: str - sample: acl-123456789abcdef01 + sample: "acl-123456789abcdef01" """ -try: - import botocore -except ImportError: - pass # Handled by AnsibleAWSModule - +from typing import Any +from typing import Dict +from typing import List +from typing import Optional + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleEC2Error +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import create_network_acl +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import create_network_acl_entry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import delete_network_acl +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import delete_network_acl_entry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_network_acls +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_subnets from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import replace_network_acl_association from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule @@ -170,70 +174,63 @@ # Utility methods -def icmp_present(entry): - if len(entry) == 6 and entry[1] in ["icmp", "ipv6-icmp"] or entry[1] in [1, 58]: - return True - - -def subnets_removed(nacl_id, subnets, client, module): - results = find_acl_by_id(nacl_id, client, module) - associations = results["NetworkAcls"][0]["Associations"] - subnet_ids = [assoc["SubnetId"] for assoc in associations] - return [subnet for subnet in subnet_ids if subnet not in subnets] +def icmp_present(entry: List[str]) -> bool: + return len(entry) == 6 and entry[1] in ["icmp", "ipv6-icmp"] or entry[1] in [1, 58] -def subnets_added(nacl_id, subnets, client, module): - results = find_acl_by_id(nacl_id, client, module) - associations = results["NetworkAcls"][0]["Associations"] - subnet_ids = [assoc["SubnetId"] for assoc in associations] - return [subnet for subnet in subnets if subnet not in subnet_ids] - - -def subnets_changed(nacl, client, module): +def subnets_changed(client, module: AnsibleAWSModule, nacl_id: str, subnets_ids: List[str]) -> bool: changed = False vpc_id = module.params.get("vpc_id") - nacl_id = nacl["NetworkAcls"][0]["NetworkAclId"] - subnets = subnets_to_associate(nacl, client, module) - if not subnets: - default_nacl_id = find_default_vpc_nacl(vpc_id, client, module)[0] - subnets = find_subnet_ids_by_nacl_id(nacl_id, client, module) - if subnets: - replace_network_acl_association(default_nacl_id, subnets, client, module) - changed = True - return changed - changed = False + + if not subnets_ids: + default_nacl_id = find_default_vpc_nacl(client, vpc_id) + # Find subnets by Network ACL ids + network_acls = describe_network_acls( + client, Filters=[{"Name": "association.network-acl-id", "Values": [nacl_id]}] + ) + subnets = [ + association["SubnetId"] + for nacl in network_acls + for association in nacl["Associations"] + if association["SubnetId"] + ] + changed = associate_nacl_to_subnets(client, module, default_nacl_id, subnets) return changed - subs_added = subnets_added(nacl_id, subnets, client, module) - if subs_added: - replace_network_acl_association(nacl_id, subs_added, client, module) - changed = True - subs_removed = subnets_removed(nacl_id, subnets, client, module) - if subs_removed: - default_nacl_id = find_default_vpc_nacl(vpc_id, client, module)[0] - replace_network_acl_association(default_nacl_id, subs_removed, client, module) - changed = True + + network_acls = describe_network_acls(client, NetworkAclIds=[nacl_id]) + current_subnets = [ + association["SubnetId"] + for nacl in network_acls + for association in nacl["Associations"] + if association["SubnetId"] + ] + subnets_added = [subnet for subnet in subnets_ids if subnet not in current_subnets] + subnets_removed = [subnet for subnet in current_subnets if subnet not in subnets_ids] + + if subnets_added: + changed |= associate_nacl_to_subnets(client, module, nacl_id, subnets_added) + if subnets_removed: + default_nacl_id = find_default_vpc_nacl(client, vpc_id) + changed |= associate_nacl_to_subnets(client, module, default_nacl_id, subnets_removed) + return changed -def nacls_changed(nacl, client, module): +def nacls_changed(client, module: AnsibleAWSModule, nacl_info: Dict[str, Any]) -> bool: changed = False - params = dict() - params["egress"] = module.params.get("egress") - params["ingress"] = module.params.get("ingress") - - nacl_id = nacl["NetworkAcls"][0]["NetworkAclId"] - nacl = describe_network_acl(client, module) - entries = nacl["NetworkAcls"][0]["Entries"] - egress = [rule for rule in entries if rule["Egress"] is True and rule["RuleNumber"] < 32767] - ingress = [rule for rule in entries if rule["Egress"] is False and rule["RuleNumber"] < 32767] - if rules_changed(egress, params["egress"], True, nacl_id, client, module): - changed = True - if rules_changed(ingress, params["ingress"], False, nacl_id, client, module): - changed = True + entries = nacl_info["Entries"] + nacl_id = nacl_info["NetworkAclId"] + aws_egress_rules = [rule for rule in entries if rule["Egress"] is True and rule["RuleNumber"] < 32767] + aws_ingress_rules = [rule for rule in entries if rule["Egress"] is False and rule["RuleNumber"] < 32767] + + # Egress Rules + changed |= rules_changed(client, nacl_id, module.params.get("egress"), aws_egress_rules, True, module.check_mode) + # Ingress Rules + changed |= rules_changed(client, nacl_id, module.params.get("ingress"), aws_ingress_rules, False, module.check_mode) return changed -def tags_changed(nacl_id, client, module): +def tags_changed(client, module: AnsibleAWSModule, nacl_id: str) -> bool: tags = module.params.get("tags") name = module.params.get("name") purge_tags = module.params.get("purge_tags") @@ -255,42 +252,84 @@ def tags_changed(nacl_id, client, module): ) -def rules_changed(aws_rules, param_rules, Egress, nacl_id, client, module): +def ansible_to_boto3_dict_rule(ansible_rule: List[Any], egress: bool) -> Dict[str, Any]: + boto3_rule = {} + if isinstance(ansible_rule, list): + boto3_rule["RuleNumber"] = ansible_rule[0] + boto3_rule["Protocol"] = str(PROTOCOL_NUMBERS[ansible_rule[1]]) + boto3_rule["RuleAction"] = ansible_rule[2] + boto3_rule["Egress"] = egress + if is_ipv6(ansible_rule[3]): + boto3_rule["Ipv6CidrBlock"] = ansible_rule[3] + else: + boto3_rule["CidrBlock"] = ansible_rule[3] + if icmp_present(ansible_rule): + boto3_rule["IcmpTypeCode"] = {"Type": int(ansible_rule[4]), "Code": int(ansible_rule[5])} + else: + if ansible_rule[6] or ansible_rule[7]: + boto3_rule["PortRange"] = {"From": ansible_rule[6], "To": ansible_rule[7]} + return boto3_rule + + +def find_added_rules(rules_a: List[Dict[str, Any]], rules_b: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + results = [] + # A rule is considered as a new rule if either the RuleNumber does exist in the list of + # current Rules stored in AWS or if the Rule differs with the Rule stored in AWS with the same RuleNumber + for a in rules_a: + if not any(a["RuleNumber"] == b["RuleNumber"] and a == b for b in rules_b): + results.append(a) + return results + + +def rules_changed( + client, + nacl_id: str, + ansible_rules: List[List[str]], + aws_rules: List[Dict[str, Any]], + egress: bool, + check_mode: bool, +) -> bool: + # transform rules: from ansible list to boto3 dict + ansible_rules = [ansible_to_boto3_dict_rule(r, egress) for r in ansible_rules] + + # find added rules + added_rules = find_added_rules(ansible_rules, aws_rules) + # find removed rules + removed_rules = find_added_rules(aws_rules, ansible_rules) + changed = False - rules = list() - for entry in param_rules: - rules.append(process_rule_entry(entry, Egress)) - if rules == aws_rules: - return changed - else: - removed_rules = [x for x in aws_rules if x not in rules] - if removed_rules: - params = dict() - for rule in removed_rules: - params["NetworkAclId"] = nacl_id - params["RuleNumber"] = rule["RuleNumber"] - params["Egress"] = Egress - delete_network_acl_entry(params, client, module) - changed = True - added_rules = [x for x in rules if x not in aws_rules] - if added_rules: - for rule in added_rules: - rule["NetworkAclId"] = nacl_id - create_network_acl_entry(rule, client, module) - changed = True + for rule in added_rules: + changed = True + if not check_mode: + rule_number = rule.pop("RuleNumber") + protocol = rule.pop("Protocol") + rule_action = rule.pop("RuleAction") + egress = rule.pop("Egress") + create_network_acl_entry( + client, + network_acl_id=nacl_id, + protocol=protocol, + egress=egress, + rule_action=rule_action, + rule_number=rule_number, + **rule, + ) + + # Removed Rules + for rule in removed_rules: + changed = True + if not check_mode: + delete_network_acl_entry(client, network_acl_id=nacl_id, rule_number=rule["RuleNumber"], egress=egress) + return changed -def is_ipv6(cidr): +def is_ipv6(cidr: str) -> bool: return ":" in cidr -def process_rule_entry(entry, Egress): - params = dict() - params["RuleNumber"] = entry[0] - params["Protocol"] = str(PROTOCOL_NUMBERS[entry[1]]) - params["RuleAction"] = entry[2] - params["Egress"] = Egress +def process_rule_entry(entry: List[Any]) -> Dict[str, Any]: + params = {} if is_ipv6(entry[3]): params["Ipv6CidrBlock"] = entry[3] else: @@ -300,275 +339,161 @@ def process_rule_entry(entry, Egress): else: if entry[6] or entry[7]: params["PortRange"] = {"From": entry[6], "To": entry[7]} - return params - -def restore_default_associations(assoc_ids, default_nacl_id, client, module): - if assoc_ids: - params = dict() - params["NetworkAclId"] = default_nacl_id[0] - for assoc_id in assoc_ids: - params["AssociationId"] = assoc_id - restore_default_acl_association(params, client, module) - return True - - -def construct_acl_entries(nacl, client, module): - for entry in module.params.get("ingress"): - params = process_rule_entry(entry, Egress=False) - params["NetworkAclId"] = nacl["NetworkAcl"]["NetworkAclId"] - create_network_acl_entry(params, client, module) - for rule in module.params.get("egress"): - params = process_rule_entry(rule, Egress=True) - params["NetworkAclId"] = nacl["NetworkAcl"]["NetworkAclId"] - create_network_acl_entry(params, client, module) + return params -# Module invocations -def setup_network_acl(client, module): +def add_network_acl_entries( + client, nacl_id: str, ansible_entries: List[List[str]], egress: bool, check_mode: bool +) -> bool: changed = False - nacl = describe_network_acl(client, module) - if not nacl["NetworkAcls"]: - tags = {} - if module.params.get("name"): - tags["Name"] = module.params.get("name") - tags.update(module.params.get("tags") or {}) - nacl = create_network_acl(module.params.get("vpc_id"), client, module, tags) - nacl_id = nacl["NetworkAcl"]["NetworkAclId"] - subnets = subnets_to_associate(nacl, client, module) - replace_network_acl_association(nacl_id, subnets, client, module) - construct_acl_entries(nacl, client, module) + for entry in ansible_entries: changed = True - return changed, nacl["NetworkAcl"]["NetworkAclId"] - else: - changed = False - nacl_id = nacl["NetworkAcls"][0]["NetworkAclId"] - changed |= subnets_changed(nacl, client, module) - changed |= nacls_changed(nacl, client, module) - changed |= tags_changed(nacl_id, client, module) - return changed, nacl_id + if not check_mode: + create_network_acl_entry( + client, + network_acl_id=nacl_id, + protocol=str(PROTOCOL_NUMBERS[entry[1]]), + egress=egress, + rule_action=entry[2], + rule_number=entry[0], + **process_rule_entry(entry), + ) + return changed -def remove_network_acl(client, module): +def associate_nacl_to_subnets(client, module: AnsibleAWSModule, nacl_id: str, subnets_ids: List[str]) -> bool: changed = False - result = dict() - nacl = describe_network_acl(client, module) - if nacl["NetworkAcls"]: - nacl_id = nacl["NetworkAcls"][0]["NetworkAclId"] - vpc_id = nacl["NetworkAcls"][0]["VpcId"] - associations = nacl["NetworkAcls"][0]["Associations"] - assoc_ids = [a["NetworkAclAssociationId"] for a in associations] - default_nacl_id = find_default_vpc_nacl(vpc_id, client, module) - if not default_nacl_id: - result = {vpc_id: "Default NACL ID not found - Check the VPC ID"} - return changed, result - if restore_default_associations(assoc_ids, default_nacl_id, client, module): - delete_network_acl(nacl_id, client, module) + if subnets_ids: + network_acls = describe_network_acls(client, Filters=[{"Name": "association.subnet-id", "Values": subnets_ids}]) + associations = [ + association["NetworkAclAssociationId"] + for nacl in network_acls + for association in nacl["Associations"] + if association["SubnetId"] in subnets_ids + ] + for association_id in associations: changed = True - result[nacl_id] = "Successfully deleted" - return changed, result - if not assoc_ids: - delete_network_acl(nacl_id, client, module) - changed = True - result[nacl_id] = "Successfully deleted" - return changed, result - return changed, result - + if not module.check_mode: + replace_network_acl_association(client, network_acl_id=nacl_id, association_id=association_id) + return changed -# Boto3 client methods -@AWSRetry.jittered_backoff() -def _create_network_acl(client, *args, **kwargs): - return client.create_network_acl(*args, **kwargs) +def ensure_present(client, module: AnsibleAWSModule) -> None: + changed = False + nacl = describe_network_acl(client, module) + nacl_id = None + subnets_ids = [] + subnets = module.params.get("subnets") + if subnets: + subnets_ids = find_subnets_ids(client, module, subnets) -def create_network_acl(vpc_id, client, module, tags): - params = dict(VpcId=vpc_id) - if tags: - params["TagSpecifications"] = boto3_tag_specifications(tags, ["network-acl"]) - try: + if not nacl: if module.check_mode: - nacl = dict(NetworkAcl=dict(NetworkAclId="nacl-00000000")) - else: - nacl = _create_network_acl(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - return nacl - + module.exit_json(changed=True, msg="Would have created Network ACL if not in check mode.") -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _create_network_acl_entry(client, *args, **kwargs): - return client.create_network_acl_entry(*args, **kwargs) - - -def create_network_acl_entry(params, client, module): - try: - if not module.check_mode: - _create_network_acl_entry(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) + # Create Network ACL + tags = {} + name = module.params.get("name") + vpc_id = module.params.get("vpc_id") + if name: + tags["Name"] = name + if module.params.get("tags"): + tags.update(module.params.get("tags")) + nacl = create_network_acl(client, vpc_id, tags) + changed = True + # Associate Subnets to Network ACL + nacl_id = nacl["NetworkAclId"] + changed |= associate_nacl_to_subnets(client, module, nacl_id, subnets_ids) -@AWSRetry.jittered_backoff() -def _delete_network_acl(client, *args, **kwargs): - return client.delete_network_acl(*args, **kwargs) + # Create Network ACL entries (ingress and egress) + changed |= add_network_acl_entries( + client, nacl_id, module.params.get("ingress"), egress=False, check_mode=module.check_mode + ) + changed |= add_network_acl_entries( + client, nacl_id, module.params.get("egress"), egress=True, check_mode=module.check_mode + ) + else: + nacl_id = nacl["NetworkAclId"] + changed |= subnets_changed(client, module, nacl_id, subnets_ids) + changed |= nacls_changed(client, module, nacl) + changed |= tags_changed(client, module, nacl_id) + module.exit_json(changed=changed, nacl_id=nacl_id) -def delete_network_acl(nacl_id, client, module): - try: - if not module.check_mode: - _delete_network_acl(client, NetworkAclId=nacl_id) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) +def ensure_absent(client, module: AnsibleAWSModule) -> None: + changed = False + result = {} + nacl = describe_network_acl(client, module) + if not nacl: + module.exit_json(changed=changed) -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _delete_network_acl_entry(client, *args, **kwargs): - return client.delete_network_acl_entry(*args, **kwargs) + nacl_id = nacl["NetworkAclId"] + vpc_id = nacl["VpcId"] + associations = nacl["Associations"] + assoc_ids = [a["NetworkAclAssociationId"] for a in associations] + # Find default NACL associated to the VPC + default_nacl_id = find_default_vpc_nacl(client, vpc_id) + if not default_nacl_id: + module.exit_json(changed=changed, msg="Default NACL ID not found - Check the VPC ID") -def delete_network_acl_entry(params, client, module): - try: + # Replace Network ACL association + for assoc_id in assoc_ids: + changed = True if not module.check_mode: - _delete_network_acl_entry(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - - -@AWSRetry.jittered_backoff() -def _describe_network_acls(client, **kwargs): - return client.describe_network_acls(**kwargs) - - -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _describe_network_acls_retry_missing(client, **kwargs): - return client.describe_network_acls(**kwargs) + replace_network_acl_association(client, network_acl_id=default_nacl_id, association_id=assoc_id) + # Delete Network ACL + changed = True + if module.check_mode: + module.exit_json(changed=changed, msg=f"Would have deleted Network ACL id '{nacl_id}' if not in check mode.") -def describe_acl_associations(subnets, client, module): - if not subnets: - return [] - try: - results = _describe_network_acls_retry_missing( - client, Filters=[{"Name": "association.subnet-id", "Values": subnets}] - ) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - associations = results["NetworkAcls"][0]["Associations"] - return [a["NetworkAclAssociationId"] for a in associations if a["SubnetId"] in subnets] - + changed = delete_network_acl(client, network_acl_id=nacl_id) + module.exit_json(changed=changed, msg=f"Network ACL id '{nacl_id}' successfully deleted.") -def describe_network_acl(client, module): - try: - if module.params.get("nacl_id"): - nacl = _describe_network_acls( - client, Filters=[{"Name": "network-acl-id", "Values": [module.params.get("nacl_id")]}] - ) - else: - nacl = _describe_network_acls(client, Filters=[{"Name": "tag:Name", "Values": [module.params.get("name")]}]) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - return nacl - - -def find_acl_by_id(nacl_id, client, module): - try: - return _describe_network_acls_retry_missing(client, NetworkAclIds=[nacl_id]) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - - -def find_default_vpc_nacl(vpc_id, client, module): - try: - response = _describe_network_acls_retry_missing(client, Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - nacls = response["NetworkAcls"] - return [n["NetworkAclId"] for n in nacls if n["IsDefault"] is True] +def describe_network_acl(client, module: AnsibleAWSModule) -> Optional[Dict[str, Any]]: + nacl_id = module.params.get("nacl_id") + name = module.params.get("name") -def find_subnet_ids_by_nacl_id(nacl_id, client, module): - try: - results = _describe_network_acls_retry_missing( - client, Filters=[{"Name": "association.network-acl-id", "Values": [nacl_id]}] - ) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - if results["NetworkAcls"]: - associations = results["NetworkAcls"][0]["Associations"] - return [s["SubnetId"] for s in associations if s["SubnetId"]] + if nacl_id: + filters = [{"Name": "network-acl-id", "Values": [nacl_id]}] else: - return [] - - -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _replace_network_acl_association(client, *args, **kwargs): - return client.replace_network_acl_association(*args, **kwargs) + filters = [{"Name": "tag:Name", "Values": [name]}] + network_acls = describe_network_acls(client, Filters=filters) + return None if not network_acls else network_acls[0] -def replace_network_acl_association(nacl_id, subnets, client, module): - params = dict() - params["NetworkAclId"] = nacl_id - for association in describe_acl_associations(subnets, client, module): - params["AssociationId"] = association - try: - if not module.check_mode: - _replace_network_acl_association(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - - -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _replace_network_acl_entry(client, *args, **kwargs): - return client.replace_network_acl_entry(*args, **kwargs) +def find_default_vpc_nacl(client, vpc_id: str) -> Optional[str]: + default_nacl_id = None + for nacl in describe_network_acls(client, Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]): + if nacl.get("IsDefault", False): + default_nacl_id = nacl["NetworkAclId"] + break + return default_nacl_id -def replace_network_acl_entry(entries, Egress, nacl_id, client, module): - for entry in entries: - params = entry - params["NetworkAclId"] = nacl_id - try: - if not module.check_mode: - _replace_network_acl_entry(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - +def find_subnets_ids(client, module: AnsibleAWSModule, subnets_ids_or_names: List[str]) -> List[str]: + subnets_ids = [] + subnets_names = [] -@AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidNetworkAclID.NotFound"]) -def _replace_network_acl_association(client, *args, **kwargs): - return client.replace_network_acl_association(*args, **kwargs) + # Find Subnets by ID + subnets = describe_subnets(client, Filters=[{"Name": "subnet-id", "Values": subnets_ids_or_names}]) + subnets_ids += [subnet["SubnetId"] for subnet in subnets] + subnets_names += [tag["Value"] for subnet in subnets for tag in subnet.get("Tags", []) if tag["Key"] == "Name"] + # Find Subnets by Name + subnets = describe_subnets(client, Filters=[{"Name": "tag:Name", "Values": subnets_ids_or_names}]) + subnets_ids += [subnet["SubnetId"] for subnet in subnets] + subnets_names += [tag["Value"] for subnet in subnets for tag in subnet.get("Tags", []) if tag["Key"] == "Name"] -def restore_default_acl_association(params, client, module): - try: - if not module.check_mode: - _replace_network_acl_association(client, **params) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - - -@AWSRetry.jittered_backoff() -def _describe_subnets(client, *args, **kwargs): - return client.describe_subnets(*args, **kwargs) - - -def subnets_to_associate(nacl, client, module): - params = list(module.params.get("subnets")) - if not params: - return [] - all_found = [] - if any(x.startswith("subnet-") for x in params): - try: - subnets = _describe_subnets(client, Filters=[{"Name": "subnet-id", "Values": params}]) - all_found.extend(subnets.get("Subnets", [])) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - if len(params) != len(all_found): - try: - subnets = _describe_subnets(client, Filters=[{"Name": "tag:Name", "Values": params}]) - all_found.extend(subnets.get("Subnets", [])) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e) - return list(set(s["SubnetId"] for s in all_found if s.get("SubnetId"))) + unexisting_subnets = [s for s in subnets_ids_or_names if s not in subnets_names + subnets_ids] + if unexisting_subnets: + module.fail_json(msg=f"The following subnets do not exist: {unexisting_subnets}") + return subnets_ids def main(): @@ -576,30 +501,35 @@ def main(): vpc_id=dict(), name=dict(), nacl_id=dict(), - subnets=dict(required=False, type="list", default=list(), elements="str"), + subnets=dict(required=False, type="list", default=[], elements="str"), tags=dict(required=False, type="dict", aliases=["resource_tags"]), purge_tags=dict(required=False, type="bool", default=True), ingress=dict(required=False, type="list", default=list(), elements="list"), egress=dict(required=False, type="list", default=list(), elements="list"), state=dict(default="present", choices=["present", "absent"]), ) + + mutually_exclusive = [ + ["name", "nacl_id"], + ] + module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=[["name", "nacl_id"]], required_if=[["state", "present", ["vpc_id"]]], + mutually_exclusive=mutually_exclusive, ) - state = module.params.get("state").lower() - client = module.client("ec2") - invocations = { - "present": setup_network_acl, - "absent": remove_network_acl, - } - (changed, results) = invocations[state](client, module) - module.exit_json(changed=changed, nacl_id=results) + try: + if module.params.get("state") == "present": + ensure_present(client, module) + else: + ensure_absent(client, module) + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) if __name__ == "__main__": diff --git a/plugins/modules/ec2_vpc_nacl_info.py b/plugins/modules/ec2_vpc_nacl_info.py index d95508a894e..9e0bc4e7f05 100644 --- a/plugins/modules/ec2_vpc_nacl_info.py +++ b/plugins/modules/ec2_vpc_nacl_info.py @@ -102,15 +102,15 @@ sample: [[100, 'all', 'allow', '0.0.0.0/0', null, null, null, null]] """ -try: - import botocore -except ImportError: - pass # caught by AnsibleAWSModule +from typing import Any +from typing import Dict +from typing import List +from typing import Union from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleEC2Error +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_network_acls from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list @@ -121,55 +121,60 @@ PROTOCOL_NAMES = {"-1": "all", "1": "icmp", "6": "tcp", "17": "udp"} -def list_ec2_vpc_nacls(connection, module): +def format_nacl(nacl: Dict[str, Any]) -> Dict[str, Any]: + # Turn the boto3 result into ansible friendly snake cases + nacl = camel_dict_to_snake_dict(nacl) + + # convert boto3 tags list into ansible dict + if "tags" in nacl: + nacl["tags"] = boto3_tag_list_to_ansible_dict(nacl["tags"], "key", "value") + + # Convert NACL entries + if "entries" in nacl: + nacl["egress"] = [ + nacl_entry_to_list(entry) for entry in nacl["entries"] if entry["rule_number"] < 32767 and entry["egress"] + ] + nacl["ingress"] = [ + nacl_entry_to_list(entry) + for entry in nacl["entries"] + if entry["rule_number"] < 32767 and not entry["egress"] + ] + del nacl["entries"] + + # Read subnets from NACL Associations + if "associations" in nacl: + nacl["subnets"] = [a["subnet_id"] for a in nacl["associations"]] + del nacl["associations"] + + # Read Network ACL id + if "network_acl_id" in nacl: + nacl["nacl_id"] = nacl["network_acl_id"] + del nacl["network_acl_id"] + + return nacl + + +def list_ec2_vpc_nacls(connection, module: AnsibleAWSModule) -> None: nacl_ids = module.params.get("nacl_ids") - filters = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + filters = module.params.get("filters") - if nacl_ids is None: - nacl_ids = [] + params = {} + if filters: + params["Filters"] = ansible_dict_to_boto3_filter_list(filters) + if nacl_ids: + params["NetworkAclIds"] = nacl_ids try: - nacls = connection.describe_network_acls(aws_retry=True, NetworkAclIds=nacl_ids, Filters=filters) - except is_boto3_error_code("InvalidNetworkAclID.NotFound"): - module.fail_json(msg="Unable to describe ACL. NetworkAcl does not exist") - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg=f"Unable to describe network ACLs {nacl_ids}") - - # Turn the boto3 result in to ansible_friendly_snaked_names - snaked_nacls = [] - for nacl in nacls["NetworkAcls"]: - snaked_nacls.append(camel_dict_to_snake_dict(nacl)) - - # Turn the boto3 result in to ansible friendly tag dictionary - for nacl in snaked_nacls: - if "tags" in nacl: - nacl["tags"] = boto3_tag_list_to_ansible_dict(nacl["tags"], "key", "value") - if "entries" in nacl: - nacl["egress"] = [ - nacl_entry_to_list(entry) - for entry in nacl["entries"] - if entry["rule_number"] < 32767 and entry["egress"] - ] - nacl["ingress"] = [ - nacl_entry_to_list(entry) - for entry in nacl["entries"] - if entry["rule_number"] < 32767 and not entry["egress"] - ] - del nacl["entries"] - if "associations" in nacl: - nacl["subnets"] = [a["subnet_id"] for a in nacl["associations"]] - del nacl["associations"] - if "network_acl_id" in nacl: - nacl["nacl_id"] = nacl["network_acl_id"] - del nacl["network_acl_id"] - - module.exit_json(nacls=snaked_nacls) - - -def nacl_entry_to_list(entry): + network_acls = describe_network_acls(connection, **params) + if not network_acls: + module.fail_json(msg="Unable to describe ACL. NetworkAcl does not exist") + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) + + module.exit_json(nacls=[format_nacl(nacl) for nacl in network_acls]) + + +def nacl_entry_to_list(entry: Dict[str, Any]) -> List[Union[str, int, None]]: # entry list format # [ rule_num, protocol name or number, allow or deny, ipv4/6 cidr, icmp type, icmp code, port from, port to] elist = [] @@ -217,7 +222,7 @@ def main(): supports_check_mode=True, ) - connection = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff()) + connection = module.client("ec2") list_ec2_vpc_nacls(connection, module) diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml index 875e7f0b2d7..432aaf3ba99 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml @@ -1,7 +1,8 @@ # ============================================================ -- block: - - name: create ingress and egress rules using subnet IDs - ec2_vpc_nacl: +- name: Test Ingress and Egress rules + block: + - name: Create ingress and egress rules using subnet IDs + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -16,20 +17,20 @@ state: 'present' register: nacl - - name: assert the network acl was created - assert: + - name: Assert the network acl was created + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - - name: get network ACL facts - ec2_vpc_nacl_info: + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 3 @@ -37,8 +38,8 @@ # ============================================================ - - name: remove an ingress rule - ec2_vpc_nacl: + - name: Remove an ingress rule + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -52,20 +53,20 @@ state: 'present' register: nacl - - name: assert the network acl changed - assert: + - name: Assert the network acl changed + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - - name: get network ACL facts - ec2_vpc_nacl_info: + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 2 @@ -73,8 +74,8 @@ # ============================================================ - - name: remove the egress rule - ec2_vpc_nacl: + - name: Remove the egress rule + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -87,20 +88,20 @@ state: 'present' register: nacl - - name: assert the network acl changed - assert: + - name: Assert the network acl changed + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - - name: get network ACL facts - ec2_vpc_nacl_info: + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 2 @@ -108,8 +109,8 @@ # ============================================================ - - name: add egress rules - ec2_vpc_nacl: + - name: Add egress rules + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -124,20 +125,20 @@ state: 'present' register: nacl - - name: assert the network acl changed - assert: + - name: Assert the network acl changed + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - - name: get network ACL facts - ec2_vpc_nacl_info: + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 2 @@ -145,14 +146,14 @@ # ============================================================ - - name: remove the network ACL - ec2_vpc_nacl: + - name: Remove the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: nacl - - name: assert nacl was removed - assert: + - name: Assert nacl was removed + ansible.builtin.assert: that: - nacl.changed diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml index 1366971613a..2113fb4aa9c 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml @@ -1,9 +1,10 @@ -- block: +- name: Test using IPv6 + block: # ============================================================ - - name: create ingress and egress rules using subnet names - ec2_vpc_nacl: + - name: Create ingress and egress rules using subnet names + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_ipv6_id }}" name: "{{ nacl_name }}" subnets: @@ -18,15 +19,18 @@ - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] state: 'present' register: nacl - - assert: + + - name: Assert that module returned the Network ACL id + ansible.builtin.assert: that: - nacl.nacl_id - - set_fact: + - name: Set fact for Network ACL ID + ansible.builtin.set_fact: nacl_id: "{{ nacl.nacl_id }}" - - name: add ipv6 entries - ec2_vpc_nacl: + - name: Add ipv6 entries + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_ipv6_id }}" name: "{{ nacl_name }}" subnets: @@ -45,26 +49,27 @@ state: 'present' register: nacl - - assert: + - name: Assert that module reported change while the Network ACL remained unchanged + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id == nacl_id - - name: get network ACL facts (test that it works with ipv6 entries) - ec2_vpc_nacl_info: + - name: Get network ACL facts (test that it works with ipv6 entries) + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 5 - nacl_facts.nacls[0].egress | length == 2 - - name: purge ingress entries - ec2_vpc_nacl: + - name: Purge ingress entries + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_ipv6_id }}" name: "{{ nacl_name }}" subnets: @@ -78,13 +83,14 @@ state: 'present' register: nacl - - assert: + - name: Assert that module reported change while the Network ACL remained unchanged + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id == nacl_id - - name: purge egress entries - ec2_vpc_nacl: + - name: Purge egress entries + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_ipv6_id }}" name: "{{ nacl_name }}" subnets: @@ -96,18 +102,19 @@ state: 'present' register: nacl - - assert: + - name: Assert that module reported change + ansible.builtin.assert: that: - nacl.changed - - name: get network ACL facts (test that removed entries are gone) - ec2_vpc_nacl_info: + - name: Get network ACL facts (test that removed entries are gone) + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl_id }}" register: nacl_facts - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].ingress | length == 0 @@ -115,10 +122,10 @@ always: - - name: remove network ACL - ec2_vpc_nacl: + - name: Remove network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_ipv6_id }}" name: "{{ nacl_name }}" state: absent register: removed_acl - ignore_errors: yes + ignore_errors: true diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml index 36c7ab2d8bb..445161ccd82 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/main.yml @@ -5,40 +5,39 @@ secret_key: "{{ aws_secret_key }}" session_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" - collections: - - amazon.aws + block: # ============================================================ - - name: test without any parameters - ec2_vpc_nacl: + - name: Test without any parameters + community.aws.ec2_vpc_nacl: register: result - ignore_errors: yes + ignore_errors: true - - name: assert required parameters - assert: + - name: Assert required parameters + ansible.builtin.assert: that: - result.failed - "result.msg == 'one of the following is required: name, nacl_id'" - - name: get network ACL info without any parameters - ec2_vpc_nacl_info: + - name: Get network ACL info without any parameters + community.aws.ec2_vpc_nacl_info: register: nacl_facts - - name: assert we don't error + - name: Assert we don't error assert: that: - nacl_facts is succeeded - - name: get network ACL info with invalid ID - ec2_vpc_nacl_info: + - name: Get network ACL info with invalid ID + community.aws.ec2_vpc_nacl_info: nacl_ids: - 'acl-000000000000' register: nacl_facts - ignore_errors: yes + ignore_errors: true - - name: assert message mentions missing ACLs + - name: Assert message mentions missing ACLs assert: that: - nacl_facts is failed @@ -46,34 +45,34 @@ # ============================================================ - - name: fetch AZ availability - aws_az_info: + - name: Fetch AZ availability + amazon.aws.aws_az_info: register: az_info - name: Assert that we have multiple AZs available to us - assert: + ansible.builtin.assert: that: az_info.availability_zones | length >= 2 - - name: pick AZs - set_fact: + - name: Pick AZs + ansible.builtin.set_fact: az_one: '{{ az_info.availability_zones[0].zone_name }}' az_two: '{{ az_info.availability_zones[1].zone_name }}' # ============================================================ - - name: create a VPC - ec2_vpc_net: + - name: Create a VPC + amazon.aws.ec2_vpc_net: cidr_block: "{{ vpc_cidr }}" name: "{{ vpc_name }}" state: present register: vpc - name: Save VPC ID for later - set_fact: + ansible.builtin.set_fact: vpc_id: "{{ vpc.vpc.id }}" - - name: create subnets - ec2_vpc_subnet: + - name: Create subnets + amazon.aws.ec2_vpc_subnet: cidr: "{{ item.cidr }}" az: "{{ item.az }}" vpc_id: "{{ vpc_id }}" @@ -95,27 +94,29 @@ name: "{{ subnet_name }}-4" register: subnets - - name: set helpful facts about subnets - set_fact: + - name: Set helpful facts about subnets + ansible.builtin.set_fact: subnet_ids: "{{ subnets | community.general.json_query('results[*].subnet.id') }}" subnet_names: "{{ subnets | community.general.json_query('results[*].subnet.tags.Name') }}" - - name: create VPC for IPv6 tests - ec2_vpc_net: + - name: Create VPC for IPv6 tests + amazon.aws.ec2_vpc_net: cidr_block: "{{ vpc_ipv6_cidr }}" name: "{{ vpc_ipv6_name }}" state: present - ipv6_cidr: yes + ipv6_cidr: true register: vpc_result - - set_fact: + + - name: Set helpful IPv6 facts + ansible.builtin.set_fact: vpc_ipv6_id: "{{ vpc_result.vpc.id }}" vpc_ipv6_cidr_v6: "{{ _ipv6_cidr }}" subnet_ipv6: "{{ _ipv6_cidr | regex_replace('::/56', '::/64') }}" vars: _ipv6_cidr: "{{ vpc_result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}" - - name: create subnet with IPv6 - ec2_vpc_subnet: + - name: Create subnet with IPv6 + amazon.aws.ec2_vpc_subnet: cidr: "{{ vpc_ipv6_cidr }}" vpc_id: "{{ vpc_ipv6_id }}" ipv6_cidr: "{{ subnet_ipv6 }}" @@ -124,31 +125,29 @@ Name: "{{ subnet_name }}-ipv6" # ============================================================ - - - include_tasks: tasks/subnet_ids.yml - - - include_tasks: tasks/subnet_names.yml - - - include_tasks: tasks/tags.yml - - - include_tasks: tasks/ingress_and_egress.yml - - - include_tasks: tasks/ipv6.yml + - name: Run individual tasks + ansible.builtin.include_tasks: "tasks/{{ item }}.yml" + with_items: + - subnet_ids + - subnet_names + - tags + - ingress_and_egress + - ipv6 # ============================================================ always: - - name: remove network ACL - ec2_vpc_nacl: + - name: Remove network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: removed_acl - ignore_errors: yes + ignore_errors: true - - name: remove subnets - ec2_vpc_subnet: + - name: Remove subnets + amazon.aws.ec2_vpc_subnet: cidr: "{{ item.cidr }}" vpc_id: "{{ item.vpc_id | default(vpc_id) }}" state: absent @@ -159,14 +158,14 @@ - cidr: "{{ subnet_4 }}" - cidr: "{{ vpc_ipv6_cidr }}" vpc_id: "{{ vpc_ipv6_id }}" - ignore_errors: yes + ignore_errors: true register: removed_subnets - - name: remove the VPCs - ec2_vpc_net: + - name: Remove the VPCs + amazon.aws.ec2_vpc_net: vpc_id: "{{ item }}" state: absent - ignore_errors: yes + ignore_errors: true register: removed_vpc with_items: - '{{ vpc_id }}' diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml index 4e1affa1f34..3a367e84f89 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml @@ -1,7 +1,7 @@ # ============================================================ -- name: create ingress and egress rules using subnet IDs - ec2_vpc_nacl: +- name: Create ingress and egress rules using subnet IDs + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -16,23 +16,24 @@ state: 'present' register: nacl -- set_fact: +- name: Set helpful fact for Network ACL ID + ansible.builtin.set_fact: nacl_id: "{{ nacl.nacl_id }}" -- name: assert the network acl was created - assert: +- name: Assert the network acl was created + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl_id }}" register: nacl_facts -- name: assert the nacl has the correct attributes - assert: +- name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].nacl_id == nacl_id @@ -44,8 +45,8 @@ # ============================================================ -- name: test idempotence - ec2_vpc_nacl: +- name: Test idempotence + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -60,28 +61,28 @@ state: 'present' register: nacl -- name: assert the network acl already existed - assert: +- name: Assert the network acl already existed + ansible.builtin.assert: that: - not nacl.changed - nacl.nacl_id == nacl_id - nacl.nacl_id.startswith('acl-') -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts_idem -- name: assert the facts are the same as before - assert: +- name: Assert the facts are the same as before + ansible.builtin.assert: that: - nacl_facts_idem == nacl_facts # ============================================================ -- name: remove a subnet from the network ACL - ec2_vpc_nacl: +- name: Remove a subnet from the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: @@ -99,21 +100,21 @@ state: 'present' register: nacl -- name: assert the network ACL changed - assert: +- name: Assert the network ACL changed + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - nacl.nacl_id == nacl_id -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_id: - "{{ nacl.nacl_id }}" register: nacl_facts -- name: assert the nacl has the correct attributes - assert: +- name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].nacl_id == nacl_id @@ -125,37 +126,38 @@ # ============================================================ -- name: remove the network ACL - ec2_vpc_nacl: +- name: Remove the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: nacl -- name: assert nacl was removed - assert: +- name: Assert nacl was removed + ansible.builtin.assert: that: - nacl.changed -- name: re-remove the network ACL by name (test idempotency) - ec2_vpc_nacl: +- name: Re-remove the network ACL by name (test idempotency) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: nacl -- name: assert nacl was removed - assert: + +- name: Assert nacl was removed + ansible.builtin.assert: that: - nacl is not changed -- name: re-remove the network ACL by id (test idempotency) - ec2_vpc_nacl: +- name: Re-remove the network ACL by id (test idempotency) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" nacl_id: "{{ nacl_id }}" state: absent register: nacl -- name: assert nacl was removed - assert: +- name: Assert nacl was removed + ansible.builtin.assert: that: - nacl is not changed diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml index 4db7e1b2068..dc44fef804a 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml @@ -1,7 +1,7 @@ # ============================================================ -- name: create ingress and egress rules using subnet names - ec2_vpc_nacl: +- name: Create ingress and egress rules using subnet names + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_names }}" @@ -16,23 +16,24 @@ state: 'present' register: nacl -- set_fact: +- name: Set helpful fact for Network ACL ID + ansible.builtin.set_fact: nacl_id: "{{ nacl.nacl_id }}" -- name: assert the network acl was created - assert: +- name: Assert the network acl was created + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl_id }}" register: nacl_facts -- name: assert the nacl has the correct attributes - assert: +- name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].nacl_id == nacl_id @@ -43,8 +44,8 @@ # ============================================================ -- name: test idempotence - ec2_vpc_nacl: +- name: Test idempotence + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_names }}" @@ -59,28 +60,28 @@ state: 'present' register: nacl -- name: assert the network acl already existed - assert: +- name: Assert the network acl already existed + ansible.builtin.assert: that: - not nacl.changed - nacl.nacl_id == nacl_id - nacl.nacl_id.startswith('acl-') -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts_idem -- name: assert the facts are the same as before - assert: +- name: Assert the facts are the same as before + ansible.builtin.assert: that: - nacl_facts_idem == nacl_facts # ============================================================ -- name: remove a subnet from the network ACL - ec2_vpc_nacl: +- name: Remove a subnet from the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: @@ -98,21 +99,21 @@ state: 'present' register: nacl -- name: assert the network ACL changed - assert: +- name: Assert the network ACL changed + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id == nacl_id - nacl.nacl_id.startswith('acl-') -- name: get network ACL facts - ec2_vpc_nacl_info: +- name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_facts -- name: assert the nacl has the correct attributes - assert: +- name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_facts.nacls | length == 1 - nacl_facts.nacls[0].nacl_id == nacl_id @@ -123,14 +124,14 @@ # ============================================================ -- name: remove the network ACL - ec2_vpc_nacl: +- name: Remove the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: nacl -- name: assert nacl was removed - assert: +- name: Assert nacl was removed + ansible.builtin.assert: that: - nacl.changed diff --git a/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml b/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml index da3ad71dda3..0e69ce760ac 100644 --- a/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml +++ b/tests/integration/targets/ec2_vpc_nacl/tasks/tags.yml @@ -1,4 +1,5 @@ -- vars: +- name: Run test from tags.yml + vars: first_tags: 'Key with Spaces': Value with spaces CamelCaseKey: CamelCaseValue @@ -30,40 +31,40 @@ # ============================================================ - - name: create a network ACL using subnet IDs - ec2_vpc_nacl: + - name: Create a network ACL using subnet IDs + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" state: 'present' register: nacl - - name: assert the network acl was created - assert: + - name: Assert the network acl was created + ansible.builtin.assert: that: - nacl.changed - nacl.nacl_id.startswith('acl-') - name: Store NACL ID - set_fact: + ansible.builtin.set_fact: nacl_id: '{{ nacl.nacl_id }}' - - name: get network ACL facts - ec2_vpc_nacl_info: + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl_id }}" register: nacl_info - - name: assert the nacl has the correct attributes - assert: + - name: Assert the nacl has the correct attributes + ansible.builtin.assert: that: - nacl_info.nacls[0].nacl_id == nacl_id - nacl_info.nacls[0].tags == name_tags # ============================================================ - - name: (check) add tags - ec2_vpc_nacl: + - name: Add tags (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -72,36 +73,37 @@ register: nacl check_mode: True - - name: assert would change - assert: + - name: Assert would change + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - - name: add tags - ec2_vpc_nacl: + - name: Add tags + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: "{{ first_tags }}" state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify the tags were added - assert: + - name: Verify the tags were added + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - nacl_info.nacls[0].nacl_id == nacl_id - nacl_info.nacls[0].tags == ( first_tags | combine(name_tags) ) - - name: (check) add tags - IDEMPOTENCY - ec2_vpc_nacl: + - name: Add tags - IDEMPOTENCY (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -110,28 +112,29 @@ register: nacl check_mode: True - - name: assert would not change - assert: + - name: Assert would not change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id - - name: add tags - IDEMPOTENCY - ec2_vpc_nacl: + - name: Add tags - IDEMPOTENCY + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: "{{ first_tags }}" state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify no change - assert: + - name: Verify no change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id @@ -140,14 +143,14 @@ # ============================================================ - - name: get network ACL facts by filter - ec2_vpc_nacl_info: + - name: Get network ACL facts by filter + community.aws.ec2_vpc_nacl_info: filters: "tag:Name": "{{ nacl_name }}" register: nacl_info - - name: assert the facts are the same as before - assert: + - name: Assert the facts are the same as before + ansible.builtin.assert: that: - nacl_info.nacls | length == 1 - nacl.nacl_id == nacl_id @@ -155,8 +158,8 @@ # ============================================================ - - name: (check) modify tags with purge - ec2_vpc_nacl: + - name: Modify tags with purge (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -165,36 +168,37 @@ register: nacl check_mode: True - - name: assert would change - assert: + - name: Assert would change + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - - name: modify tags with purge - ec2_vpc_nacl: + - name: Modify tags with purge + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: "{{ second_tags }}" state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify the tags were added - assert: + - name: Verify the tags were added + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - nacl_info.nacls[0].nacl_id == nacl_id - nacl_info.nacls[0].tags == ( second_tags | combine(name_tags) ) - - name: (check) modify tags with purge - IDEMPOTENCY - ec2_vpc_nacl: + - name: Modify tags with purge - IDEMPOTENCY (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -203,28 +207,29 @@ register: nacl check_mode: True - - name: assert would not change - assert: + - name: Assert would not change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id - - name: modify tags with purge - IDEMPOTENCY - ec2_vpc_nacl: + - name: Modify tags with purge - IDEMPOTENCY + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: "{{ second_tags }}" state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify no change - assert: + - name: Verify no change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id @@ -233,8 +238,8 @@ # ============================================================ - - name: (check) modify tags without purge - ec2_vpc_nacl: + - name: Modify tags without purge (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -244,14 +249,14 @@ register: nacl check_mode: True - - name: assert would change - assert: + - name: Assert would change + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - - name: modify tags without purge - ec2_vpc_nacl: + - name: Modify tags without purge + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -259,22 +264,23 @@ state: 'present' purge_tags: False register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify the tags were added - assert: + - name: Verify the tags were added + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - nacl_info.nacls[0].nacl_id == nacl_id - nacl_info.nacls[0].tags == ( final_tags | combine(name_tags) ) - - name: (check) modify tags without purge - IDEMPOTENCY - ec2_vpc_nacl: + - name: Modify tags without purge - IDEMPOTENCY (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -284,14 +290,14 @@ register: nacl check_mode: True - - name: assert would not change - assert: + - name: Assert would not change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id - - name: modify tags without purge - IDEMPOTENCY - ec2_vpc_nacl: + - name: Modify tags without purge - IDEMPOTENCY + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -299,14 +305,15 @@ state: 'present' purge_tags: False register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify no change - assert: + - name: Verify no change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id @@ -315,8 +322,8 @@ # ============================================================ - - name: (check) No change to tags without setting tags - ec2_vpc_nacl: + - name: No change to tags without setting tags (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -324,27 +331,28 @@ register: nacl check_mode: True - - name: assert would change - assert: + - name: Assert would change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id - name: No change to tags without setting tags - ec2_vpc_nacl: + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify the tags were added - assert: + - name: Verify the tags were added + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id @@ -353,8 +361,8 @@ # ============================================================ - - name: (check) remove non name tags - ec2_vpc_nacl: + - name: Remove non name tags (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -363,36 +371,37 @@ register: nacl check_mode: True - - name: assert would change - assert: + - name: Assert would change + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - - name: remove non name tags - ec2_vpc_nacl: + - name: Remove non name tags + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: {} state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify the tags were added - assert: + - name: Verify the tags were added + ansible.builtin.assert: that: - nacl is changed - nacl.nacl_id == nacl_id - nacl_info.nacls[0].nacl_id == nacl_id - nacl_info.nacls[0].tags == name_tags - - name: (check) remove non name tags - IDEMPOTENCY - ec2_vpc_nacl: + - name: Remove non name tags - IDEMPOTENCY (check mode) + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" @@ -401,28 +410,29 @@ register: nacl check_mode: True - - name: assert would not change - assert: + - name: Assert would not change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id - - name: remove non name tags - IDEMPOTENCY - ec2_vpc_nacl: + - name: Remove non name tags - IDEMPOTENCY + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" subnets: "{{ subnet_ids }}" tags: {} state: 'present' register: nacl - - name: get network ACL facts - ec2_vpc_nacl_info: + + - name: Get network ACL facts + community.aws.ec2_vpc_nacl_info: nacl_ids: - "{{ nacl.nacl_id }}" register: nacl_info - - name: verify no change - assert: + - name: Verify no change + ansible.builtin.assert: that: - nacl is not changed - nacl.nacl_id == nacl_id @@ -432,14 +442,14 @@ # ============================================================ always: - - name: remove the network ACL - ec2_vpc_nacl: + - name: Remove the network ACL + community.aws.ec2_vpc_nacl: vpc_id: "{{ vpc_id }}" name: "{{ nacl_name }}" state: absent register: nacl - - name: assert nacl was removed - assert: + - name: Assert nacl was removed + ansible.builtin.assert: that: - nacl.changed