Repository: ansible-collections/community.aws

Issue:
Add partition strategy to placement groups module
### Summary

Add partition as a strategy for the community.aws.ec2_placement_group module.

Also add an option to choose the actual number of partitions (min 2 which is the default and a max of 7). This option would be taken into account when the strategy is set to partition.

### Issue Type

Feature Idea

### Component Name

ec2_placement_group

### Additional Information

Possible module definition
```yaml (paste below)
- name: Create a Spread placement group.
  community.aws.ec2_placement_group:
    name: my-cluster
    state: present
    strategy: partition
    partition_number: 4
```

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
@hferreira23, thank you for this feature idea. Would you be willing to open a PR to implement this feature? 

Resolving patch:
diff --git a/plugins/modules/ec2_placement_group.py b/plugins/modules/ec2_placement_group.py
--- a/plugins/modules/ec2_placement_group.py
+++ b/plugins/modules/ec2_placement_group.py
@@ -23,6 +23,13 @@
       - The name for the placement group.
     required: true
     type: str
+  partition_count:
+    description:
+      - The number of partitions.
+      - Valid only when I(Strategy) is set to C(partition).
+      - Must be a value between C(1) and C(7).
+    type: int
+    version_added: 3.1.0
   state:
     description:
       - Create or delete placement group.
@@ -35,7 +42,7 @@
         low-latency group in a single Availability Zone, while Spread spreads
         instances across underlying hardware.
     default: cluster
-    choices: [ 'cluster', 'spread' ]
+    choices: [ 'cluster', 'spread', 'partition' ]
     type: str
 extends_documentation_fragment:
 - amazon.aws.aws
@@ -58,6 +65,13 @@
     state: present
     strategy: spread
 
+- name: Create a Partition strategy placement group.
+  community.aws.ec2_placement_group:
+    name: my-cluster
+    state: present
+    strategy: partition
+    partition_count: 3
+
 - name: Delete a placement group.
   community.aws.ec2_placement_group:
     name: my-cluster
@@ -126,10 +140,21 @@ def get_placement_group_details(connection, module):
 def create_placement_group(connection, module):
     name = module.params.get("name")
     strategy = module.params.get("strategy")
+    partition_count = module.params.get("partition_count")
+
+    if strategy != 'partition' and partition_count:
+        module.fail_json(
+            msg="'partition_count' can only be set when strategy is set to 'partition'.")
+
+    params = {}
+    params['GroupName'] = name
+    params['Strategy'] = strategy
+    if partition_count:
+        params['PartitionCount'] = partition_count
+    params['DryRun'] = module.check_mode
 
     try:
-        connection.create_placement_group(
-            GroupName=name, Strategy=strategy, DryRun=module.check_mode)
+        connection.create_placement_group(**params)
     except is_boto3_error_code('DryRunOperation'):
         module.exit_json(changed=True, placement_group={
             "name": name,
@@ -165,8 +190,9 @@ def delete_placement_group(connection, module):
 def main():
     argument_spec = dict(
         name=dict(required=True, type='str'),
+        partition_count=dict(type='int'),
         state=dict(default='present', choices=['present', 'absent']),
-        strategy=dict(default='cluster', choices=['cluster', 'spread'])
+        strategy=dict(default='cluster', choices=['cluster', 'spread', 'partition'])
     )
 
     module = AnsibleAWSModule(


Test patch:
diff --git a/tests/integration/targets/ec2_placement_group/aliases b/tests/integration/targets/ec2_placement_group/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/aliases
@@ -0,0 +1,3 @@
+cloud/aws
+
+ec2_placement_group_info
diff --git a/tests/integration/targets/ec2_placement_group/defaults/main.yml b/tests/integration/targets/ec2_placement_group/defaults/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/defaults/main.yml
@@ -0,0 +1 @@
+---
diff --git a/tests/integration/targets/ec2_placement_group/meta/main.yml b/tests/integration/targets/ec2_placement_group/meta/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/meta/main.yml
@@ -0,0 +1 @@
+---
diff --git a/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml b/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/tasks/env_cleanup.yml
@@ -0,0 +1,94 @@
+- name: remove any instances in the test VPC
+  ec2_instance:
+    filters:
+      vpc_id: "{{ testing_vpc.vpc.id }}"
+    state: absent
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: Get ENIs
+  ec2_eni_info:
+    filters:
+      vpc-id: "{{ testing_vpc.vpc.id }}"
+  register: enis
+
+- name: delete all ENIs
+  ec2_eni:
+    eni_id: "{{ item.id }}"
+    state: absent
+  until: removed is not failed
+  with_items: "{{ enis.network_interfaces }}"
+  ignore_errors: yes
+  retries: 10
+
+- name: remove the security group
+  ec2_group:
+    name: "{{ resource_prefix }}-sg"
+    description: a security group for ansible tests
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    state: absent
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: remove routing rules
+  ec2_vpc_route_table:
+    state: absent
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    tags:
+      created: "{{ resource_prefix }}-route"
+    routes:
+      - dest: 0.0.0.0/0
+        gateway_id: "{{ igw.gateway_id }}"
+    subnets:
+      - "{{ testing_subnet_a.subnet.id }}"
+      - "{{ testing_subnet_b.subnet.id }}"
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: remove internet gateway
+  ec2_vpc_igw:
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    state: absent
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: remove subnet A
+  ec2_vpc_subnet:
+    state: absent
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    cidr: 10.22.32.0/24
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: remove subnet B
+  ec2_vpc_subnet:
+    state: absent
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    cidr: 10.22.33.0/24
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
+
+- name: remove the VPC
+  ec2_vpc_net:
+    name: "{{ resource_prefix }}-vpc"
+    cidr_block: 10.22.32.0/23
+    state: absent
+    tags:
+      Name: Ansible Testing VPC
+    tenancy: default
+  register: removed
+  until: removed is not failed
+  ignore_errors: yes
+  retries: 10
diff --git a/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml b/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/tasks/env_setup.yml
@@ -0,0 +1,64 @@
+- name: Create VPC for use in testing
+  ec2_vpc_net:
+    name: "{{ resource_prefix }}-vpc"
+    cidr_block: 10.22.32.0/23
+    tags:
+      Name: Ansible ec2_lc Testing VPC
+    tenancy: default
+  register: testing_vpc
+
+- name: Create internet gateway for use in testing
+  ec2_vpc_igw:
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    state: present
+    tags:
+      Name: Ansible ec2_lc Testing gateway
+  register: igw
+
+- name: Create default subnet in zone A
+  ec2_vpc_subnet:
+    state: present
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    cidr: 10.22.32.0/24
+    az: "{{ aws_region }}a"
+    resource_tags:
+      Name: "{{ resource_prefix }}-subnet-a"
+  register: testing_subnet_a
+
+- name: Create secondary subnet in zone B
+  ec2_vpc_subnet:
+    state: present
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    cidr: 10.22.33.0/24
+    az: "{{ aws_region }}b"
+    resource_tags:
+      Name: "{{ resource_prefix }}-subnet-b"
+  register: testing_subnet_b
+
+- name: create routing rules
+  ec2_vpc_route_table:
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    tags:
+      created: "{{ resource_prefix }}-route"
+    routes:
+      - dest: 0.0.0.0/0
+        gateway_id: "{{ igw.gateway_id }}"
+    subnets:
+      - "{{ testing_subnet_a.subnet.id }}"
+      - "{{ testing_subnet_b.subnet.id }}"
+
+- name: create a security group with the vpc
+  ec2_group:
+    name: "{{ resource_prefix }}-sg"
+    description: a security group for ansible tests
+    vpc_id: "{{ testing_vpc.vpc.id }}"
+    rules:
+      - proto: tcp
+        from_port: 22
+        to_port: 22
+        cidr_ip: 0.0.0.0/0
+      - proto: tcp
+        from_port: 80
+        to_port: 80
+        cidr_ip: 0.0.0.0/0
+  register: sg
diff --git a/tests/integration/targets/ec2_placement_group/tasks/main.yml b/tests/integration/targets/ec2_placement_group/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/tasks/main.yml
@@ -0,0 +1,408 @@
+- name: run ec2_placement_group tests
+  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 }}"
+  collections:
+    - amazon.aws
+  vars:
+    placement_group_names: []
+
+  block:
+
+    - name: set up environment for testing.
+      include_tasks: env_setup.yml
+
+    - name: Create a placement group 1 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: present
+      check_mode: true
+      register: pg_1_create_check_mode
+
+    - assert:
+        that:
+          - pg_1_create_check_mode is changed
+          - pg_1_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg1'
+          - pg_1_create_check_mode.placement_group.state == "DryRun"
+          - '"ec2:CreatePlacementGroup" in pg_1_create_check_mode.resource_actions'
+
+    - name: Create a placement group 1
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: present
+      register: pg_1_create
+
+    - set_fact:
+        placement_group_names: "{{ placement_group_names + [pg_1_create.placement_group.name] }}"
+
+    - assert:
+        that:
+          - pg_1_create is changed
+          - pg_1_create.placement_group.name == '{{ resource_prefix }}-pg1'
+          - pg_1_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" in pg_1_create.resource_actions'
+
+    - name: Gather information about placement group 1
+      community.aws.ec2_placement_group_info:
+        names:
+          - '{{ resource_prefix }}-pg1'
+      register: pg_1_info_result
+
+    - assert:
+        that:
+          - pg_1_info_result is not changed
+          - pg_1_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg1'
+          - pg_1_info_result.placement_groups[0].state == "available"
+          - pg_1_info_result.placement_groups[0].strategy == "cluster"
+          - '"ec2:DescribePlacementGroups" in pg_1_info_result.resource_actions'
+
+    - name: Create a placement group 1 - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: present
+      register: pg_1_create
+
+    - assert:
+        that:
+          - pg_1_create is not changed
+          - pg_1_create.placement_group.name == '{{ resource_prefix }}-pg1'
+          - pg_1_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_1_create.resource_actions'
+
+    - name: Create a placement group 1 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: present
+      check_mode: true
+      register: pg_1_create_check_mode_idem
+
+    - assert:
+        that:
+          - pg_1_create_check_mode_idem is not changed
+          - pg_1_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg1'
+          - pg_1_create_check_mode_idem.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_1_create_check_mode_idem.resource_actions'
+
+    - name: Create a placement group 2 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: present
+        strategy: spread
+      check_mode: true
+      register: pg_2_create_check_mode
+
+    - assert:
+        that:
+          - pg_2_create_check_mode is changed
+          - pg_2_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg2'
+          - pg_2_create_check_mode.placement_group.state == "DryRun"
+          - '"ec2:CreatePlacementGroup" in pg_2_create_check_mode.resource_actions'
+
+    - name: Create a placement group 2 with spread strategy
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: present
+        strategy: spread
+      register: pg_2_create
+
+    - assert:
+        that:
+          - pg_2_create is changed
+          - pg_2_create.placement_group.name == '{{ resource_prefix }}-pg2'
+          - pg_2_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" in pg_2_create.resource_actions'
+
+    - set_fact:
+        placement_group_names: "{{ placement_group_names + [pg_2_create.placement_group.name] }}"
+
+    - name: Gather information about placement group 2
+      community.aws.ec2_placement_group_info:
+        names:
+          - '{{ resource_prefix }}-pg2'
+      register: pg_2_info_result
+
+    - assert:
+        that:
+          - pg_2_info_result is not changed
+          - pg_2_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg2'
+          - pg_2_info_result.placement_groups[0].state == "available"
+          - pg_2_info_result.placement_groups[0].strategy == "spread"
+          - '"ec2:DescribePlacementGroups" in pg_2_info_result.resource_actions'
+
+    - name: Create a placement group 2 with spread strategy - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: present
+        strategy: spread
+      register: pg_2_create
+
+    - assert:
+        that:
+          - pg_2_create is not changed
+          - pg_2_create.placement_group.name == '{{ resource_prefix }}-pg2'
+          - pg_2_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_2_create.resource_actions'
+
+    - name: Create a placement group 2 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: present
+        strategy: spread
+      check_mode: true
+      register: pg_2_create_check_mode_idem
+
+    - assert:
+        that:
+          - pg_2_create_check_mode_idem is not changed
+          - pg_2_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg2'
+          - pg_2_create_check_mode_idem.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_2_create_check_mode_idem.resource_actions'
+
+    - name: Create a placement group 3 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: present
+        strategy: partition
+        partition_count: 4
+      check_mode: true
+      register: pg_3_create_check_mode
+
+    - assert:
+        that:
+          - pg_3_create_check_mode is changed
+          - pg_3_create_check_mode.placement_group.name == '{{ resource_prefix }}-pg3'
+          - pg_3_create_check_mode.placement_group.state == "DryRun"
+          - '"ec2:CreatePlacementGroup" in pg_3_create_check_mode.resource_actions'
+
+    - name: Create a placement group 3 with Partition strategy
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: present
+        strategy: partition
+        partition_count: 4
+      register: pg_3_create
+
+    - assert:
+        that:
+          - pg_3_create is changed
+          - pg_3_create.placement_group.name == '{{ resource_prefix }}-pg3'
+          - pg_3_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" in pg_3_create.resource_actions'
+
+    - set_fact:
+        placement_group_names: "{{ placement_group_names + [pg_3_create.placement_group.name] }}"
+
+
+    - name: Gather information about placement group 3
+      community.aws.ec2_placement_group_info:
+        names:
+          - '{{ resource_prefix }}-pg3'
+      register: pg_3_info_result
+
+    - assert:
+        that:
+          - pg_3_info_result is not changed
+          - pg_3_info_result.placement_groups[0].name == '{{ resource_prefix }}-pg3'
+          - pg_3_info_result.placement_groups[0].state == "available"
+          - pg_3_info_result.placement_groups[0].strategy == "partition"
+          - '"ec2:DescribePlacementGroups" in pg_3_info_result.resource_actions'
+
+    - name: Create a placement group 3 with Partition strategy - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: present
+        strategy: partition
+        partition_count: 4
+      register: pg_3_create
+
+    - assert:
+        that:
+          - pg_3_create is not changed
+          - pg_3_create.placement_group.name == '{{ resource_prefix }}-pg3'
+          - pg_3_create.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_3_create.resource_actions'
+
+    - name: Create a placement group 3 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: present
+        strategy: partition
+        partition_count: 4
+      check_mode: true
+      register: pg_3_create_check_mode_idem
+
+    - assert:
+        that:
+          - pg_3_create_check_mode_idem is not changed
+          - pg_3_create_check_mode_idem.placement_group.name == '{{ resource_prefix }}-pg3'
+          - pg_3_create_check_mode_idem.placement_group.state == "available"
+          - '"ec2:CreatePlacementGroup" not in pg_3_create_check_mode_idem.resource_actions'
+
+    - name: List all placement groups.
+      community.aws.ec2_placement_group_info:
+      register: all_ec2_placement_groups
+
+# Delete Placement Group ==========================================
+
+    # On using check_mode for delete placement group operation
+    # If operation would have succeeded, the error response is DryRunOperation.
+    # Otherwise, it is UnauthorizedOperation .
+    - name: Delete a placement group 1 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: absent
+      check_mode: true
+      register: pg_1_delete_check_mode
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_1_delete_check_mode is not changed
+          - pg_1_delete_check_mode.error.code == 'DryRunOperation'
+          - '"ec2:DeletePlacementGroup" in pg_1_delete_check_mode.resource_actions'
+
+    - name: Delete a placement group 1
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: absent
+      register: pg_1_delete
+
+    - assert:
+        that:
+          - pg_1_delete is changed
+          - '"ec2:DeletePlacementGroup" in pg_1_delete.resource_actions'
+
+    - name: Delete a placement group 1 - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: absent
+      register: pg_1_delete
+
+    - assert:
+        that:
+          - pg_1_delete is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_1_delete.resource_actions'
+
+    - name: Delete a placement group 1 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg1'
+        state: absent
+      check_mode: true
+      register: pg_1_delete_check_mode_idem
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_1_delete_check_mode_idem is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_1_delete_check_mode_idem.resource_actions'
+
+    - name: Delete a placement group 2 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: absent
+      check_mode: true
+      register: pg_2_delete_check_mode
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_2_delete_check_mode is not changed
+          - pg_2_delete_check_mode.error.code == 'DryRunOperation'
+          - '"ec2:DeletePlacementGroup" in pg_2_delete_check_mode.resource_actions'
+
+    - name: Delete a placement group 2
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: absent
+      register: pg_2_delete
+
+    - assert:
+        that:
+          - pg_2_delete is changed
+          - '"ec2:DeletePlacementGroup" in pg_2_delete.resource_actions'
+
+    - name: Delete a placement group 2 - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: absent
+      register: pg_2_delete
+
+    - assert:
+        that:
+          - pg_2_delete is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_2_delete.resource_actions'
+
+    - name: Delete a placement group 2 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg2'
+        state: absent
+      check_mode: true
+      register: pg_2_delete_check_mode_idem
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_2_delete_check_mode_idem is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_2_delete_check_mode_idem.resource_actions'
+
+    - name: Delete a placement group 3 - check_mode
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: absent
+      check_mode: true
+      register: pg_3_delete_check_mode
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_3_delete_check_mode is not changed
+          - pg_3_delete_check_mode.error.code == 'DryRunOperation'
+          - '"ec2:DeletePlacementGroup" in pg_3_delete_check_mode.resource_actions'
+
+    - name: Delete a placement group 3
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: absent
+      register: pg_3_delete
+
+    - assert:
+        that:
+          - pg_3_delete is changed
+          - '"ec2:DeletePlacementGroup" in pg_3_delete.resource_actions'
+
+    - name: Delete a placement group 3 - Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: absent
+      register: pg_3_delete
+
+    - assert:
+        that:
+          - pg_3_delete is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_3_delete.resource_actions'
+
+    - name: Delete a placement group 3 - check_mode Idempotency
+      community.aws.ec2_placement_group:
+        name: '{{ resource_prefix }}-pg3'
+        state: absent
+      check_mode: true
+      register: pg_3_delete_check_mode_idem
+      ignore_errors: true
+
+    - assert:
+        that:
+          - pg_3_delete_check_mode_idem is not changed
+          - '"ec2:DeletePlacementGroup" not in pg_3_delete_check_mode_idem.resource_actions'
+
+  always:
+
+    - name: Make sure placement groups created during test are deleted
+      community.aws.ec2_placement_group:
+        name: '{{ item }}'
+        state: absent
+      with_items: '{{ placement_group_names }}'
+
+    - include_tasks: env_cleanup.yml
diff --git a/tests/integration/targets/ec2_placement_group/vars/main.yml b/tests/integration/targets/ec2_placement_group/vars/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/ec2_placement_group/vars/main.yml
@@ -0,0 +1 @@
+---