diff --git a/hacking/aws_config/testing_policies/compute-policy.json b/hacking/aws_config/testing_policies/compute-policy.json index 866a82b64d9..26932dfa5e9 100644 --- a/hacking/aws_config/testing_policies/compute-policy.json +++ b/hacking/aws_config/testing_policies/compute-policy.json @@ -38,61 +38,21 @@ "ec2:*LaunchTemplate", "ec2:*LaunchTemplateVersion", "ec2:*LaunchTemplateVersions", - "ec2:AllocateAddress", - "ec2:AssociateAddress", - "ec2:AssociateDhcpOptions", - "ec2:AssociateRouteTable", - "ec2:AssociateVpcCidrBlock", - "ec2:AssociateSubnetCidrBlock", - "ec2:AttachInternetGateway", - "ec2:AttachNetworkInterface", "ec2:AttachVolume", - "ec2:AttachVpnGateway", - "ec2:CreateCustomerGateway", - "ec2:CreateDhcpOptions", "ec2:CreateImage", - "ec2:CreateInternetGateway", "ec2:CreateKeyPair", - "ec2:CreateNatGateway", - "ec2:CreateNetworkInterface", - "ec2:CreateRoute", - "ec2:CreateRouteTable", "ec2:CreateSecurityGroup", "ec2:CreateSnapshot", - "ec2:CreateSubnet", "ec2:CreateTags", - "ec2:CreateVpc", - "ec2:CreateVpnConnection", - "ec2:CreateVpnGateway", - "ec2:DeleteCustomerGateway", - "ec2:DeleteDhcpOptions", - "ec2:DeleteInternetGateway", "ec2:DeleteKeyPair", - "ec2:DeleteNatGateway", - "ec2:DeleteNetworkInterface", - "ec2:DeleteRoute", - "ec2:DeleteRouteTable", "ec2:DeleteSnapshot", - "ec2:DeleteSubnet", "ec2:DeleteTags", - "ec2:DeleteVpc", - "ec2:DeleteVpnConnection", - "ec2:DeleteVpnGateway", "ec2:DeregisterImage", - "ec2:DetachInternetGateway", - "ec2:DetachVpnGateway", "ec2:Describe*", - "ec2:DisassociateAddress", - "ec2:DisassociateRouteTable", - "ec2:DisassociateSubnetCidrBlock", "ec2:ImportKeyPair", "ec2:ModifyImageAttribute", "ec2:ModifyInstanceAttribute", - "ec2:ModifySubnetAttribute", - "ec2:ModifyVpcAttribute", "ec2:RegisterImage", - "ec2:ReleaseAddress", - "ec2:ReplaceRouteTableAssociation", "ec2:ReplaceIamInstanceProfileAssociation", "ec2:ReportInstanceStatus" ], diff --git a/hacking/aws_config/testing_policies/network-policy.json b/hacking/aws_config/testing_policies/network-policy.json index d5cb2d36ec9..cec617ca294 100644 --- a/hacking/aws_config/testing_policies/network-policy.json +++ b/hacking/aws_config/testing_policies/network-policy.json @@ -23,6 +23,59 @@ ], "Resource": "*" }, + { + "Sid": "AllowUnspecifiedEC2NetworkingResource", + "Effect": "Allow", + "Action": [ + "ec2:AllocateAddress", + "ec2:AssociateAddress", + "ec2:AssociateDhcpOptions", + "ec2:AssociateRouteTable", + "ec2:AssociateVpcCidrBlock", + "ec2:AssociateSubnetCidrBlock", + "ec2:AttachInternetGateway", + "ec2:AttachNetworkInterface", + "ec2:AttachVpnGateway", + "ec2:CreateCustomerGateway", + "ec2:CreateDhcpOptions", + "ec2:CreateNatGateway", + "ec2:CreateNetworkAcl", + "ec2:CreateNetworkAclEntry", + "ec2:CreateNetworkInterface", + "ec2:CreateRoute", + "ec2:CreateRouteTable", + "ec2:CreateSubnet", + "ec2:CreateVpc", + "ec2:CreateVpnConnection", + "ec2:CreateVpnGateway", + "ec2:DeleteCustomerGateway", + "ec2:DeleteDhcpOptions", + "ec2:DeleteInternetGateway", + "ec2:DeleteNatGateway", + "ec2:DeleteNetworkAcl", + "ec2:DeleteNetworkAclEntry", + "ec2:DeleteNetworkInterface", + "ec2:DeleteRoute", + "ec2:DeleteRouteTable", + "ec2:DeleteSubnet", + "ec2:DeleteVpc", + "ec2:DeleteVpnConnection", + "ec2:DeleteVpnGateway", + "ec2:DetachInternetGateway", + "ec2:DetachVpnGateway", + "ec2:Describe*", + "ec2:DisassociateAddress", + "ec2:DisassociateRouteTable", + "ec2:DisassociateSubnetCidrBlock", + "ec2:ModifySubnetAttribute", + "ec2:ModifyVpcAttribute", + "ec2:ReleaseAddress", + "ec2:ReplaceNetworkAclAssociation", + "ec2:ReplaceNetworkAclEntry", + "ec2:ReplaceRouteTableAssociation" + ], + "Resource": "*" + }, { "Sid": "AllowCloudfrontUsage", "Effect": "Allow", diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py index 37ce065e5f0..5de27e1dddf 100644 --- a/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py +++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py @@ -24,23 +24,27 @@ options: - Tagged name identifying a network ACL. - One and only one of the I(name) or I(nacl_id) is required. 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. required: false version_added: "2.4" + type: str vpc_id: description: - VPC id of the requesting VPC. - Required when state present. required: false + type: str subnets: description: - The list of subnets that should be associated with the network ACL. - Must be specified as a list - Each subnet can be specified as subnet ID, or its tagged name. required: false + type: list egress: description: - A list of rules for outgoing traffic. Each rule must be specified as a list. @@ -51,6 +55,7 @@ options: See examples. default: [] required: false + type: list ingress: description: - List of rules for incoming traffic. Each rule must be specified as a list. @@ -61,15 +66,18 @@ options: See examples. default: [] required: false + type: list tags: description: - Dictionary of tags to look for and apply when creating a network ACL. required: false + type: dict state: description: - Creates or modifies an existing NACL - Deletes a NACL and reassociates subnets to the default NACL required: false + type: str choices: ['present', 'absent'] default: present author: Mike Mochan (@mmochan) @@ -140,19 +148,19 @@ 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) + returned: success + type: str + sample: acl-123456789abcdef01 ''' try: import botocore - import boto3 - HAS_BOTO3 = True except ImportError: - HAS_BOTO3 = False - -import traceback -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info + pass # Handled by AnsibleAWSModule +from ansible.module_utils.aws.core import AnsibleAWSModule # VPC-supported IANA protocol numbers # http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml @@ -379,7 +387,7 @@ def create_network_acl(vpc_id, client, module): else: nacl = client.create_network_acl(VpcId=vpc_id) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) return nacl @@ -388,7 +396,7 @@ def create_network_acl_entry(params, client, module): if not module.check_mode: client.create_network_acl_entry(**params) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def create_tags(nacl_id, client, module): @@ -397,7 +405,7 @@ def create_tags(nacl_id, client, module): if not module.check_mode: client.create_tags(Resources=[nacl_id], Tags=load_tags(module)) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def delete_network_acl(nacl_id, client, module): @@ -405,7 +413,7 @@ def delete_network_acl(nacl_id, client, module): if not module.check_mode: client.delete_network_acl(NetworkAclId=nacl_id) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def delete_network_acl_entry(params, client, module): @@ -413,7 +421,7 @@ def delete_network_acl_entry(params, client, module): if not module.check_mode: client.delete_network_acl_entry(**params) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def delete_tags(nacl_id, client, module): @@ -421,7 +429,7 @@ def delete_tags(nacl_id, client, module): if not module.check_mode: client.delete_tags(Resources=[nacl_id]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def describe_acl_associations(subnets, client, module): @@ -432,7 +440,7 @@ def describe_acl_associations(subnets, client, module): {'Name': 'association.subnet-id', 'Values': subnets} ]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) associations = results['NetworkAcls'][0]['Associations'] return [a['NetworkAclAssociationId'] for a in associations if a['SubnetId'] in subnets] @@ -448,7 +456,7 @@ def describe_network_acl(client, module): {'Name': 'tag:Name', 'Values': [module.params.get('name')]} ]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) return nacl @@ -456,7 +464,7 @@ def find_acl_by_id(nacl_id, client, module): try: return client.describe_network_acls(NetworkAclIds=[nacl_id]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def find_default_vpc_nacl(vpc_id, client, module): @@ -464,7 +472,7 @@ def find_default_vpc_nacl(vpc_id, client, module): response = client.describe_network_acls(Filters=[ {'Name': 'vpc-id', 'Values': [vpc_id]}]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) nacls = response['NetworkAcls'] return [n['NetworkAclId'] for n in nacls if n['IsDefault'] is True] @@ -475,7 +483,7 @@ def find_subnet_ids_by_nacl_id(nacl_id, client, module): {'Name': 'association.network-acl-id', 'Values': [nacl_id]} ]) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) if results['NetworkAcls']: associations = results['NetworkAcls'][0]['Associations'] return [s['SubnetId'] for s in associations if s['SubnetId']] @@ -492,7 +500,7 @@ def replace_network_acl_association(nacl_id, subnets, client, module): if not module.check_mode: client.replace_network_acl_association(**params) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def replace_network_acl_entry(entries, Egress, nacl_id, client, module): @@ -503,7 +511,7 @@ def replace_network_acl_entry(entries, Egress, nacl_id, client, module): if not module.check_mode: client.replace_network_acl_entry(**params) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def restore_default_acl_association(params, client, module): @@ -511,7 +519,7 @@ def restore_default_acl_association(params, client, module): if not module.check_mode: client.replace_network_acl_association(**params) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + module.fail_json_aws(e) def subnets_to_associate(nacl, client, module): @@ -525,20 +533,19 @@ def subnets_to_associate(nacl, client, module): {'Name': 'subnet-id', 'Values': params}]) all_found.extend(subnets.get('Subnets', [])) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) + module.fail_json_aws(e) if len(params) != len(all_found): try: subnets = client.describe_subnets(Filters=[ {'Name': 'tag:Name', 'Values': params}]) all_found.extend(subnets.get('Subnets', [])) except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e), exception=traceback.format_exc()) + module.fail_json_aws(e) return list(set(s['SubnetId'] for s in all_found if s.get('SubnetId'))) def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( + argument_spec = dict( vpc_id=dict(), name=dict(), nacl_id=dict(), @@ -547,21 +554,15 @@ def main(): ingress=dict(required=False, type='list', default=list()), egress=dict(required=False, type='list', default=list()), state=dict(default='present', choices=['present', 'absent']), - ), ) - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True, - required_one_of=[['name', 'nacl_id']], - required_if=[['state', 'present', ['vpc_id']]]) + module = AnsibleAWSModule(argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=[['name', 'nacl_id']], + required_if=[['state', 'present', ['vpc_id']]]) - if not HAS_BOTO3: - module.fail_json(msg='json, botocore and boto3 are required.') state = module.params.get('state').lower() - try: - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - client = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs) - except botocore.exceptions.NoCredentialsError as e: - module.fail_json(msg="Can't authorize connection - %s" % str(e)) + + client = module.client('ec2') invocations = { "present": setup_network_acl, diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl_info.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl_info.py index 64929b9aec2..51cdbc99a44 100644 --- a/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl_info.py +++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_nacl_info.py @@ -27,6 +27,7 @@ options: required: false default: [] aliases: [nacl_id] + type: list filters: description: - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See \ @@ -34,6 +35,7 @@ options: names and values are case sensitive. required: false default: {} + type: dict notes: - By default, the module will return all Network ACLs. @@ -109,10 +111,9 @@ try: except ImportError: pass # caught by imported HAS_BOTO3 +from ansible.module_utils.aws.core import AnsibleAWSModule from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ec2 import (ec2_argument_spec, boto3_conn, get_aws_connection_info, - ansible_dict_to_boto3_filter_list, HAS_BOTO3, +from ansible.module_utils.ec2 import (ansible_dict_to_boto3_filter_list, camel_dict_to_snake_dict, boto3_tag_list_to_ansible_dict) @@ -132,11 +133,9 @@ def list_ec2_vpc_nacls(connection, module): try: nacls = connection.describe_network_acls(NetworkAclIds=nacl_ids, Filters=filters) except ClientError as e: - module.fail_json(msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e)), - exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) + module.fail_json_aws(e, msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e))) except BotoCoreError as e: - module.fail_json(msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e)), - exception=traceback.format_exc()) + module.fail_json_aws(e, msg="Unable to describe network ACLs {0}: {1}".format(nacl_ids, to_native(e))) # Turn the boto3 result in to ansible_friendly_snaked_names snaked_nacls = [] @@ -203,24 +202,15 @@ def nacl_entry_to_list(entry): def main(): - argument_spec = ec2_argument_spec() - argument_spec.update( - dict( - nacl_ids=dict(default=[], type='list', aliases=['nacl_id']), - filters=dict(default={}, type='dict') - ) - ) + argument_spec = dict( + nacl_ids=dict(default=[], type='list', aliases=['nacl_id']), + filters=dict(default={}, type='dict')) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) if module._name == 'ec2_vpc_nacl_facts': module.deprecate("The 'ec2_vpc_nacl_facts' module has been renamed to 'ec2_vpc_nacl_info'", version='2.13') - if not HAS_BOTO3: - module.fail_json(msg='boto3 required for this module') - - region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) - connection = boto3_conn(module, conn_type='client', resource='ec2', - region=region, endpoint=ec2_url, **aws_connect_params) + connection = module.client('ec2') list_ec2_vpc_nacls(connection, module) diff --git a/test/integration/targets/ec2_vpc_nacl/aliases b/test/integration/targets/ec2_vpc_nacl/aliases new file mode 100644 index 00000000000..074f2ab60c1 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/aliases @@ -0,0 +1,3 @@ +ec2_vpc_nacl_info +cloud/aws +shippable/aws/group2 diff --git a/test/integration/targets/ec2_vpc_nacl/meta/main.yml b/test/integration/targets/ec2_vpc_nacl/meta/main.yml new file mode 100644 index 00000000000..1f64f1169a9 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml b/test/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml new file mode 100644 index 00000000000..4eb60791290 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/ingress_and_egress.yml @@ -0,0 +1,162 @@ +# ============================================================ + +- name: create ingress and egress rules using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + +# ============================================================ + +- name: remove an ingress rule + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 1 + +# ============================================================ + +- name: remove the egress rule + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: [] + state: 'present' + register: nacl + +- name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 0 + +# ============================================================ + +- name: add egress rules + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + egress: + - [100, 'tcp', 'allow', '10.0.0.0/24', null, null, 22, 22] + - [200, 'udp', 'allow', '10.0.0.0/24', null, null, 22, 22] + state: 'present' + register: nacl + +- name: assert the network acl changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].ingress | length == 2 + - nacl_facts.nacls[0].egress | length == 2 + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: nacl + until: nacl is success + ignore_errors: yes + retries: 5 + delay: 5 + +- name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml b/test/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml new file mode 100644 index 00000000000..16b3a5aaaff --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/ipv6.yml @@ -0,0 +1,178 @@ +- block: + - name: create a VPC + ec2_vpc_net: + cidr_block: 10.230.231.0/24 + name: "{{ resource_prefix }}-ipv6" + state: present + ipv6_cidr: yes + register: vpc_result + + - set_fact: + vpc_ipv6_cidr: "{{ vpc_result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}" + + # ============================================================ + - name: create subnet with IPv6 (expected changed=true) + ec2_vpc_subnet: + cidr: 10.230.231.0/26 + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ vpc_ipv6_cidr | regex_replace('::/56', '::/64') }}" + state: present + tags: + Name: "{{ resource_prefix }}-ipv6-subnet-1" + register: vpc_subnet_ipv6 + + - name: assert creation with IPv6 happened (expected changed=true) + assert: + that: + - "vpc_subnet_ipv6.subnet.ipv6_cidr_block == '{{ vpc_ipv6_cidr | regex_replace('::/56', '::/64') }}'" + + # ============================================================ + + - name: create ingress and egress rules using subnet names + ec2_vpc_nacl: + vpc_id: "{{ vpc_result.vpc.id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ resource_prefix }}-ipv6-subnet-1" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + - assert: + that: + - nacl.nacl_id + + - set_fact: + nacl_id: "{{ nacl.nacl_id }}" + + - name: add ipv6 entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_result.vpc.id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ resource_prefix }}-ipv6-subnet-1" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [205, 'ipv6-tcp', 'allow', '::/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + - [305, 'ipv6-icmp', 'allow', '::/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + - [105, 'all', 'allow', '::/0', null, null, null, null] + state: 'present' + register: nacl + # FIXME: Currently IPv6 rules are not supported - uncomment assertion when + # fixed (and add some nacl_info tests) + ignore_errors: yes + - name: get network ACL facts (test that it works with ipv6 entries) + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + + + #- assert: + # that: + # - nacl.changed + # - nacl.nacl_id == nacl_id + + - name: purge ingress entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_result.vpc.id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ resource_prefix }}-ipv6-subnet-1" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: [] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + - [105, 'all', 'allow', '::/0', null, null, null, null] + state: 'present' + register: nacl + # FIXME: Currently IPv6 rules are not supported - uncomment assertion when + # fixed (and add some nacl_info tests) + ignore_errors: yes + + #- assert: + # that: + # - nacl.changed + # - nacl.nacl_id == nacl_id + + - name: purge egress entries + ec2_vpc_nacl: + vpc_id: "{{ vpc_result.vpc.id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ resource_prefix }}-ipv6-subnet-1" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: [] + egress: [] + state: 'present' + register: nacl + + - assert: + that: + - nacl.changed + + # ============================================================ + - name: remove subnet ipv6 cidr (expected changed=true) + ec2_vpc_subnet: + cidr: 10.230.231.0/26 + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + register: vpc_remove_ipv6_cidr + + - name: assert subnet ipv6 cidr removed (expected changed=true) + assert: + that: + - 'vpc_remove_ipv6_cidr.changed' + + always: + + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: remove network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_result.vpc.id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: removed_acl + until: removed_acl is success + retries: 5 + delay: 5 + ignore_errors: yes + + - name: tidy up subnet + ec2_vpc_subnet: + cidr: 10.230.231.0/26 + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + register: removed_subnet + until: removed_subnet is success + retries: 5 + delay: 5 + ignore_errors: yes + + - name: tidy up VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-ipv6" + state: absent + cidr_block: 10.230.231.0/24 + register: removed_vpc + until: removed_vpc is success + retries: 5 + delay: 5 + ignore_errors: yes diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/main.yml b/test/integration/targets/ec2_vpc_nacl/tasks/main.yml new file mode 100644 index 00000000000..ad72530e290 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/main.yml @@ -0,0 +1,170 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + # ============================================================ + + - name: test without any parameters + ec2_vpc_nacl: + register: result + ignore_errors: yes + + - name: assert required parameters + 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: + register: nacl_facts + + - name: assert we don't error + assert: + that: + - nacl_facts is succeeded + + - name: get network ACL info with invalid ID + ec2_vpc_nacl_info: + nacl_ids: + - 'acl-000000000000' + register: nacl_facts + ignore_errors: yes + + - name: assert message mentions missing ACLs + assert: + that: + - nacl_facts is failed + - '"does not exist" in nacl_facts.msg' + + # ============================================================ + + - name: fetch AZ availability + aws_az_info: + register: az_info + + - name: Assert that we have multiple AZs available to us + assert: + that: az_info.availability_zones | length >= 2 + + - name: pick AZs + 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: + cidr_block: 10.230.230.0/24 + name: "{{ resource_prefix }}" + state: present + register: vpc + + - name: create subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ item.az }}" + vpc_id: "{{ vpc.vpc.id }}" + state: present + tags: + Name: "{{ item.name }}" + with_items: + - cidr: 10.230.230.0/26 + az: "{{ az_one }}" + name: "{{ resource_prefix }}-subnet-1" + - cidr: 10.230.230.64/26 + az: "{{ az_two }}" + name: "{{ resource_prefix }}-subnet-2" + - cidr: 10.230.230.128/26 + az: "{{ az_one }}" + name: "{{ resource_prefix }}-subnet-3" + - cidr: 10.230.230.192/26 + az: "{{ az_two }}" + name: "{{ resource_prefix }}-subnet-4" + register: subnets + + # ============================================================ + + - include_tasks: tasks/subnet_ids.yml + vars: + vpc_id: "{{ vpc.vpc.id }}" + subnet_ids: "{{ subnets | json_query('results[*].subnet.id') }}" + + - include_tasks: tasks/subnet_names.yml + vars: + vpc_id: "{{ vpc.vpc.id }}" + subnet_names: "{{ subnets | json_query('results[*].subnet.tags.Name') }}" + + - include_tasks: tasks/tags.yml + vars: + vpc_id: "{{ vpc.vpc.id }}" + subnet_ids: "{{ subnets | json_query('results[*].subnet.id') }}" + + - include_tasks: tasks/ingress_and_egress.yml + vars: + vpc_id: "{{ vpc.vpc.id }}" + subnet_ids: "{{ subnets | json_query('results[*].subnet.id') }}" + + - include_tasks: tasks/ipv6.yml + + # ============================================================ + + always: + + - name: remove network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc.vpc.id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: removed_acl + until: removed_acl is success + retries: 5 + delay: 5 + ignore_errors: yes + + - name: remove subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ aws_region}}{{ item.az }}" + vpc_id: "{{ vpc.vpc.id }}" + state: absent + tags: + Public: "{{ item.public | string }}" + Name: "{{ item.public | ternary('public', 'private') }}-{{ item.az }}" + with_items: + - cidr: 10.230.230.0/26 + az: "a" + public: "True" + - cidr: 10.230.230.64/26 + az: "b" + public: "True" + - cidr: 10.230.230.128/26 + az: "a" + public: "False" + - cidr: 10.230.230.192/26 + az: "b" + public: "False" + ignore_errors: yes + register: removed_subnets + until: removed_subnets is success + retries: 5 + delay: 5 + + - name: remove the VPC + ec2_vpc_net: + cidr_block: 10.230.230.0/24 + name: "{{ resource_prefix }}" + state: absent + ignore_errors: yes + register: removed_vpc + until: removed_vpc is success + retries: 5 + delay: 5 + + # ============================================================ diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml b/test/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml new file mode 100644 index 00000000000..cd4b0c55172 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/subnet_ids.yml @@ -0,0 +1,142 @@ +# ============================================================ + +- name: create ingress and egress rules using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- set_fact: + nacl_id: "{{ nacl.nacl_id }}" + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 4 + - nacl_facts.nacls[0].subnets | sort == subnet_ids | sort + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: test idempotence + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl already existed + assert: + that: + - not nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts_idem + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts_idem == nacl_facts + +# ============================================================ + +- name: remove a subnet from the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ subnet_ids[0] }}" + - "{{ subnet_ids[1] }}" + - "{{ subnet_ids[2] }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network ACL changed + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + - nacl.nacl_id == nacl_id + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_id: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 3 + - subnet_ids[3] not in nacl_facts.nacls[0].subnets + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: nacl + until: nacl is success + ignore_errors: yes + retries: 5 + delay: 5 + +- name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml b/test/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml new file mode 100644 index 00000000000..5a4db04df92 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/subnet_names.yml @@ -0,0 +1,140 @@ +# ============================================================ + +- name: create ingress and egress rules using subnet names + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_names }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- set_fact: + nacl_id: "{{ nacl.nacl_id }}" + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 4 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: test idempotence + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_names }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network acl already existed + assert: + that: + - not nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts_idem + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts_idem == nacl_facts + +# ============================================================ + +- name: remove a subnet from the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: + - "{{ subnet_names[0] }}" + - "{{ subnet_names[1] }}" + - "{{ subnet_names[2] }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + ingress: + - [100, 'tcp', 'allow', '0.0.0.0/0', null, null, 22, 22] + - [200, 'tcp', 'allow', '0.0.0.0/0', null, null, 80, 80] + - [300, 'icmp', 'allow', '0.0.0.0/0', 0, 8] + egress: + - [100, 'all', 'allow', '0.0.0.0/0', null, null, null, null] + state: 'present' + register: nacl + +- name: assert the network ACL changed + assert: + that: + - nacl.changed + - nacl.nacl_id == nacl_id + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].nacl_id == nacl_id + - nacl_facts.nacls[0].subnets | length == 3 + - nacl_facts.nacls[0].ingress | length == 3 + - nacl_facts.nacls[0].egress | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: nacl + until: nacl is success + ignore_errors: yes + retries: 5 + delay: 5 + +- name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/test/integration/targets/ec2_vpc_nacl/tasks/tags.yml b/test/integration/targets/ec2_vpc_nacl/tasks/tags.yml new file mode 100644 index 00000000000..f7847850a58 --- /dev/null +++ b/test/integration/targets/ec2_vpc_nacl/tasks/tags.yml @@ -0,0 +1,117 @@ +# ============================================================ + +- name: create a network ACL using subnet IDs + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + state: 'present' + register: nacl + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls[0].tags | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: add a tag + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + tags: + Created_by: "Ansible test {{ resource_prefix }}" + state: 'present' + register: nacl + +- name: assert the network acl changed + assert: + that: + - nacl.changed + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts.nacls[0].tags | length == 2 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + - "'{{ nacl_facts.nacls[0].tags.Created_by }}' == 'Ansible test {{ resource_prefix }}'" + +- name: get network ACL facts by filter + ec2_vpc_nacl_info: + filters: + "tag:Created_by": "Ansible test {{ resource_prefix }}" + register: nacl_facts + +- name: assert the facts are the same as before + assert: + that: + - nacl_facts.nacls | length == 1 + - nacl_facts.nacls[0].tags | length == 2 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + - "'{{ nacl_facts.nacls[0].tags.Created_by }}' == 'Ansible test {{ resource_prefix }}'" + +# ============================================================ + +- name: remove a tag + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + subnets: "{{ subnet_ids }}" + state: 'present' + register: nacl + +- name: assert the network acl was created + assert: + that: + - nacl.changed + - nacl.nacl_id.startswith('acl-') + +- name: get network ACL facts + ec2_vpc_nacl_info: + nacl_ids: + - "{{ nacl.nacl_id }}" + register: nacl_facts + +- name: assert the nacl has the correct attributes + assert: + that: + - nacl_facts.nacls[0].tags | length == 1 + - "'{{ nacl_facts.nacls[0].tags.Name }}' == '{{ resource_prefix }}-acl'" + +# ============================================================ + +- name: remove the network ACL + ec2_vpc_nacl: + vpc_id: "{{ vpc_id }}" + name: "{{ resource_prefix }}-acl" + state: absent + register: nacl + until: nacl is success + ignore_errors: yes + retries: 5 + delay: 5 + +- name: assert nacl was removed + assert: + that: + - nacl.changed diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 26c2b7bd59f..688eaf0ad0f 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -818,9 +818,6 @@ lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_info.py validate-modules:doc-m lib/ansible/modules/cloud/amazon/ec2_vpc_igw.py validate-modules:parameter-type-not-in-doc lib/ansible/modules/cloud/amazon/ec2_vpc_igw.py validate-modules:doc-missing-type lib/ansible/modules/cloud/amazon/ec2_vpc_igw_info.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py validate-modules:parameter-type-not-in-doc -lib/ansible/modules/cloud/amazon/ec2_vpc_nacl.py validate-modules:doc-missing-type -lib/ansible/modules/cloud/amazon/ec2_vpc_nacl_info.py validate-modules:parameter-type-not-in-doc lib/ansible/modules/cloud/amazon/ec2_vpc_nat_gateway.py pylint:blacklisted-name lib/ansible/modules/cloud/amazon/ec2_vpc_nat_gateway.py validate-modules:doc-default-does-not-match-spec lib/ansible/modules/cloud/amazon/ec2_vpc_nat_gateway.py validate-modules:parameter-type-not-in-doc