---
# Runs a set of tets without the AWS connection credentials configured
- include: ./credential_tests.yml
- set_fact:
    aws_security_token: '{{ security_token | default("") }}'
  no_log: True

# ============================================================
# EC2 Classic tests can only be run on a pre-2013 AWS account with supported-platforms=EC2
# Ansible CI does NOT have classic EC2 support; these tests are provided as-is for the
# community and can be run if you have access to a classic account.  To check if your account
# has support for EC2 Classic you can use the `amazon.aws.aws_account_attribute` plugin.

- name: determine if this is an EC2 Classic account
  set_fact:
    has_ec2_classic: "{{ lookup('amazon.aws.aws_account_attribute',
                                attribute='has-ec2-classic',
                                region=aws_region,
                                aws_access_key=aws_access_key,
                                aws_secret_key=aws_secret_key,
                                aws_security_token=aws_security_token,
                                wantlist=True) }}"

# ============================================================
- name: Run EC2 Classic accounts if account type is EC2
  include: ./ec2_classic.yml
  when: has_ec2_classic

# ============================================================
# Other tests depend on attribute='default-vpc', ie no vpc_id is set. This is
# incompatible with EC2 classic accounts, so these tests can only be run in a
# VPC-type account. See "Q. I really want a default VPC for my existing EC2
# account. Is that possible?" in https://aws.amazon.com/vpc/faqs/#Default_VPCs
- name: Run all other tests if account type is VPC
  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: determine if there is a default VPC
      set_fact:
        defaultvpc: "{{ lookup('amazon.aws.aws_account_attribute',
                              attribute='default-vpc',
                              region=aws_region,
                              aws_access_key=aws_access_key,
                              aws_secret_key=aws_secret_key,
                              aws_security_token=aws_security_token) }}"
      register: default_vpc

    - name: create a VPC
      ec2_vpc_net:
        name: "{{ resource_prefix }}-vpc"
        state: present
        cidr_block: "{{ vpc_cidr }}"
        tags:
          Name: "{{ resource_prefix }}-vpc"
          Description: "Created by ansible-test"
      register: vpc_result
    #TODO(ryansb): Update CI for VPC peering permissions
    #- include: ./multi_account.yml
    - include: ./diff_mode.yml
    - include: ./numeric_protos.yml
    - include: ./rule_group_create.yml
    - include: ./egress_tests.yml
    - include: ./data_validation.yml
    - include: ./multi_nested_target.yml
    - include: ./group_info.yml

    # ============================================================
    - name: test state=absent (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: absent
      check_mode: true
      register: result

    - name: assert no changes would be made
      assert:
        that:
          - not result.changed

    # ===========================================================
    - name: test state=absent
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: absent
      register: result

    # ============================================================
    - name: test state=present (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'

    # ============================================================
    - name: test state=present (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'
           - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test state=present different description (expected changed=false) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}CHANGED'
        state: present
      check_mode: true
      register: result

    - name: assert state=present (expected changed=false)
      assert:
        that:
           - 'not result.changed'

    # ============================================================
    - name: test state=present different description (expected changed=false)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}CHANGED'
        state: present
      ignore_errors: true
      register: result

    - name: assert state=present (expected changed=false)
      assert:
        that:
           - 'not result.changed'
           - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test state=present (expected changed=false)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
      register: result

    - name: assert state=present (expected changed=false)
      assert:
        that:
           - 'not result.changed'
           - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: tests IPv6 with the default VPC
      include: ./ipv6_default_tests.yml
      when: default_vpc

    - name: test IPv6 with a specified VPC
      block:

        # ============================================================
        - name: test state=present (expected changed=true) (CHECK MODE)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
          check_mode: true
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'

        # ============================================================
        - name: test state=present (expected changed=true)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'
               - 'result.group_id.startswith("sg-")'

        # ============================================================
        - name: test state=present for ipv6 (expected changed=true) (CHECK MODE)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
          check_mode: true
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'

        # ============================================================
        - name: test state=present for ipv6 (expected changed=true)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'
               - 'result.group_id.startswith("sg-")'

        # ============================================================
        - name: test state=present for ipv6 (expected changed=false) (CHECK MODE)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
          check_mode: true
          register: result

        - name: assert nothing changed
          assert:
            that:
              - 'not result.changed'

        # ============================================================
        - name: test state=present for ipv6 (expected changed=false)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
          register: result

        - name: assert nothing changed
          assert:
            that:
              - 'not result.changed'

        # ============================================================
        - name: test rules_egress state=present for ipv6 (expected changed=true) (CHECK MODE)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
            rules_egress:
            - proto: "tcp"
              from_port: 8181
              to_port: 8181
              cidr_ipv6: "64:ff9b::/96"
          check_mode: true
          diff: true
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'
               - 'result.diff.0.before.ip_permissions == result.diff.0.after.ip_permissions'
               - 'result.diff.0.before.ip_permissions_egress != result.diff.0.after.ip_permissions_egress'

        # ============================================================
        - name: test rules_egress state=present for ipv6 (expected changed=true)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: present
            vpc_id: '{{ vpc_result.vpc.id }}'
            rules:
            - proto: "tcp"
              from_port: 8182
              to_port: 8182
              cidr_ipv6: "64:ff9b::/96"
            rules_egress:
            - proto: "tcp"
              from_port: 8181
              to_port: 8181
              cidr_ipv6: "64:ff9b::/96"
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
               - 'result.changed'
               - 'result.group_id.startswith("sg-")'

        # ============================================================
        - name: test state=absent (expected changed=true) (CHECK MODE)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: absent
            vpc_id: '{{ vpc_result.vpc.id }}'
          check_mode: true
          diff: true
          register: result

        - name: assert group was removed
          assert:
            that:
              - 'result.changed'
              - 'not result.diff.0.after'

        # ============================================================
        - name: test state=absent (expected changed=true)
          ec2_group:
            name: '{{ ec2_group_name }}-2'
            description: '{{ ec2_group_description }}-2'
            state: absent
            vpc_id: '{{ vpc_result.vpc.id }}'
          register: result

        - name: assert group was removed
          assert:
            that:
              - 'result.changed'

    # ============================================================
    - name: test state=present for ipv4 (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
        - 'result.changed'

    # ============================================================
    - name: test state=present for ipv4 (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
        - 'result.changed'
        - 'result.group_id.startswith("sg-")'
        - 'result.ip_permissions|length == 1'
        - 'result.ip_permissions_egress|length == 1'

    # ============================================================
    - name: add same rule to the existing group  (expected changed=false) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      check_mode: true
      diff: true
      register: check_result

    - assert:
        that:
          - not check_result.changed
          - check_result.diff.0.before.ip_permissions.0 == check_result.diff.0.after.ip_permissions.0

    # ============================================================
    - name: add same rule to the existing group  (expected changed=false)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      register: result

    - name: assert state=present (expected changed=false)
      assert:
        that:
          - 'not result.changed'
          - 'result.group_id.startswith("sg-")'

    - name: assert state=present (expected changed=false)
      assert:
        that:
          - 'not check_result.changed'

    # ============================================================
    - name: add a rule that auto creates another security group (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        purge_rules: no
        rules:
        - proto: "tcp"
          group_name: "{{ resource_prefix }} - Another security group"
          group_desc: Another security group
          ports: 7171
      check_mode: true
      register: result

    - name: check that there are now two rules
      assert:
        that:
          - result.changed

    # ============================================================
    - name: add a rule that auto creates another security group
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        purge_rules: no
        rules:
        - proto: "tcp"
          group_name: "{{ resource_prefix }} - Another security group"
          group_desc: Another security group
          ports: 7171
      register: result

    - name: check that there are now two rules
      assert:
        that:
          - result.changed
          - result.warning is not defined
          - result.ip_permissions|length == 2
          - result.ip_permissions[0].user_id_group_pairs or
            result.ip_permissions[1].user_id_group_pairs
          - 'result.ip_permissions_egress[0].ip_protocol == "-1"'

    # ============================================================
    - name: test ip rules convert port numbers from string to int (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: "8183"
          to_port: "8183"
          cidr_ip: "10.1.1.1/32"
        rules_egress:
        - proto: "tcp"
          from_port: "8184"
          to_port: "8184"
          cidr_ip: "10.1.1.1/32"
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'

    # ============================================================
    - name: test ip rules convert port numbers from string to int (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: "8183"
          to_port: "8183"
          cidr_ip: "10.1.1.1/32"
        rules_egress:
        - proto: "tcp"
          from_port: "8184"
          to_port: "8184"
          cidr_ip: "10.1.1.1/32"
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'
           - 'result.group_id.startswith("sg-")'
           - 'result.ip_permissions|length == 1'
           - 'result.ip_permissions_egress[0].ip_protocol == "tcp"'


    # ============================================================
    - name: test group rules convert port numbers from string to int (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: "8185"
          to_port: "8185"
          group_id: "{{result.group_id}}"
        rules_egress:
        - proto: "tcp"
          from_port: "8186"
          to_port: "8186"
          group_id: "{{result.group_id}}"
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'

    # ============================================================
    - name: test group rules convert port numbers from string to int (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        rules:
        - proto: "tcp"
          from_port: "8185"
          to_port: "8185"
          group_id: "{{result.group_id}}"
        rules_egress:
        - proto: "tcp"
          from_port: "8186"
          to_port: "8186"
          group_id: "{{result.group_id}}"
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
           - 'result.changed'
           - 'result.group_id.startswith("sg-")'
           - result.warning is not defined

    # ============================================================
    - name: test adding a range of ports and ports given as strings (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8183-8190
            - '8192'
          cidr_ip: 10.1.1.1/32
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'

    # ============================================================
    - name: test adding a range of ports and ports given as strings (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8183-8190
            - '8192'
          cidr_ip: 10.1.1.1/32
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test adding a rule with a IPv4 CIDR with host bits set (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8195
          cidr_ip: 10.0.0.1/8
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'

    # ============================================================
    - name: test adding a rule with a IPv4 CIDR with host bits set (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8195
          cidr_ip: 10.0.0.1/8
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test adding the same rule with a IPv4 CIDR with host bits set (expected changed=false) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8195
          cidr_ip: 10.0.0.1/8
      check_mode: true
      register: check_result

    # ============================================================
    - name: test adding the same rule with a IPv4 CIDR with host bits set (expected changed=false and a warning)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        state: present
        # set purge_rules to false so we don't get a false positive from previously added rules
        purge_rules: false
        rules:
        - proto: "tcp"
          ports:
            - 8195
          cidr_ip: 10.0.0.1/8
      register: result

    - name: assert state=present (expected changed=false and a warning)
      assert:
        that:
          - 'not check_result.changed'

    - name: assert state=present (expected changed=false and a warning)
      assert:
        that:
          # No way to assert for warnings?
          - 'not result.changed'
          - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test using the default VPC
      block:

        - name: test adding a rule with a IPv6 CIDR with host bits set (expected changed=true) (CHECK MODE)
          ec2_group:
            name: '{{ec2_group_name}}'
            description: '{{ec2_group_description}}'
            state: present
            # set purge_rules to false so we don't get a false positive from previously added rules
            purge_rules: false
            rules:
            - proto: "tcp"
              ports:
                - 8196
              cidr_ipv6: '2001:db00::1/24'
          check_mode: true
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
              - 'result.changed'

        # ============================================================
        - name: test adding a rule with a IPv6 CIDR with host bits set (expected changed=true)
          ec2_group:
            name: '{{ec2_group_name}}'
            description: '{{ec2_group_description}}'
            state: present
            # set purge_rules to false so we don't get a false positive from previously added rules
            purge_rules: false
            rules:
            - proto: "tcp"
              ports:
                - 8196
              cidr_ipv6: '2001:db00::1/24'
          register: result

        - name: assert state=present (expected changed=true)
          assert:
            that:
              - 'result.changed'
              - 'result.group_id.startswith("sg-")'

        # ============================================================

        - name: test adding a rule again with a IPv6 CIDR with host bits set (expected changed=false and a warning)
          ec2_group:
            name: '{{ec2_group_name}}'
            description: '{{ec2_group_description}}'
            state: present
            # set purge_rules to false so we don't get a false positive from previously added rules
            purge_rules: false
            rules:
            - proto: "tcp"
              ports:
                - 8196
              cidr_ipv6: '2001:db00::1/24'
          register: result

        - name: assert state=present (expected changed=false and a warning)
          assert:
            that:
              # No way to assert for warnings?
              - 'not result.changed'
              - 'result.group_id.startswith("sg-")'

      when: default_vpc

    # ============================================================
    - name: test state=absent (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        state: absent
      check_mode: true
      register: result

    - name: assert state=absent (expected changed=true)
      assert:
        that:
           - 'result.changed'

    # ============================================================
    - name: test state=absent (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        state: absent
      register: result

    - name: assert state=absent (expected changed=true)
      assert:
        that:
           - 'result.changed'
           - 'not result.group_id'

    # ============================================================
    - name: create security group in the VPC (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      check_mode: true
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'

    # ============================================================
    - name: create security group in the VPC
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      register: result

    - name: assert state=present (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'result.vpc_id == vpc_result.vpc.id'
          - 'result.group_id.startswith("sg-")'

    # ============================================================
    - name: test adding tags (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
          tag2: test2
      check_mode: true
      diff: true
      register: result

    - name: assert that tags were added (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'not result.diff.0.before.tags'
          - 'result.diff.0.after.tags.tag1 == "test1"'
          - 'result.diff.0.after.tags.tag2 == "test2"'

    # ============================================================
    - name: test adding tags (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
          tag2: test2
      register: result

    - name: assert that tags were added (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'result.tags == {"tag1": "test1", "tag2": "test2"}'

    # ============================================================
    - name: test that tags are present (expected changed=False) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        purge_rules_egress: false
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
          tag2: test2
      check_mode: true
      register: result

    - name: assert that tags were not changed (expected changed=False)
      assert:
        that:
          - 'not result.changed'

    # ============================================================
    - name: test that tags are present (expected changed=False)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        purge_rules_egress: false
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
          tag2: test2
      register: result

    - name: assert that tags were not changed (expected changed=False)
      assert:
        that:
          - 'not result.changed'
          - 'result.tags == {"tag1": "test1", "tag2": "test2"}'

    # ============================================================
    - name: test purging tags (expected changed=True) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
      check_mode: true
      register: result

    - name: assert that tag2 was removed (expected changed=true)
      assert:
        that:
          - 'result.changed'

    # ============================================================
    - name: test purging tags (expected changed=True)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags:
          tag1: test1
      register: result

    - name: assert that tag2 was removed (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'result.tags == {"tag1": "test1"}'

    # ============================================================

    - name: assert that tags are left as-is if not specified (expected changed=False)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
      register: result

    - name: assert that the tags stayed the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
          - 'result.tags == {"tag1": "test1"}'

    # ============================================================

    - name: test purging all tags (expected changed=True)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        state: present
        rules:
        - proto: "tcp"
          from_port: 8182
          to_port: 8182
          cidr_ip: "10.1.1.1/32"
        tags: {}
      register: result

    - name: assert that tag1 was removed (expected changed=true)
      assert:
        that:
          - 'result.changed'
          - 'not result.tags'

    # ============================================================
    - name: test adding a rule and egress rule descriptions (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        # purge the other rules so assertions work for the subsequent tests for rule descriptions
        purge_rules_egress: true
        purge_rules: true
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 1
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 1
      check_mode: true
      register: result

    - name: assert that rule descriptions are created (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed changes should still have changed due to purged rules (expected changed=true)
      assert:
        that:
          - 'result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # =========================================================================================
    - name: add rules without descriptions ready for adding descriptions to existing rules
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        # purge the other rules so assertions work for the subsequent tests for rule descriptions
        purge_rules_egress: true
        purge_rules: true
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
      register: result

    # ============================================================
    - name: test adding a rule and egress rule descriptions (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        # purge the other rules so assertions work for the subsequent tests for rule descriptions
        purge_rules_egress: true
        purge_rules: true
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 1
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 1
      register: result

    - name: assert that rule descriptions are created (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.changed'
          - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 1"'
          - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 1"'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed changes should still have changed due to purged rules (expected changed=true)
      assert:
        that:
          - 'result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # ============================================================
    - name: test modifying rule and egress rule descriptions (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 2
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 2
      check_mode: true
      register: result

    - name: assert that rule descriptions were modified (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.ip_permissions | length > 0'
          - 'result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined and result.ip_permissions_egress[1].ip_ranges[0].description is undefined

    # ============================================================
    - name: test modifying rule and egress rule descriptions (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 2
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 2
      register: result

    - name: assert that rule descriptions were modified (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.changed'
          - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 2"'
          - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 2"'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # ============================================================

    - name: test creating rule in default vpc with egress rule (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}-default-vpc'
        description: '{{ec2_group_description}} default VPC'
        purge_rules_egress: true
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ip: 10.1.1.1/24
          rule_desc: ipv4 rule desc
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 2
      register: result

    - name: assert that rule descriptions were modified (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.changed'
          - 'result.ip_permissions_egress|length == 1'

    # ============================================================
    - name: test that keeping the same rule descriptions (expected changed=false) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 2
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 2
      check_mode: true
      register: result

    - name: assert that rule descriptions stayed the same (expected changed=false)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # ============================================================
    - name: test that keeping the same rule descriptions (expected changed=false)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc: ipv6 rule desc 2
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc: egress rule desc 2
      register: result

    - name: assert that rule descriptions stayed the same (expected changed=false)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'not result.changed'
          - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 2"'
          - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 2"'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # ============================================================
    - name: test removing rule descriptions (expected changed=true) (CHECK MODE)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc:
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc:
      check_mode: true
      register: result

    - name: assert that rule descriptions were removed (expected changed=true)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is defined

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined

    # ============================================================
    - name: test removing rule descriptions (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        description: '{{ec2_group_description}}'
        vpc_id: '{{ vpc_result.vpc.id }}'
        purge_rules_egress: false
        purge_rules: false
        state: present
        rules:
        - proto: "tcp"
          ports:
          - 8281
          cidr_ipv6: 1001:d00::/24
          rule_desc:
        rules_egress:
        - proto: "tcp"
          ports:
          - 8282
          cidr_ip: 10.2.2.2/32
          rule_desc:
      register: result
      ignore_errors: true

    - name: assert that rule descriptions were removed (expected changed=true with newer botocore)
      # Only assert this if rule description is defined as the botocore version may < 1.7.2.
      # It's still helpful to have these tests run on older versions since it verifies backwards
      # compatibility with this feature.
      assert:
        that:
          - 'result.ip_permissions[0].ipv6_ranges[0].description is undefined'
          - 'result.ip_permissions_egress[0].ip_ranges[0].description is undefined'
      when: result is changed

    - name: if an older version of botocore is installed everything should stay the same (expected changed=false)
      assert:
        that:
          - 'not result.changed'
      when: result.failed

    # ============================================================

    - name: test state=absent (expected changed=true)
      ec2_group:
        name: '{{ec2_group_name}}'
        state: absent
      register: result

    - name: assert state=absent (expected changed=true)
      assert:
        that:
           - 'result.changed'
           - 'not result.group_id'
  when: not has_ec2_classic

  always:
    # ============================================================
    - name: tidy up security group
      ec2_group:
        name: '{{ec2_group_name}}'
        state: absent
      ignore_errors: yes

    - name: tidy up security group for IPv6 EC2-Classic tests
      ec2_group:
        name: '{{ ec2_group_name }}-2'
        state: absent
      ignore_errors: yes

    - name: tidy up default VPC security group
      ec2_group:
        name: '{{ec2_group_name}}-default-vpc'
        state: absent
      ignore_errors: yes

    - name: tidy up automatically created SG
      ec2_group:
        name: "{{ resource_prefix }} - Another security group"
        state: absent
      ignore_errors: yes

    - name: tidy up VPC
      ec2_vpc_net:
        name: "{{ resource_prefix }}-vpc"
        state: absent
        cidr_block: "{{ vpc_cidr }}"
      ignore_errors: yes
