Repository: ansible-collections/amazon.aws

Issue:
Inconsistency in parameters parsing for the ec2_group module when with ICMP rules
### Summary

Current `amazon.aws.ec2_group` module supports a variety of IP protocols. When writing rules for ICMP `from_port` and `to_port` parameters have different meaning than when using TCP or UDP rules.

This behavior aligns with current [Boto3 implementation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.SecurityGroup.authorize_ingress) for which payload does not distinguish between a port range and ICMP code and type:

> FromPort (integer) --
> The start of port range for the TCP and UDP protocols, or an ICMP type number. For the ICMP type number, use -1 to specify all types. If you specify all ICMP types, you must specify all codes.
> Alternatively, use a set of IP permissions to specify multiple rules and a description for the rule.
> GroupName (string) -- [EC2-Classic, default VPC] The name of the security group. You must specify either the security group ID or the security group name in the request.
> IpPermissions (list) --
> 
> ToPort (integer) --
> The end of port range for the TCP and UDP protocols, or an ICMP/ICMPv6 code. A value of -1 indicates all ICMP/ICMPv6 codes. If you specify all ICMP/ICMPv6 types, you must specify all codes.

I suggest to create supplementary parameters to decouple usage of the module from the payload that the method expects.
This would involve creating a set of two new parameters `icmp_code` and `icmp_type` that would be mutually exclusive with `from_port` and `to_port` parameters.

```yaml
      - proto: tcp
        from_port: 3306
        to_port: 3306
        group_id: 123412341234/sg-87654321/exact-name-of-sg
      - proto: udp
        from_port: 10050
        to_port: 10050
        cidr_ip: 10.0.0.0/8
      - proto: icmp
        icmp_code: 8      # New parameter
        icmp_type:  -1    # New parameter
        cidr_ip: 10.0.0.0/8
```
The usage of `from_port` and `to_port` would fail if `proto` reads `icmp` and vice versa, Ansible would complain if the `icmp_port` and `icmp_type` are used without a `icmp` or `icmpv6`.

##
Regarding the new `ports` key instead, there may be two solutions:
1. Updating the documentation to make it clear that the list defines ranges for TCP and UDP protocols and and ICMP port range and type.
2. Make this parameter exclusive to TCP and UDP protocols.

This change would align the semantics of the module with Ansible's philosophy, that is, using mutually exclusive parameters (for instance `instance_name` and `instance_id`) instead of basing the behavior on the module based on the _value_ of a parameter.
The change would not change the payload being sent, only create a "superset" for consistency.


### Issue Type

Feature Idea

### Component Name

ec2_group

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
Inconsistency in parameters parsing for the ec2_group module when with ICMP rules
### Summary

Current `amazon.aws.ec2_group` module supports a variety of IP protocols. When writing rules for ICMP `from_port` and `to_port` parameters have different meaning than when using TCP or UDP rules.

This behavior aligns with current [Boto3 implementation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.SecurityGroup.authorize_ingress) for which payload does not distinguish between a port range and ICMP code and type:

> FromPort (integer) --
> The start of port range for the TCP and UDP protocols, or an ICMP type number. For the ICMP type number, use -1 to specify all types. If you specify all ICMP types, you must specify all codes.
> Alternatively, use a set of IP permissions to specify multiple rules and a description for the rule.
> GroupName (string) -- [EC2-Classic, default VPC] The name of the security group. You must specify either the security group ID or the security group name in the request.
> IpPermissions (list) --
> 
> ToPort (integer) --
> The end of port range for the TCP and UDP protocols, or an ICMP/ICMPv6 code. A value of -1 indicates all ICMP/ICMPv6 codes. If you specify all ICMP/ICMPv6 types, you must specify all codes.

I suggest to create supplementary parameters to decouple usage of the module from the payload that the method expects.
This would involve creating a set of two new parameters `icmp_code` and `icmp_type` that would be mutually exclusive with `from_port` and `to_port` parameters.

```yaml
      - proto: tcp
        from_port: 3306
        to_port: 3306
        group_id: 123412341234/sg-87654321/exact-name-of-sg
      - proto: udp
        from_port: 10050
        to_port: 10050
        cidr_ip: 10.0.0.0/8
      - proto: icmp
        icmp_code: 8      # New parameter
        icmp_type:  -1    # New parameter
        cidr_ip: 10.0.0.0/8
```
The usage of `from_port` and `to_port` would fail if `proto` reads `icmp` and vice versa, Ansible would complain if the `icmp_port` and `icmp_type` are used without a `icmp` or `icmpv6`.

##
Regarding the new `ports` key instead, there may be two solutions:
1. Updating the documentation to make it clear that the list defines ranges for TCP and UDP protocols and and ICMP port range and type.
2. Make this parameter exclusive to TCP and UDP protocols.

This change would align the semantics of the module with Ansible's philosophy, that is, using mutually exclusive parameters (for instance `instance_name` and `instance_id`) instead of basing the behavior on the module based on the _value_ of a parameter.
The change would not change the payload being sent, only create a "superset" for consistency.


### Issue Type

Feature Idea

### Component Name

ec2_group

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/ec2_group.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/ec2_group.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @adq @jillr @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Any change that modifies existing behaviour would need to maintain the existing behaviour, at least for a deprecation period, so that we don't break user's playbooks.  We'd definitely consider a PR that implements this feature!
Files identified in the description:
* [`plugins/modules/ec2_group.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/ec2_group.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @adq @jillr @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Any change that modifies existing behaviour would need to maintain the existing behaviour, at least for a deprecation period, so that we don't break user's playbooks.  We'd definitely consider a PR that implements this feature!

Resolving patch:
diff --git a/plugins/modules/ec2_group.py b/plugins/modules/ec2_group.py
--- a/plugins/modules/ec2_group.py
+++ b/plugins/modules/ec2_group.py
@@ -11,7 +11,9 @@
 ---
 module: ec2_group
 version_added: 1.0.0
-author: "Andrew de Quincey (@adq)"
+author:
+  - "Andrew de Quincey (@adq)"
+  - "Razique Mahroua (@Razique)"
 short_description: maintain an ec2 VPC security group.
 description:
     - Maintains ec2 security groups.
@@ -92,7 +94,11 @@
         proto:
             type: str
             description:
-            - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers))
+            - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or
+            - number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers))
+            - When using C(icmp) or C(icmpv6) as the protocol, you can pass
+            - the C(icmp_type) and C(icmp_code) parameters instead of
+            - C(from_port) and C(to_port).
         from_port:
             type: int
             description:
@@ -105,6 +111,20 @@
             - The end of the range of ports that traffic is coming from.
             - A value can be between C(0) to C(65535).
             - A value of C(-1) indicates all ports (only supported when I(proto=icmp)).
+        icmp_type:
+          version_added: 3.3.0
+          type: int
+          description:
+          - When using C(icmp) or C(icmpv6) as the protocol, allows you to
+          - specify the ICMP type to use. The option is mutually exclusive with C(from_port).
+          - A value of C(-1) indicates all ICMP types.
+        icmp_code:
+          version_added: 3.3.0
+          type: int
+          description:
+          - When using C(icmp) or C(icmpv6) as the protocol, allows you to specify
+          - the ICMP code to use. The option is mutually exclusive with C(to_port).
+          - A value of C(-1) indicates all ICMP codes.
         rule_desc:
             type: str
             description: A description for the rule.
@@ -159,7 +179,10 @@
         proto:
             type: str
             description:
-            - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers))
+            - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or
+            - number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers))
+            - When using C(icmp) or C(icmpv6) as the protocol, you can pass the
+            - C(icmp_type) and C(icmp_code) parameters instead of C(from_port) and C(to_port).
         from_port:
             type: int
             description:
@@ -172,6 +195,20 @@
             - The end of the range of ports that traffic is going to.
             - A value can be between C(0) to C(65535).
             - A value of C(-1) indicates all ports (only supported when I(proto=icmp)).
+        icmp_type:
+          version_added: 3.3.0
+          type: int
+          description:
+          - When using C(icmp) or C(icmpv6) as the protocol, allows you to specify
+          - the ICMP type to use. The option is mutually exclusive with C(from_port).
+          - A value of C(-1) indicates all ICMP types.
+        icmp_code:
+          version_added: 3.3.0
+          type: int
+          description:
+          - When using C(icmp) or C(icmpv6) as the protocol, allows you to specify
+          - the ICMP code to use. The option is mutually exclusive with C(to_port).
+          - A value of C(-1) indicates all ICMP codes.
         rule_desc:
             type: str
             description: A description for the rule.
@@ -205,8 +242,8 @@
     aliases: ['resource_tags']
   purge_tags:
     description:
-      - If yes, existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter. If the I(tags) parameter is not set then
-        tags will not be modified.
+      - If yes, existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter.
+      - If the I(tags) parameter is not set then tags are not modified.
     required: false
     default: yes
     type: bool
@@ -238,6 +275,19 @@
         cidr_ip: 0.0.0.0/0
         rule_desc: allow all on port 80
 
+- name: example using ICMP types and codes
+  amazon.aws.ec2_group:
+    name: "{{ name }}"
+    description: sg for ICMP
+    vpc_id: vpc-xxxxxxxx
+    profile: "{{ aws_profile }}"
+    region: us-east-1
+    rules:
+      - proto: icmp
+        icmp_type: 3
+        icmp_code: 1
+        cidr_ip: 0.0.0.0/0
+
 - name: example ec2 group
   amazon.aws.ec2_group:
     name: example
@@ -281,10 +331,11 @@
         # the containing group name may be specified here
         group_name: example
       - proto: all
-        # in the 'proto' attribute, if you specify -1 (only supported when I(proto=icmp)), all, or a protocol number other than tcp, udp, icmp, or 58 (ICMPv6),
-        # traffic on all ports is allowed, regardless of any ports you specify
+        # in the 'proto' attribute, if you specify -1 (only supported when I(proto=icmp)), all, or a protocol number
+        # other than tcp, udp, icmp, or 58 (ICMPv6), traffic on all ports is allowed, regardless of any ports that
+        # you specify.
         from_port: 10050 # this value is ignored
-        to_port: 10050 # this value is ignored
+        to_port: 10050   # this value is ignored
         cidr_ip: 10.0.0.0/8
 
     rules_egress:
@@ -304,7 +355,8 @@
     vpc_id: 12345
     region: eu-west-1
     rules:
-      # 'ports' rule keyword was introduced in version 2.4. It accepts a single port value or a list of values including ranges (from_port-to_port).
+      # 'ports' rule keyword was introduced in version 2.4. It accepts a single
+      # port value or a list of values including ranges (from_port-to_port).
       - proto: tcp
         ports: 22
         group_name: example-vpn
@@ -314,7 +366,8 @@
           - 443
           - 8080-8099
         cidr_ip: 0.0.0.0/0
-      # Rule sources list support was added in version 2.4. This allows to define multiple sources per source type as well as multiple source types per rule.
+      # Rule sources list support was added in version 2.4. This allows to
+      # define multiple sources per source type as well as multiple source types per rule.
       - proto: tcp
         ports:
           - 6379
@@ -591,9 +644,21 @@ def deduplicate_rules_args(rules):
 
 
 def validate_rule(module, rule):
-    VALID_PARAMS = ('cidr_ip', 'cidr_ipv6', 'ip_prefix',
-                    'group_id', 'group_name', 'group_desc',
-                    'proto', 'from_port', 'to_port', 'rule_desc')
+    VALID_PARAMS = (
+        'cidr_ip',
+        'cidr_ipv6',
+        'ip_prefix',
+        'group_id',
+        'group_name',
+        'group_desc',
+        'proto',
+        'from_port',
+        'to_port',
+        'icmp_type',
+        'icmp_code',
+        'icmp_keys',
+        'rule_desc',
+    )
     if not isinstance(rule, dict):
         module.fail_json(msg='Invalid rule parameter type [%s].' % type(rule))
     for k in rule:
@@ -612,6 +677,12 @@ def validate_rule(module, rule):
         module.fail_json(msg="Specify cidr_ip OR cidr_ipv6, not both")
     elif 'group_id' in rule and 'group_name' in rule:
         module.fail_json(msg='Specify group_id OR group_name, not both')
+    elif ('icmp_type' in rule or 'icmp_code' in rule) and 'ports' in rule:
+        module.fail_json(msg='Specify icmp_code/icmp_type OR ports, not both')
+    elif ('from_port' in rule or 'to_port' in rule) and ('icmp_type' in rule or 'icmp_code' in rule) and 'icmp_keys' not in rule:
+        module.fail_json(msg='Specify from_port/to_port OR icmp_type/icmp_code, not both')
+    elif ('icmp_type' in rule or 'icmp_code' in rule) and ('icmp' not in rule['proto']):
+        module.fail_json(msg='Specify proto: icmp or icmpv6 when using icmp_type/icmp_code')
 
 
 def get_target_from_rule(module, client, rule, name, group, groups, vpc_id):
@@ -741,11 +812,27 @@ def ports_expand(ports):
 
 def rule_expand_ports(rule):
     # takes a rule dict and returns a list of expanded rule dicts
+    # uses icmp_code and icmp_type instead of from_ports and to_ports when
+    # available.
     if 'ports' not in rule:
-        if isinstance(rule.get('from_port'), string_types):
-            rule['from_port'] = int(rule.get('from_port'))
-        if isinstance(rule.get('to_port'), string_types):
-            rule['to_port'] = int(rule.get('to_port'))
+        non_icmp_params = any([
+            rule.get('icmp_type', None) is None, rule.get('icmp_code', None) is None])
+        conflict = not non_icmp_params and any([
+            rule.get('from_port', None), rule.get('to_port', None)])
+
+        if non_icmp_params:
+            if isinstance(rule.get('from_port'), string_types):
+                rule['from_port'] = int(rule.get('from_port'))
+            if isinstance(rule.get('to_port'), string_types):
+                rule['to_port'] = int(rule.get('to_port'))
+        else:
+            rule['from_port'] = int(rule.get('icmp_type')) if isinstance(rule.get('icmp_type'), string_types) else rule.get('icmp_type')
+            rule['to_port'] = int(rule.get('icmp_code')) if isinstance(rule.get('icmp_code'), string_types) else rule.get('icmp_code')
+            # Used temporarily to track the fact that icmp keys were converted
+            # to from_port/to_port
+            if not conflict:
+                rule['icmp_keys'] = True
+
         return [rule]
 
     ports = rule['ports'] if isinstance(rule['ports'], list) else [rule['ports']]
@@ -786,7 +873,7 @@ def rule_expand_source(rule, source_type):
 
 
 def rule_expand_sources(rule):
-    # takes a rule dict and returns a list of expanded rule discts
+    # takes a rule dict and returns a list of expanded rule dicts
     source_types = (stype for stype in ('cidr_ip', 'cidr_ipv6', 'group_id', 'group_name', 'ip_prefix') if stype in rule)
 
     return [r for stype in source_types
@@ -845,8 +932,7 @@ def revoke(client, module, ip_permissions, group_id, rule_type):
                     aws_retry=True, GroupId=group_id, IpPermissions=ip_permissions)
             elif rule_type == 'out':
                 client.revoke_security_group_egress(
-                    aws_retry=True,
-                    GroupId=group_id, IpPermissions=ip_permissions)
+                    aws_retry=True, GroupId=group_id, IpPermissions=ip_permissions)
         except (BotoCoreError, ClientError) as e:
             rules = 'ingress rules' if rule_type == 'in' else 'egress rules'
             module.fail_json_aws(e, "Unable to revoke {0}: {1}".format(rules, ip_permissions))
@@ -1106,6 +1192,7 @@ def get_final_rules(client, module, security_group_rules, specified_rules, purge
             # Order final rules consistently
             final_rules.sort(key=get_ip_permissions_sort_key)
         return final_rules
+
     security_group_ingress = security_group.get('ip_permissions', [])
     specified_ingress = module.params['rules']
     purge_ingress = module.params['purge_rules']
@@ -1273,10 +1360,15 @@ def main():
                     module, client, rule, name, group, groups, vpc_id)
                 changed |= target_group_created
 
+                rule.pop('icmp_type', None)
+                rule.pop('icmp_code', None)
+                rule.pop('icmp_keys', None)
+
                 if rule.get('proto', 'tcp') in ('all', '-1', -1):
                     rule['proto'] = '-1'
                     rule['from_port'] = None
                     rule['to_port'] = None
+
                 try:
                     int(rule.get('proto', 'tcp'))
                     rule['proto'] = to_text(rule.get('proto', 'tcp'))
@@ -1285,7 +1377,6 @@ def main():
                 except ValueError:
                     # rule does not use numeric protocol spec
                     pass
-
                 named_tuple_rule_list.append(
                     Rule(
                         port_range=(rule['from_port'], rule['to_port']),


Test patch:
diff --git a/tests/integration/targets/ec2_group/tasks/icmp_verbs.yml b/tests/integration/targets/ec2_group/tasks/icmp_verbs.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_group/tasks/icmp_verbs.yml
@@ -0,0 +1,191 @@
+---
+- block:
+    # ============================================================
+    - name: Create simple rule using icmp verbs
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-1'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+          - proto: "icmp"
+            icmp_type: 3
+            icmp_code: 8
+            cidr_ip:
+              - 10.0.0.0/8
+              - 172.16.40.10/32
+        state: present
+      register: result
+
+    - name: Retrieve security group info
+      ec2_group_info:
+        filters:
+          group-name: '{{ ec2_group_name }}-icmp-1'
+      register: result_1
+
+    - assert:
+        that:
+          - result is changed
+          - result_1.security_groups is defined
+          - (result_1.security_groups|first).group_name == '{{ ec2_group_name }}-icmp-1'
+          - (result_1.security_groups|first).ip_permissions[0].ip_protocol == "icmp"
+
+    - name: Create ipv6 rule using icmp verbs
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-2'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+          - proto: "icmpv6"
+            icmp_type: 1
+            icmp_code: 4
+            cidr_ipv6: "64:ff9b::/96"
+        state: present
+      register: result
+
+    - name: Retrieve security group info
+      ec2_group_info:
+        filters:
+          group-name: '{{ ec2_group_name }}-icmp-2'
+      register: result_1
+
+    - assert:
+        that:
+          - result is changed
+          - result_1.security_groups is defined
+          - (result_1.security_groups|first).group_name == '{{ ec2_group_name }}-icmp-2'
+          - (result_1.security_groups|first).ip_permissions[0].ip_protocol == "icmpv6"
+
+
+    - name: Create rule using security group referencing
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-3'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+          - proto: "icmp"
+            icmp_type: 5
+            icmp_code: 1
+            group_name: '{{ec2_group_name}}-auto-create-2'
+            group_desc: "sg-group-referencing"
+        state: present
+      register: result
+
+    - name: Retrieve security group info
+      ec2_group_info:
+        filters:
+          group-name: '{{ ec2_group_name }}-icmp-3'
+      register: result_1
+
+    - assert:
+        that:
+          - result is changed
+          - (result_1.security_groups | first).ip_permissions[0].user_id_group_pairs is defined
+
+    - name: Create list rule using 0 as icmp_type
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-4'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+          - proto: icmp
+            icmp_type: 0
+            icmp_code: 1
+            cidr_ip:
+              - 10.0.0.0/8
+              - 172.16.40.10/32
+          - proto: "tcp"
+            from_port: 80
+            to_port: 80
+            cidr_ip: 172.16.40.10/32
+        state: present
+      register: result
+
+    - name: Retrieve security group info
+      ec2_group_info:
+        filters:
+          group-name: '{{ ec2_group_name }}-icmp-4'
+      register: result_1
+
+    - assert:
+        that:
+          - result is changed
+          - (result_1.security_groups | first).ip_permissions | length == 2
+    # ============================================================
+
+    # ============================================================
+    - name: Create a group with non-ICMP protocol
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-4'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+        - proto: "tcp"
+          icmp_type: 0
+          icmp_code: 1
+          cidr_ip:
+            - 10.0.0.0/8
+            - 172.16.40.10/32
+        state: present
+      register: result
+      ignore_errors: true
+
+    - name: assert that group creation fails when proto != icmp with icmp parameters
+      assert:
+        that:
+          - result is failed
+
+    - name: Create a group with conflicting parameters
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-4'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+          - proto: icmp
+            from_port: 5
+            to_port: 1
+            icmp_type: 0
+            icmp_code: 1
+            cidr_ip:
+              - 10.0.0.0/8
+              - 172.16.40.10/32
+        state: present
+      register: result
+      ignore_errors: true
+
+    - name: assert that group creation fails when using conflicting parameters
+      assert:
+        that:
+          - result is failed
+
+    - name: Create a group with missing icmp parameters
+      ec2_group:
+        name: '{{ec2_group_name}}-icmp-4'
+        vpc_id: '{{ vpc_result.vpc.id }}'
+        description: '{{ec2_group_description}}'
+        rules:
+        - proto: "tcp"
+          icmp_type: 0
+          cidr_ip:
+            - 10.0.0.0/8
+            - 172.16.40.10/32
+        state: present
+      register: result
+      ignore_errors: true
+
+    - name: assert that group creation fails when missing icmp parameters
+      assert:
+        that:
+          - result is failed
+
+  always:
+    - name: tidy up egress rule test security group
+      ec2_group:
+        name: '{{ec2_group_name}}-auto-create-{{ item }}'
+        state: absent
+        vpc_id: '{{ vpc_result.vpc.id }}'
+      ignore_errors: yes
+      with_items:
+        - 1
+        - 2
+        - 3
+        - 4
diff --git a/tests/integration/targets/ec2_group/tasks/main.yml b/tests/integration/targets/ec2_group/tasks/main.yml
--- a/tests/integration/targets/ec2_group/tasks/main.yml
+++ b/tests/integration/targets/ec2_group/tasks/main.yml
@@ -62,6 +62,7 @@
     - include: ./numeric_protos.yml
     - include: ./rule_group_create.yml
     - include: ./egress_tests.yml
+    - include: ./icmp_verbs.yml
     - include: ./data_validation.yml
     - include: ./multi_nested_target.yml
     - include: ./group_info.yml