Repository: ansible-collections/community.aws (language: python)

Issue:
Creating a new launch template version can fail if there are over 200 existing versions
### Summary

If there are over 200 launch template versions *and* the `source_version` is old enough, creating a new launch template version will fail due to the maximum number of versions that is returned by default in Boto3 ([MaxResults](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2/client/describe_launch_template_versions.html#EC2.Client.describe_launch_template_versions:~:text=page%20of%20results.-,MaxResults,-(integer)%20%E2%80%93%20The) defaults to the maximum, which is 200)

### Issue Type

Bug Report

### Component Name

ec2_launch_template

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.17.2]
  config file = /home/runner/work/<redacted>/<redacted>/ansible.cfg
  configured module search path = ['/home/runner/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/ansible
  ansible collection location = /home/runner/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/hostedtoolcache/Python/3.11.9/x64/bin/ansible
  python version = 3.11.9 (main, Jul 15 2024, 21:50:21) [GCC 11.4.0] (/opt/hostedtoolcache/Python/3.11.9/x64/bin/python)
  jinja version = 3.1.4
  libyaml = True
```


### Collection Versions

```console (paste below)
$ ansible-galaxy collection list
# /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/ansible_collections
Collection                               Version
---------------------------------------- -------
amazon.aws                               8.1.0  
ansible.netcommon                        6.1.3  
ansible.posix                            1.5.4  
ansible.utils                            4.1.0  
ansible.windows                          2.4.0  
arista.eos                               9.0.0  
awx.awx                                  24.6.1 
azure.azcollection                       2.6.0  
check_point.mgmt                         5.2.3  
chocolatey.chocolatey                    1.5.1  
cisco.aci                                2.10.1 
cisco.asa                                5.0.1  
cisco.dnac                               6.16.0 
cisco.intersight                         2.0.9  
cisco.ios                                8.0.0  
cisco.iosxr                              9.0.0  
cisco.ise                                2.9.2  
cisco.meraki                             2.18.1 
cisco.mso                                2.8.0  
cisco.nxos                               8.1.0  
cisco.ucs                                1.10.0 
cloud.common                             3.0.0  
cloudscale_ch.cloud                      2.3.1  
community.aws                            8.0.0  
community.ciscosmb                       1.0.9  
community.crypto                         2.21.0 
community.digitalocean                   1.26.0 
community.dns                            3.0.2  
community.docker                         3.11.0 
community.general                        9.2.0  
community.grafana                        1.9.1  
community.hashi_vault                    6.2.0  
community.hrobot                         2.0.1  
community.library_inventory_filtering_v1 1.0.1  
community.libvirt                        1.3.0  
community.mongodb                        1.7.5  
community.mysql                          3.9.0  
community.network                        5.0.3  
community.okd                            3.0.1  
community.postgresql                     3.4.1  
community.proxysql                       1.6.0  
community.rabbitmq                       1.3.0  
community.routeros                       2.17.0 
community.sap_libs                       1.4.2  
community.sops                           1.8.0  
community.vmware                         4.5.0  
community.windows                        2.2.0  
community.zabbix                         2.5.1  
containers.podman                        1.15.4 
cyberark.conjur                          1.3.0  
cyberark.pas                             1.0.25 
dellemc.enterprise_sonic                 2.4.0  
dellemc.openmanage                       9.4.0  
dellemc.powerflex                        2.5.0  
dellemc.unity                            2.0.0  
f5networks.f5_modules                    1.29.0 
fortinet.fortimanager                    2.5.0  
fortinet.fortios                         2.3.7  
frr.frr                                  2.0.2  
google.cloud                             1.3.0  
grafana.grafana                          5.3.0  
hetzner.hcloud                           3.1.1  
ibm.qradar                               3.0.0  
ibm.spectrum_virtualize                  2.0.0  
ibm.storage_virtualize                   2.4.1  
ieisystem.inmanage                       2.0.0  
infinidat.infinibox                      1.4.5  
infoblox.nios_modules                    1.6.1  
inspur.ispim                             2.2.3  
inspur.sm                                2.3.0  
junipernetworks.junos                    8.0.0  
kaytus.ksmanage                          1.2.2  
kubernetes.core                          3.2.0  
kubevirt.core                            1.5.0  
lowlydba.sqlserver                       2.3.3  
microsoft.ad                             1.6.0  
netapp.cloudmanager                      21.22.1
netapp.ontap                             22.11.0
netapp.storagegrid                       21.12.0
netapp_eseries.santricity                1.4.0  
netbox.netbox                            3.19.1 
ngine_io.cloudstack                      2.3.0  
ngine_io.exoscale                        1.1.0  
openstack.cloud                          2.2.0  
openvswitch.openvswitch                  2.1.1  
ovirt.ovirt                              3.2.0  
purestorage.flasharray                   1.30.0 
purestorage.flashblade                   1.17.0 
sensu.sensu_go                           1.14.0 
splunk.es                                3.0.0  
t_systems_mms.icinga_director            2.0.1  
telekom_mms.icinga_director              2.1.2  
theforeman.foreman                       4.0.0  
vmware.vmware                            1.3.0  
vmware.vmware_rest                       3.0.1  
vultr.cloud                              1.13.0 
vyos.vyos                                4.1.0  
wti.remote                               1.0.5
```


### AWS SDK versions

```console (paste below)
$ pip show boto boto3 botocore
WARNING: Package(s) not found: boto
Name: boto3
Version: 1.34.151
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: 
---
Name: botocore
Version: 1.34.151
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: boto3, s3transfer
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CONFIG_FILE() = /home/runner/work/<redacted>/<redacted>/ansible.cfg
LOCALHOST_WARNING(/home/runner/work/<redacted>/<redacted>/ansible.cfg) = False
```


### OS / Environment

The target OS is irrelevant here

### Steps to Reproduce

Use the [ec2_launch_template](https://docs.ansible.com/ansible/2.9/modules/ec2_launch_template_module.html) to create a new version of a launch template, repeat until there are over 200 versions of the launch template

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- name: Create new launch template version for the defined AMI
  register: created_launch_template
  ec2_launch_template:
    template_id: "lt-123"
    image_id: "ami-123"
    version_description: "Some new version description {{ now(utc=true,fmt='%Y-%m-%d %H:%M:%S') }}"
    source_version: 1
    default_version: 1
```


### Expected Results

The new version of the launch template should be created instead of failing

### Actual Results

One the pagination limit has been reached, the version creation will fail with:

```
fatal: [localhost]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": { … snip ...  }
    },
    "msg": "source_version does not exist, got \"1\""
}
```

This happens even though the version *does* exist on AWS. 

`ec2.describe_launch_template_versions` only returns the last 200 versions in: https://github.com/ansible-collections/community.aws/blob/97131eca035974f41ad17357ef9bef8a0a61f675/plugins/modules/ec2_launch_template.py#L505-L507

Which causes the version number validation to erroneously fail here: https://github.com/ansible-collections/community.aws/blob/97131eca035974f41ad17357ef9bef8a0a61f675/plugins/modules/ec2_launch_template.py#L633-L636

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion / hints:
Sounds like pagination with `NextToken` must be implemented here.  
@matiaskorhonen do you have time to implement it?  
There are already some implementations like that https://github.com/search?q=repo%3Aansible-collections%2Fcommunity.aws%20NextToken&type=code

Fix patch:
diff --git a/changelogs/fragments/20240110-ec2_launch_template-refactor.yml b/changelogs/fragments/20240110-ec2_launch_template-refactor.yml
new file mode 100644
index 00000000000..d2e7293fa9a
--- /dev/null
+++ b/changelogs/fragments/20240110-ec2_launch_template-refactor.yml
@@ -0,0 +1,9 @@
+---
+breaking_changes:
+  - ec2_launch_template - Tags defined using option ``tags`` are now applied to the launch template resources not the resource created using this launch template (https://github.com/ansible-collections/community.aws/issues/176).
+minor_changes:
+  - ec2_launch_template - Refactor module to use shared code from ``amazon.aws.plugins.module_utils.ec2`` and update ``RETURN`` block (https://github.com/ansible-collections/community.aws/pull/2164).
+  - ec2_launch_template - Add the possibility to delete specific versions of a launch template using ``versions_to_delete`` (https://github.com/ansible-collections/community.aws/pull/2164).
+  - ec2_launch_template - Add suboption ``throughput`` to ``block_device_mappings`` argument (https://github.com/ansible-collections/community.aws/issues/1944).
+  - ec2_launch_template - Add option ``tag_specifications`` to define tags to be applied to the resources created with the launch template (https://github.com/ansible-collections/community.aws/issues/176).
+  - ec2_launch_template - Add support ``purge_tags`` parameter (https://github.com/ansible-collections/community.aws/issues/176).
diff --git a/plugins/modules/ec2_launch_template.py b/plugins/modules/ec2_launch_template.py
index 9fd32711f91..aa055535a9a 100644
--- a/plugins/modules/ec2_launch_template.py
+++ b/plugins/modules/ec2_launch_template.py
@@ -21,6 +21,7 @@
   template_id:
     description:
     - The ID for the launch template, can be used for all cases except creating a new Launch Template.
+    - At least one of O(template_id) and O(template_name) must be specified.
     aliases: [id]
     type: str
   template_name:
@@ -31,6 +32,7 @@
       nothing happens.
     - If a launch template with the specified name already exists and the configuration has changed,
       a new version of the launch template is created.
+    - At least one of O(template_id) and O(template_name) must be specified.
     aliases: [name]
     type: str
   default_version:
@@ -44,6 +46,15 @@
     - The description of a launch template version.
     default: ""
     type: str
+  versions_to_delete:
+    description:
+    - The version numbers of a launch template versions to delete.
+    - Use O(default_version) to specify a new default version when deleting the current default version.
+    - By default, the latest version will be made the default.
+    - Ignored when O(state=present).
+    type: list
+    elements: int
+    version_added: 9.0.0
   state:
     description:
     - Whether the launch template should exist or not.
@@ -63,7 +74,7 @@
     elements: dict
     suboptions:
       device_name:
-        description: The device name (for example, /dev/sdh or xvdh).
+        description: The device name (for example, V(/dev/sdh) or V(xvdh)).
         type: str
       no_device:
         description: Suppresses the specified device included in the block device mapping of the AMI.
@@ -119,6 +130,12 @@
           volume_type:
             description: The volume type
             type: str
+          throughput:
+            description: >
+              The throughput to provision for a gp3 volume, with a maximum of 1,000 MiB/s.
+              Valid Range - Minimum value of V(125). Maximum value of V(1000).
+            type: int
+            version_added: 9.0.0
   cpu_options:
     description:
     - Choose CPU settings for the EC2 instances that will be created with this template.
@@ -131,24 +148,21 @@
       threads_per_core:
         description: >
           The number of threads per CPU core. To disable Intel Hyper-Threading
-          Technology for the instance, specify a value of 1. Otherwise, specify
-          the default value of 2.
+          Technology for the instance, specify a value of V(1). Otherwise, specify
+          the default value of V(2).
         type: int
   credit_specification:
     description: The credit option for CPU usage of the instance. Valid for T2 or T3 instances only.
     type: dict
     suboptions:
       cpu_credits:
-        description: >
-          The credit option for CPU usage of a T2 or T3 instance. Valid values
-          are C(standard) and C(unlimited).
+        description:
+          - The credit option for CPU usage of a T2 or T3 instance. Valid values are C(standard) and C(unlimited).
         type: str
   disable_api_termination:
-    description: >
-      This helps protect instances from accidental termination. If set to true,
-      you can't terminate the instance using the Amazon EC2 console, CLI, or
-      API. To change this attribute to false after launch, use
-      I(ModifyInstanceAttribute).
+    description:
+      - This helps protect instances from accidental termination.
+      - If set to V(true), you can't terminate the instance using the Amazon EC2 console, CLI, or API.
     type: bool
   ebs_optimized:
     description: >
@@ -187,20 +201,19 @@
     type: dict
     suboptions:
       market_type:
-        description: The market type. This should always be 'spot'.
+        description: The market type. This should always be V(spot).
         type: str
       spot_options:
         description: Spot-market specific settings.
         type: dict
         suboptions:
           block_duration_minutes:
-            description: >
-              The required duration for the Spot Instances (also known as Spot
-              blocks), in minutes. This value must be a multiple of 60 (60,
-              120, 180, 240, 300, or 360).
+            description:
+              - The required duration for the Spot Instances (also known as Spot blocks), in minutes.
+              - This value must be a multiple of V(60) (V(60), V(120), V(180), V(240), V(300), or V(360)).
             type: int
           instance_interruption_behavior:
-            description: The behavior when a Spot Instance is interrupted. The default is C(terminate).
+            description: The behavior when a Spot Instance is interrupted. The default is V(terminate).
             choices: [hibernate, stop, terminate]
             type: str
           max_price:
@@ -211,22 +224,21 @@
             choices: [one-time, persistent]
             type: str
   instance_type:
-    description: >
-      The instance type, such as C(c5.2xlarge). For a full list of instance types, see
-      U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html).
+    description:
+      - The instance type, such as V(c5.2xlarge). For a full list of instance types, see
+        U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html).
     type: str
   kernel_id:
-    description: >
-      The ID of the kernel. We recommend that you use PV-GRUB instead of
-      kernels and RAM disks. For more information, see
-      U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html)
+    description:
+      - The ID of the kernel.
+      - We recommend that you use PV-GRUB instead of kernels and RAM disks. For more information, see
+        U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html)
     type: str
   key_name:
     description:
     - The name of the key pair. You can create a key pair using M(amazon.aws.ec2_key).
     - If you do not specify a key pair, you can't connect to the instance
-      unless you choose an AMI that is configured to allow users another way to
-      log in.
+      unless you choose an AMI that is configured to allow users another way to log in.
     type: str
   monitoring:
     description: Settings for instance monitoring.
@@ -257,16 +269,15 @@
         type: list
         elements: str
       ipv6_address_count:
-        description: >
-          The number of IPv6 addresses to assign to a network interface. Amazon
-          EC2 automatically selects the IPv6 addresses from the subnet range.
-          You can't use this option if specifying the I(ipv6_addresses) option.
+        description:
+          - The number of IPv6 addresses to assign to a network interface.
+          - Amazon EC2 automatically selects the IPv6 addresses from the subnet range.
+          - You can't use this option if specifying the O(network_interfaces.ipv6_addresses) option.
         type: int
       ipv6_addresses:
-        description: >
-          A list of one or more specific IPv6 addresses from the IPv6 CIDR
-          block range of your subnet. You can't use this option if you're
-          specifying the I(ipv6_address_count) option.
+        description:
+          - A list of one or more specific IPv6 addresses from the IPv6 CIDR block range of your subnet.
+          - You can't use this option if you're specifying the O(network_interfaces.ipv6_address_count) option.
         type: list
         elements: str
       network_interface_id:
@@ -312,24 +323,41 @@
   security_groups:
     description: >
       A list of security group names (Default VPC or EC2-Classic) that the new instances will be added to.
-      For any VPC other than Default, you must use I(security_group_ids).
+      For any VPC other than Default, you must use O(security_group_ids).
     type: list
     elements: str
   source_version:
-    description: >
-      The version number of the launch template version on which to base the new version.
-      The new version inherits the same launch parameters as the source version, except for parameters that you explicity specify.
-      Snapshots applied to the block device mapping are ignored when creating a new version unless they are explicitly included.
+    description:
+      - The version number of the launch template version on which to base the new version.
+      - The new version inherits the same launch parameters as the source version, except for parameters that you explicity specify.
+      - Snapshots applied to the O(block_device_mappings) are ignored when creating a new version unless they are explicitly included.
     type: str
     default: latest
     version_added: 4.1.0
-  tags:
-    type: dict
+  tag_specifications:
     description:
-    - A set of key-value pairs to be applied to resources when this Launch Template is used.
-    - "Tag key constraints: Tag keys are case-sensitive and accept a maximum of 127 Unicode characters. May not begin with I(aws:)"
-    - "Tag value constraints: Tag values are case-sensitive and accept a maximum of 255 Unicode characters."
-    aliases: ['resource_tags']
+    - The tags to apply to the resources when this Launch template is used.
+    type: list
+    elements: dict
+    version_added: 9.0.0
+    suboptions:
+      resource_type:
+        description:
+          - The type of resource to tag.
+          - If the instance does not include the resource type that you specify, the instance launch fails.
+        type: str
+        default: instance
+        choices:
+          - instance
+          - volume
+          - network-interface
+          - spot-instances-request
+      tags:
+        description:
+        - A set of key-value pairs to be applied to the resource type.
+        - "Tag key constraints: Tag keys are case-sensitive and accept a maximum of 127 Unicode characters. May not begin with I(aws:)"
+        - "Tag value constraints: Tag values are case-sensitive and accept a maximum of 255 Unicode characters."
+        type: dict
   user_data:
     description: >
       The Base64-encoded user data to make available to the instance. For more information, see the Linux
@@ -347,40 +375,43 @@
     suboptions:
       http_endpoint:
         type: str
-        description: >
-          This parameter enables or disables the HTTP metadata endpoint on your instances.
+        description: This parameter enables or disables the HTTP metadata endpoint on your instances.
         choices: [enabled, disabled]
         default: 'enabled'
       http_put_response_hop_limit:
         type: int
-        description: >
-          The desired HTTP PUT response hop limit for instance metadata requests.
-          The larger the number, the further instance metadata requests can travel.
+        description:
+          - The desired HTTP PUT response hop limit for instance metadata requests.
+          - The larger the number, the further instance metadata requests can travel.
         default: 1
       http_tokens:
         type: str
-        description: >
-          The state of token usage for your instance metadata requests.
+        description: The state of token usage for your instance metadata requests.
         choices: [optional, required]
         default: 'optional'
       http_protocol_ipv6:
         version_added: 3.1.0
         type: str
-        description: >
-          - Wether the instance metadata endpoint is available via IPv6 (C(enabled)) or not (C(disabled)).
+        description:
+          - Whether the instance metadata endpoint is available via IPv6.
         choices: [enabled, disabled]
         default: 'disabled'
       instance_metadata_tags:
         version_added: 3.1.0
         type: str
         description:
-          - Wether the instance tags are availble (C(enabled)) via metadata endpoint or not (C(disabled)).
+          - Whether the instance tags are availble (V(enabled)) via metadata endpoint or not (V(disabled)).
         choices: [enabled, disabled]
         default: 'disabled'
+notes:
+  - The O(tags) option used has been in release 9.0.0 to be applied to the launch template resource instead of launch template resource.
+  - Use O(tag_specifications) to define tags to be applied to resources when this Launch Template is used.
+  - Support for O(purge_tags) was added in release 9.0.0.
 extends_documentation_fragment:
 - amazon.aws.common.modules
 - amazon.aws.region.modules
 - amazon.aws.boto3
+- amazon.aws.tags
 """
 
 EXAMPLES = r"""
@@ -406,122 +437,751 @@
     name: "my_template"
     state: absent
 
-# This module does not yet allow deletion of specific versions of launch templates
+- name: Delete a specific version of an ec2 launch template
+  community.aws.ec2_launch_template:
+    name: "my_template"
+    versions_to_delete:
+      - 2
+    state: absent
+
+- name: Delete a specific version of an ec2 launch template and change the default version
+  community.aws.ec2_launch_template:
+    name: "my_template"
+    versions_to_delete:
+      - 1
+    default_version: 2
+    state: absent
+
+- name: Create an ec2 launch template with specific tags
+  community.aws.ec2_launch_template:
+    name: "my_template"
+    image_id: "ami-04b762b4289fba92b"
+    instance_type: t2.micro
+    disable_api_termination: true
+    tags:
+      Some: tag
+      Another: tag
+
+- name: Create an ec2 launch template with different tag for volume and instance
+  community.aws.ec2_launch_template:
+    name: "my_template"
+    image_id: "ami-04b762b4289fba92b"
+    instance_type: t2.micro
+    block_device_mappings:
+      - device_name: /dev/sdb
+        ebs:
+          volume_size: 20
+          delete_on_termination: true
+          volume_type: standard
+    tag_specifications:
+      - resource_type: instance
+        tags:
+          OsType: Linux
+      - resource_type: volume
+        tags:
+          foo: bar
 """
 
 RETURN = r"""
 latest_version:
-  description: Latest available version of the launch template
-  returned: when state=present
+  description: The latest available version number of the launch template.
+  returned: when RV(latest_template) has a version number.
   type: int
 default_version:
   description: The version that will be used if only the template name is specified. Often this is the same as the latest version, but not always.
-  returned: when state=present
+  returned: when RV(default_template) has a version number.
   type: int
+template:
+  description: Latest available version of the launch template.
+  returned: when O(state=present)
+  type: complex
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    create_time:
+      description: The time launch template was created.
+      type: str
+      returned: always
+    created_by:
+      description: The principal that created the launch template.
+      type: str
+      returned: always
+    default_version_number:
+      description: The version number of the default version of the launch template.
+      type: int
+      returned: always
+    latest_version_number:
+      description: The version number of the latest version of the launch template.
+      type: int
+      returned: always
+    tags:
+      description: A dictionary of tags assigned to image.
+      returned: when AMI is created or already exists
+      type: dict
+      sample: {
+          "Env": "devel",
+          "Name": "nat-server"
+      }
+versions:
+  description: All available versions of the launch template.
+  returned: when O(state=present)
+  type: list
+  elements: dict
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    create_time:
+      description: The time the version was created.
+      type: str
+      returned: always
+    created_by:
+      description: The principal that created the version.
+      type: str
+      returned: always
+    default_version:
+      description: Indicates whether the version is the default version.
+      type: bool
+      returned: always
+    version_number:
+      description: The version number.
+      type: int
+      returned: always
+    version_description:
+      description: The description for the version.
+      type: str
+      returned: always
+    launch_template_data:
+      description: Information about the launch template.
+      returned: always
+      type: dict
+      contains:
+        kernel_id:
+          description:
+            - The ID of the kernel.
+          returned: if applicable
+          type: str
+        image_id:
+          description: The ID of the AMI or a Systems Manager parameter.
+          type: str
+          returned: if applicable
+        instance_type:
+          description: The instance type.
+          type: str
+          returned: if applicable
+        key_name:
+          description: The name of the key pair.
+          type: str
+          returned: if applicable
+        monitoring:
+          description: The monitoring for the instance.
+          type: dict
+          returned: if applicable
+          contains:
+            enabled:
+              description: Indicates whether detailed monitoring is enabled. Otherwise, basic monitoring is enabled.
+              type: bool
+              returned: always
+        placement:
+          description: The placement of the instance.
+          type: dict
+          returned: if applicable
+          contains:
+            availability_zone:
+              description: The Availability Zone of the instance.
+              type: str
+              returned: if applicable
+            affinity:
+              description: The affinity setting for the instance on the Dedicated Host.
+              type: str
+              returned: if applicable
+            group_name:
+              description: The name of the placement group for the instance.
+              type: str
+              returned: if applicable
+            host_id:
+              description: The ID of the Dedicated Host for the instance.
+              type: str
+              returned: if applicable
+            tenancy:
+              description: The tenancy of the instance.
+              type: str
+              returned: if applicable
+            host_resource_group_arn:
+              description: The ARN of the host resource group in which to launch the instances.
+              type: str
+              returned: if applicable
+            partition_number:
+              description: The number of the partition the instance should launch in.
+              type: int
+              returned: if applicable
+            group_id:
+              description: The Group ID of the placement group.
+              type: str
+              returned: if applicable
+        ebs_optimized:
+          description:
+            - Indicates whether the instance is optimized for Amazon EBS I/O.
+          type: bool
+          returned: always
+        iam_instance_profile:
+          description:
+            - The IAM instance profile.
+          type: dict
+          returned: if application
+          contains:
+            arn:
+              description: The Amazon Resource Name (ARN) of the instance profile.
+              type: str
+              returned: always
+            name:
+              description: The name of the instance profile.
+              type: str
+              returned: always
+        block_device_mappings:
+          description: The block device mappings.
+          type: list
+          elements: dict
+          returned: if applicable
+          contains:
+            device_name:
+              description: The device name.
+              type: str
+              returned: always
+            virtual_name:
+              description: The virtual device name.
+              type: str
+              returned: always
+            ebs:
+              description: Information about the block device for an EBS volume.
+              type: str
+              returned: if applicable
+              contains:
+                encrypted:
+                  description: Indicates whether the EBS volume is encrypted.
+                  type: bool
+                  returned: always
+                delete_on_termination:
+                  description: Indicates whether the EBS volume is deleted on instance termination.
+                  type: bool
+                  returned: always
+                iops:
+                  description: The number of I/O operations per second (IOPS) that the volume supports.
+                  type: int
+                  returned: always
+                kms_key_id:
+                  description: The ARN of the Key Management Service (KMS) CMK used for encryption.
+                  type: int
+                  returned: always
+                snapshot_id:
+                  description: The ID of the snapshot.
+                  type: str
+                  returned: always
+                volume_size:
+                  description: The size of the volume, in GiB.
+                  type: int
+                  returned: always
+                volume_type:
+                  description: The volume type.
+                  type: str
+                  returned: always
+                throughput:
+                  description: The throughput that the volume supports, in MiB/s.
+                  type: int
+                  returned: always
+            no_device:
+              description: To omit the device from the block device mapping, specify an empty string.
+              type: str
+        network_interfaces:
+          description: The network interfaces.
+          type: list
+          elements: dict
+          returned: if applicable
+          contains:
+            associate_carrier_ip_address:
+              description: Indicates whether to associate a Carrier IP address with eth0 for a new network interface.
+              type: bool
+              returned: always
+            associate_public_ip_address:
+              description: Indicates whether to associate a public IPv4 address with eth0 for a new network interface.
+              type: bool
+              returned: always
+            delete_on_termination:
+              description: Indicates whether the network interface is deleted when the instance is terminated.
+              type: bool
+              returned: always
+            description:
+              description: A description for the network interface.
+              type: str
+              returned: always
+            device_index:
+              description: The device index for the network interface attachment.
+              type: int
+              returned: always
+            groups:
+              description: The IDs of one or more security groups.
+              type: list
+              elements: str
+              returned: if applicable
+            interface_type:
+              description: The type of network interface.
+              type: str
+              returned: always
+            ipv6_address_count:
+              description: The number of IPv6 addresses for the network interface.
+              type: int
+              returned: if applicable
+            ipv6_addresses:
+              description: The IPv6 addresses for the network interface.
+              returned: if applicable
+              type: list
+              elements: dict
+              contains:
+                ipv6_address:
+                  description: The IPv6 address.
+                  type: str
+                  returned: always
+                is_primary_ipv6:
+                  description: Determines if an IPv6 address associated with a network interface is the primary IPv6 address.
+                  type: bool
+                  returned: always
+            network_interface_id:
+              description: The ID of the network interface.
+              type: str
+              returned: always
+            private_ip_address:
+              description: The primary private IPv4 address of the network interface.
+              type: str
+              returned: if applicable
+            private_ip_addresses:
+              description: A list of private IPv4 addresses.
+              type: list
+              elements: str
+              returned: if applicable
+              contains:
+                primary:
+                  description: Indicates whether the private IPv4 address is the primary private IPv4 address.
+                  type: bool
+                  returned: always
+                private_ip_address:
+                  description: The private IPv4 address.
+                  type: bool
+                  returned: always
+            secondary_private_ip_address_count:
+              description: The number of secondary private IPv4 addresses for the network interface.
+              type: int
+              returned: if applicable
+            subnet_id:
+              description: The ID of the subnet for the network interface.
+              type: str
+              returned: always
+            network_card_index:
+              description: The index of the network card.
+              type: int
+              returned: if applicable
+            ipv4_prefixes:
+              description: A list of IPv4 prefixes assigned to the network interface.
+              type: list
+              elements: dict
+              returned: if applicable
+              contains:
+                ipv4_prefix:
+                  description: The IPv4 delegated prefixes assigned to the network interface.
+                  type: str
+                  returned: always
+            ipv4_prefix_count:
+              description: The number of IPv4 prefixes that Amazon Web Services automatically assigned to the network interface.
+              type: int
+              returned: if applicable
+            ipv6_prefixes:
+              description: A list of IPv6 prefixes assigned to the network interface.
+              type: list
+              elements: dict
+              returned: if applicable
+              contains:
+                ipv6_prefix:
+                  description: The IPv6 delegated prefixes assigned to the network interface.
+                  type: str
+                  returned: always
+            ipv6_prefix_count:
+              description: The number of IPv6 prefixes that Amazon Web Services automatically assigned to the network interface.
+              type: int
+              returned: if applicable
+            primary_ipv6:
+              description: The primary IPv6 address of the network interface.
+              type: str
+              returned: if applicable
+            ena_srd_specification:
+              description: Contains the ENA Express settings for instances launched from your launch template.
+              type: dict
+              returned: if applicable
+              contains:
+                ena_srd_enabled:
+                  description: Indicates whether ENA Express is enabled for the network interface.
+                  type: bool
+                  returned: always
+                ena_srd_udp_specification:
+                  description: Configures ENA Express for UDP network traffic.
+                  type: dict
+                  returned: always
+                  contains:
+                    ena_srd_udp_enabled:
+                      description: Indicates whether UDP traffic to and from the instance uses ENA Express.
+                      type: bool
+                      returned: always
+            connection_tracking_specification:
+              description:
+                - A security group connection tracking specification that enables you to set the timeout
+                  for connection tracking on an Elastic network interface.
+              type: dict
+              returned: if applicable
+              contains:
+                tcp_established_timeout:
+                  description: Timeout (in seconds) for idle TCP connections in an established state.
+                  type: int
+                  returned: always
+                udp_timeout:
+                  description:
+                    - Timeout (in seconds) for idle UDP flows that have seen traffic only in a single direction
+                      or a single request-response transaction.
+                  type: int
+                  returned: always
+                udp_stream_timeout:
+                  description:
+                    - Timeout (in seconds) for idle UDP flows classified as streams which have seen more
+                      than one request-response transaction.
+                  type: int
+                  returned: always
+        ram_disk_id:
+          description: The ID of the RAM disk, if applicable.
+          type: str
+          returned: if applicable
+        disable_api_termination:
+          description: If set to true, indicates that the instance cannot be terminated using the Amazon EC2 console, command line tool, or API.
+          type: bool
+          returned: if applicable
+        instance_initiated_shutdown_behavior:
+          description: Indicates whether an instance stops or terminates when you initiate shutdown from the instance.
+          type: str
+          returned: if applicable
+        user_data:
+          description: The user data for the instance.
+          type: str
+          returned: if applicable
+        tag_specifications:
+          description: The tags that are applied to the resources that are created during instance launch.
+          type: list
+          elements: dict
+          returned: if applicable
+          contains:
+            resource_type:
+              description: The type of resource to tag.
+              type: str
+              returned: always
+            tags:
+              description: The tags for the resource.
+              type: list
+              elements: dict
+              contains:
+                key:
+                  description: The key of the tag.
+                  type: str
+                  returned: always
+                value:
+                  description: The value of the tag.
+                  type: str
+                  returned: always
+        enclave_options:
+          description: Indicates whether the instance is enabled for Amazon Web Services Nitro Enclaves.
+          type: dict
+          returned: if applicable
+          contains:
+            enabled:
+              description: If this parameter is set to true, the instance is enabled for Amazon Web Services Nitro Enclaves.
+              type: bool
+              returned: always
+        metadata_options:
+          description: The metadata options for the instance.
+          type: dict
+          returned: if applicable
+          contains:
+            state:
+              description: The state of the metadata option changes.
+              type: str
+              returned: if applicable
+            http_tokens:
+              description: Indicates whether IMDSv2 is required.
+              type: str
+              returned: if applicable
+            http_put_response_hop_limit:
+              description: The desired HTTP PUT response hop limit for instance metadata requests.
+              type: int
+              returned: if applicable
+            http_endpoint:
+              description: Enables or disables the HTTP metadata endpoint on your instances.
+              type: str
+              returned: if applicable
+            http_protocol_ipv6:
+              description: Enables or disables the IPv6 endpoint for the instance metadata service.
+              type: str
+              returned: if applicable
+            instance_metadata_tags:
+              description: Set to enabled to allow access to instance tags from the instance metadata.
+              type: str
+              returned: if applicable
+        cpu_options:
+          description: The CPU options for the instance.
+          type: dict
+          returned: if applicable
+          contains:
+            core_count:
+              description: The number of CPU cores for the instance.
+              type: int
+              returned: if applicable
+            threads_per_core:
+              description: The number of threads per CPU core.
+              type: int
+              returned: if applicable
+            amd_sev_snp:
+              description: Indicates whether the instance is enabled for AMD SEV-SNP.
+              type: int
+              returned: if applicable
+        security_group_ids:
+          description: The security group IDs.
+          type: list
+          elements: str
+          returned: if applicable
+        security_groups:
+          description: The security group names.
+          type: list
+          elements: str
+          returned: if applicable
+      sample: {
+          "block_device_mappings": [
+              {
+                  "device_name": "/dev/sdb",
+                  "ebs": {
+                      "delete_on_termination": true,
+                      "encrypted": true,
+                      "volumeSize": 5
+                  }
+              }
+          ],
+          "ebs_optimized": false,
+          "image_id": "ami-0231217be14a6f3ba",
+          "instance_type": "t2.micro",
+          "network_interfaces": [
+              {
+                  "associate_public_ip_address": false,
+                  "device_index": 0,
+                  "ipv6_addresses": [
+                      {
+                          "ipv6_address": "2001:0:130F:0:0:9C0:876A:130B"
+                      }
+                  ]
+              }
+          ]
+      }
+latest_template:
+  description: The latest available version of the launch template.
+  returned: when O(state=present)
+  type: complex
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    create_time:
+      description: The time launch template was created.
+      type: str
+      returned: always
+    created_by:
+      description: The principal that created the launch template.
+      type: str
+      returned: always
+    default_version_number:
+      description: The version number of the default version of the launch template.
+      type: int
+      returned: always
+    latest_version_number:
+      description: The version number of the latest version of the launch template.
+      type: int
+      returned: always
+    tags:
+      description: A dictionary of tags assigned to image.
+      returned: when AMI is created or already exists
+      type: dict
+      sample: {
+          "Env": "devel",
+          "Name": "nat-server"
+      }
+default_template:
+  description:
+    - The launch template version that will be used if only the template name is specified.
+    - Often this is the same as the latest version, but not always.
+  returned: when O(state=present)
+  type: complex
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    create_time:
+      description: The time launch template was created.
+      type: str
+      returned: always
+    created_by:
+      description: The principal that created the launch template.
+      type: str
+      returned: always
+    default_version_number:
+      description: The version number of the default version of the launch template.
+      type: int
+      returned: always
+    latest_version_number:
+      description: The version number of the latest version of the launch template.
+      type: int
+      returned: always
+    tags:
+      description: A dictionary of tags assigned to image.
+      returned: when AMI is created or already exists
+      type: dict
+      sample: {
+          "Env": "devel",
+          "Name": "nat-server"
+      }
+deleted_template:
+  description: information about a launch template deleted.
+  returned: when O(state=absent)
+  type: complex
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    create_time:
+      description: The time launch template was created.
+      type: str
+      returned: always
+    created_by:
+      description: The principal that created the launch template.
+      type: str
+      returned: always
+    default_version_number:
+      description: The version number of the default version of the launch template.
+      type: int
+      returned: always
+    latest_version_number:
+      description: The version number of the latest version of the launch template.
+      type: int
+      returned: always
+    tags:
+      description: A dictionary of tags assigned to image.
+      returned: when AMI is created or already exists
+      type: dict
+      sample: {
+          "Env": "devel",
+          "Name": "nat-server"
+      }
+deleted_versions:
+  description: Information about deleted launch template versions.
+  returned: when O(state=absent)
+  type: list
+  elements: dict
+  contains:
+    launch_template_id:
+      description: The ID of the launch template.
+      type: str
+      returned: always
+    launch_template_name:
+      description: The name of the launch template.
+      type: str
+      returned: always
+    version_number:
+      description: The version number of the launch template.
+      type: int
+      returned: always
 """
 
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Tuple
 from uuid import uuid4
 
-try:
-    from botocore.exceptions import BotoCoreError
-    from botocore.exceptions import ClientError
-    from botocore.exceptions import WaiterError
-except ImportError:
-    pass  # caught by AnsibleAWSModule
-
 from ansible.module_utils._text import to_text
 from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict
 from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict
 
-from ansible_collections.amazon.aws.plugins.module_utils.arn import validate_aws_arn
-from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code
-from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry
-from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list
+from ansible_collections.amazon.aws.plugins.module_utils.botocore import normalize_boto3_result
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleEC2Error
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import create_launch_template
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import create_launch_template_version
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import delete_launch_template
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import delete_launch_template_versions
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_launch_template_versions
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_launch_templates
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import determine_iam_arn_from_name
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import modify_launch_template
 from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict
+from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications
 from ansible_collections.amazon.aws.plugins.module_utils.transformation import scrub_none_parameters
 
 from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule
 
 
-def determine_iam_role(module, name_or_arn):
-    if validate_aws_arn(name_or_arn, service="iam", resource_type="instance-profile"):
-        return {"arn": name_or_arn}
-    iam = module.client("iam", retry_decorator=AWSRetry.jittered_backoff())
-    try:
-        role = iam.get_instance_profile(InstanceProfileName=name_or_arn, aws_retry=True)
-        return {"arn": role["InstanceProfile"]["Arn"]}
-    except is_boto3_error_code("NoSuchEntity") as e:
-        module.fail_json_aws(e, msg=f"Could not find instance_role {name_or_arn}")
-    except (BotoCoreError, ClientError) as e:  # pylint: disable=duplicate-except
-        module.fail_json_aws(
-            e,
-            msg=f"An error occurred while searching for instance_role {name_or_arn}. Please try supplying the full ARN.",
-        )
-
-
-def existing_templates(module):
-    ec2 = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff())
-    matches = None
-    try:
-        if module.params.get("template_id"):
-            matches = ec2.describe_launch_templates(
-                LaunchTemplateIds=[module.params.get("template_id")], aws_retry=True
-            )
-        elif module.params.get("template_name"):
-            matches = ec2.describe_launch_templates(
-                LaunchTemplateNames=[module.params.get("template_name")], aws_retry=True
-            )
-    except is_boto3_error_code("InvalidLaunchTemplateName.NotFoundException") as e:
-        # no named template was found, return nothing/empty versions
-        return None, []
-    except is_boto3_error_code("InvalidLaunchTemplateId.Malformed") as e:  # pylint: disable=duplicate-except
-        module.fail_json_aws(
-            e,
-            msg=(
-                f"Launch template with ID {module.params.get('launch_template_id')} is not a valid ID. It should start"
-                " with `lt-....`"
-            ),
-        )
-    except is_boto3_error_code("InvalidLaunchTemplateId.NotFoundException") as e:  # pylint: disable=duplicate-except
-        module.fail_json_aws(
-            e,
-            msg=(
-                f"Launch template with ID {module.params.get('launch_template_id')} could not be found, please supply a"
-                " name instead so that a new template can be created"
-            ),
-        )
-    except (ClientError, BotoCoreError, WaiterError) as e:  # pylint: disable=duplicate-except
-        module.fail_json_aws(e, msg="Could not check existing launch templates. This may be an IAM permission problem.")
+def find_existing(client, module: AnsibleAWSModule) -> Tuple[Optional[Dict[str, Any]], Optional[List[Dict[str, Any]]]]:
+    launch_template = None
+    launch_template_versions = []
+    params = {}
+    template_id = module.params.get("template_id")
+    template_name = module.params.get("template_name")
+    if template_id:
+        params["launch_template_ids"] = [template_id]
     else:
-        template = matches["LaunchTemplates"][0]
-        template_id, template_version, template_default = (
-            template["LaunchTemplateId"],
-            template["LatestVersionNumber"],
-            template["DefaultVersionNumber"],
+        params["launch_template_names"] = [template_name]
+    launch_templates = describe_launch_templates(client, **params)
+    if launch_templates:
+        launch_template = launch_templates[0]
+        launch_template_versions = describe_launch_template_versions(
+            client, LaunchTemplateId=launch_template["LaunchTemplateId"]
         )
-        try:
-            return (
-                template,
-                ec2.describe_launch_template_versions(LaunchTemplateId=template_id, aws_retry=True)[
-                    "LaunchTemplateVersions"
-                ],
-            )
-        except (ClientError, BotoCoreError, WaiterError) as e:
-            module.fail_json_aws(
-                e,
-                msg=f"Could not find launch template versions for {template['LaunchTemplateName']} (ID: {template_id}).",
-            )
+    return normalize_boto3_result(launch_template), normalize_boto3_result(launch_template_versions)
 
 
-def params_to_launch_data(module, template_params):
-    if template_params.get("tags"):
-        tag_list = ansible_dict_to_boto3_tag_list(template_params.get("tags"))
-        template_params["tag_specifications"] = [
-            {"resource_type": r_type, "tags": tag_list} for r_type in ("instance", "volume")
-        ]
-        del template_params["tags"]
-    if module.params.get("iam_instance_profile"):
-        template_params["iam_instance_profile"] = determine_iam_role(module, module.params["iam_instance_profile"])
+def params_to_launch_data(
+    template_params: Dict[str, Any], iam_instance_profile_arn: Optional[str] = None
+) -> Dict[str, Any]:
+    if iam_instance_profile_arn:
+        template_params["iam_instance_profile"] = {"arn": iam_instance_profile_arn}
+    for interface in template_params.get("network_interfaces") or []:
+        if interface.get("ipv6_addresses"):
+            interface["ipv6_addresses"] = [{"ipv6_address": x} for x in interface["ipv6_addresses"]]
     params = snake_dict_to_camel_dict(
         dict((k, v) for k, v in template_params.items() if v is not None),
         capitalize_first=True,
@@ -529,170 +1189,279 @@ def params_to_launch_data(module, template_params):
     return params
 
 
-def delete_template(module):
-    ec2 = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff())
-    template, template_versions = existing_templates(module)
+def validate_string_as_int(module: AnsibleAWSModule, version: str, param_name: str) -> int:
+    try:
+        return int(version)
+    except ValueError:
+        module.fail_json(msg=f'{param_name} param was not a valid integer, got "{version}"')
+
+
+def validate_version_deletion(
+    module: AnsibleAWSModule, launch_template_id: str, existing_versions: List[Dict[str, Any]]
+) -> Tuple[List[str], Optional[int]]:
+    versions_to_delete = module.params.get("versions_to_delete")
+    launch_template_versions_to_delete = []
+    default_version_to_set = None
+    if versions_to_delete:
+        unique_versions_to_delete = list(set(versions_to_delete))
+        launch_template_versions_to_delete = [
+            t["VersionNumber"] for t in existing_versions if t["VersionNumber"] in unique_versions_to_delete
+        ]
+        if len(launch_template_versions_to_delete) != len(unique_versions_to_delete):
+            missing = [m for m in unique_versions_to_delete if m not in launch_template_versions_to_delete]
+            module.fail_json(
+                msg=f"The following versions {missing} do not exist for launch template id '{launch_template_id}'."
+            )
+
+        remaining_versions = [
+            t["VersionNumber"]
+            for t in existing_versions
+            if t["VersionNumber"] not in launch_template_versions_to_delete
+        ]
+
+        # Find the default version
+        default_version = module.params.get("default_version")
+        if default_version in (None, ""):
+            default_version_int = [t["VersionNumber"] for t in existing_versions if t["DefaultVersion"]][0]
+        elif default_version == "latest":
+            default_version_int = max(remaining_versions, default=None)
+            default_version_to_set = default_version_int
+        else:
+            default_version_int = validate_string_as_int(module, default_version, "default_version")
+            default_version_to_set = default_version_int
+
+        # Ensure we are not deleting the default version
+        if default_version_int in launch_template_versions_to_delete or not remaining_versions:
+            module.fail_json(msg="Cannot delete the launch template default version.")
+
+        if default_version_to_set and default_version_to_set not in remaining_versions:
+            module.fail_json(
+                msg=f"Could not set version '{default_version_to_set}' as default, "
+                "the launch template version was not found for the specified launch template id '{launch_template_id}'."
+            )
+    else:
+        # By default delete all non default version before the launch template deletion
+        launch_template_versions_to_delete = [t["VersionNumber"] for t in existing_versions if not t["DefaultVersion"]]
+
+    return [to_text(v) for v in launch_template_versions_to_delete], default_version_to_set
+
+
+def ensure_absent(
+    client, module: AnsibleAWSModule, existing: Optional[Dict[str, Any]], existing_versions: List[Dict[str, Any]]
+) -> None:
     deleted_versions = []
-    if template or template_versions:
-        non_default_versions = [to_text(t["VersionNumber"]) for t in template_versions if not t["DefaultVersion"]]
-        if non_default_versions:
-            try:
-                v_resp = ec2.delete_launch_template_versions(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    Versions=non_default_versions,
-                    aws_retry=True,
+    deleted_template = {}
+    changed = False
+
+    if existing:
+        launch_template_id = existing["LaunchTemplateId"]
+        v_to_delete, v_default = validate_version_deletion(module, launch_template_id, existing_versions)
+
+        # Update default version
+        if v_default:
+            changed = True
+            if not module.check_mode:
+                modify_launch_template(
+                    client,
+                    LaunchTemplateId=launch_template_id,
+                    ClientToken=uuid4().hex,
+                    DefaultVersion=to_text(v_default),
                 )
-                if v_resp["UnsuccessfullyDeletedLaunchTemplateVersions"]:
+        # Delete versions
+        if v_to_delete:
+            changed = True
+            if not module.check_mode:
+                response = delete_launch_template_versions(
+                    client, launch_template_id=launch_template_id, versions=v_to_delete
+                )
+                if response["UnsuccessfullyDeletedLaunchTemplateVersions"]:
                     module.warn(
-                        f"Failed to delete template versions {v_resp['UnsuccessfullyDeletedLaunchTemplateVersions']} on"
-                        f" launch template {template['LaunchTemplateId']}"
+                        f"Failed to delete template versions {response['UnsuccessfullyDeletedLaunchTemplateVersions']} on"
+                        f" launch template {launch_template_id}"
                     )
                 deleted_versions = [
-                    camel_dict_to_snake_dict(v) for v in v_resp["SuccessfullyDeletedLaunchTemplateVersions"]
+                    camel_dict_to_snake_dict(v) for v in response["SuccessfullyDeletedLaunchTemplateVersions"]
                 ]
-            except (ClientError, BotoCoreError) as e:
-                module.fail_json_aws(
-                    e,
-                    msg=f"Could not delete existing versions of the launch template {template['LaunchTemplateId']}",
-                )
-        try:
-            resp = ec2.delete_launch_template(
-                LaunchTemplateId=template["LaunchTemplateId"],
-                aws_retry=True,
-            )
-        except (ClientError, BotoCoreError) as e:
-            module.fail_json_aws(e, msg=f"Could not delete launch template {template['LaunchTemplateId']}")
-        return {
-            "deleted_versions": deleted_versions,
-            "deleted_template": camel_dict_to_snake_dict(resp["LaunchTemplate"]),
-            "changed": True,
-        }
-    else:
-        return {"changed": False}
 
+        # Delete the launch template when a list of versions was not specified
+        if not module.params.get("versions_to_delete"):
+            changed = True
+            if not module.check_mode:
+                deleted_template = delete_launch_template(client, launch_template_id=launch_template_id)
+                deleted_template = camel_dict_to_snake_dict(deleted_template, ignore_list=["Tags"])
+                if "tags" in deleted_template:
+                    deleted_template["tags"] = boto3_tag_list_to_ansible_dict(deleted_template.get("tags"))
+
+    module.exit_json(changed=changed, deleted_versions=deleted_versions, deleted_template=deleted_template)
+
+
+def add_launch_template_version(
+    client,
+    module: AnsibleAWSModule,
+    launch_template_id: str,
+    launch_template_data: Dict[str, Any],
+    existing_versions: List[Dict[str, Any]],
+    most_recent_version_number: str,
+) -> int:
+    source_version = module.params.get("source_version")
+    version_description = module.params.get("version_description")
+
+    params = {
+        "LaunchTemplateId": launch_template_id,
+        "ClientToken": uuid4().hex,
+        "VersionDescription": version_description,
+    }
+
+    if source_version == "latest":
+        params.update({"SourceVersion": most_recent_version_number})
+    elif source_version not in (None, ""):
+        # Source version passed as int
+        source_version_int = validate_string_as_int(module, source_version, "source_version")
+        # get source template version
+        next_source_version = next(
+            (v for v in existing_versions if v["VersionNumber"] == source_version_int),
+            None,
+        )
+        if next_source_version is None:
+            module.fail_json(msg=f'source_version does not exist, got "{source_version}"')
+        params.update({"SourceVersion": str(next_source_version["VersionNumber"])})
+
+    if module.check_mode:
+        module.exit_json(changed=True, msg="Would have created launch template version if not in check mode.")
 
-def create_or_update(module, template_options):
-    ec2 = module.client(
-        "ec2", retry_decorator=AWSRetry.jittered_backoff(catch_extra_error_codes=["InvalidLaunchTemplateId.NotFound"])
+    # Create Launch template version
+    launch_template_version = create_launch_template_version(
+        client, launch_template_data=launch_template_data, **params
     )
-    template, template_versions = existing_templates(module)
-    out = {}
-    lt_data = params_to_launch_data(module, dict((k, v) for k, v in module.params.items() if k in template_options))
-    lt_data = scrub_none_parameters(lt_data, descend_into_lists=True)
-
-    if not (template or template_versions):
-        # create a full new one
-        try:
-            resp = ec2.create_launch_template(
-                LaunchTemplateName=module.params["template_name"],
-                LaunchTemplateData=lt_data,
-                ClientToken=uuid4().hex,
-                aws_retry=True,
-            )
-        except (ClientError, BotoCoreError) as e:
-            module.fail_json_aws(e, msg="Couldn't create launch template")
-        template, template_versions = existing_templates(module)
-        out["changed"] = True
-    elif template and template_versions:
-        most_recent = sorted(template_versions, key=lambda x: x["VersionNumber"])[-1]
-        if lt_data == most_recent["LaunchTemplateData"] and module.params["version_description"] == most_recent.get(
-            "VersionDescription", ""
-        ):
-            out["changed"] = False
-            return out
-        try:
-            if module.params.get("source_version") in (None, ""):
-                resp = ec2.create_launch_template_version(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    LaunchTemplateData=lt_data,
-                    ClientToken=uuid4().hex,
-                    VersionDescription=str(module.params["version_description"]),
-                    aws_retry=True,
-                )
-            elif module.params.get("source_version") == "latest":
-                resp = ec2.create_launch_template_version(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    LaunchTemplateData=lt_data,
-                    ClientToken=uuid4().hex,
-                    SourceVersion=str(most_recent["VersionNumber"]),
-                    VersionDescription=str(module.params["version_description"]),
-                    aws_retry=True,
-                )
-            else:
-                try:
-                    int(module.params.get("source_version"))
-                except ValueError:
-                    module.fail_json(
-                        msg=f"source_version param was not a valid integer, got \"{module.params.get('source_version')}\""
-                    )
-                # get source template version
-                source_version = next(
-                    (v for v in template_versions if v["VersionNumber"] == int(module.params.get("source_version"))),
-                    None,
-                )
-                if source_version is None:
-                    module.fail_json(
-                        msg=f"source_version does not exist, got \"{module.params.get('source_version')}\""
-                    )
-                resp = ec2.create_launch_template_version(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    LaunchTemplateData=lt_data,
-                    ClientToken=uuid4().hex,
-                    SourceVersion=str(source_version["VersionNumber"]),
-                    VersionDescription=str(module.params["version_description"]),
-                    aws_retry=True,
-                )
+    return launch_template_version["VersionNumber"]
 
-            if module.params.get("default_version") in (None, ""):
-                # no need to do anything, leave the existing version as default
-                pass
-            elif module.params.get("default_version") == "latest":
-                set_default = ec2.modify_launch_template(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    DefaultVersion=to_text(resp["LaunchTemplateVersion"]["VersionNumber"]),
-                    ClientToken=uuid4().hex,
-                    aws_retry=True,
-                )
-            else:
-                try:
-                    int(module.params.get("default_version"))
-                except ValueError:
-                    module.fail_json(
-                        msg=f"default_version param was not a valid integer, got \"{module.params.get('default_version')}\""
-                    )
-                set_default = ec2.modify_launch_template(
-                    LaunchTemplateId=template["LaunchTemplateId"],
-                    DefaultVersion=to_text(int(module.params.get("default_version"))),
+
+def ensure_default_version(
+    client,
+    module: AnsibleAWSModule,
+    launch_template_id: str,
+    current_default_version_number: int,
+    most_recent_version_number: int,
+) -> bool:
+    # Modify default version
+    default_version = module.params.get("default_version")
+    changed = False
+    if default_version not in (None, ""):
+        if default_version == "latest":
+            default_version = to_text(most_recent_version_number)
+        else:
+            default_version = to_text(validate_string_as_int(module, default_version, "default_version"))
+        if to_text(current_default_version_number) != default_version:
+            changed = True
+            if not module.check_mode:
+                modify_launch_template(
+                    client,
+                    LaunchTemplateId=launch_template_id,
                     ClientToken=uuid4().hex,
-                    aws_retry=True,
+                    DefaultVersion=default_version,
                 )
-        except (ClientError, BotoCoreError) as e:
-            module.fail_json_aws(e, msg="Couldn't create subsequent launch template version")
-        template, template_versions = existing_templates(module)
-        out["changed"] = True
-    return out
+    return changed
 
 
-def format_module_output(module):
-    output = {}
-    template, template_versions = existing_templates(module)
-    template = camel_dict_to_snake_dict(template)
+def format_module_output(client, module: AnsibleAWSModule) -> Dict[str, Any]:
+    # Describe launch template
+    template, template_versions = find_existing(client, module)
+    template = camel_dict_to_snake_dict(template, ignore_list=["Tags"])
+    if "tags" in template:
+        template["tags"] = boto3_tag_list_to_ansible_dict(template.get("tags"))
     template_versions = [camel_dict_to_snake_dict(v) for v in template_versions]
-    for v in template_versions:
-        for ts in v["launch_template_data"].get("tag_specifications") or []:
-            ts["tags"] = boto3_tag_list_to_ansible_dict(ts.pop("tags"))
-    output.update(dict(template=template, versions=template_versions))
-    output["default_template"] = [v for v in template_versions if v.get("default_version")][0]
-    output["latest_template"] = [
-        v
-        for v in template_versions
-        if (v.get("version_number") and int(v["version_number"]) == int(template["latest_version_number"]))
-    ][0]
-    if "version_number" in output["default_template"]:
-        output["default_version"] = output["default_template"]["version_number"]
-    if "version_number" in output["latest_template"]:
-        output["latest_version"] = output["latest_template"]["version_number"]
-    return output
+    result = {
+        "template": template,
+        "versions": template_versions,
+        "default_template": [v for v in template_versions if v.get("default_version")][0],
+        "latest_template": [
+            v
+            for v in template_versions
+            if (v.get("version_number") and int(v["version_number"]) == int(template["latest_version_number"]))
+        ][0],
+    }
+    if "version_number" in result["default_template"]:
+        result["default_version"] = result["default_template"]["version_number"]
+    if "version_number" in result["latest_template"]:
+        result["latest_version"] = result["latest_template"]["version_number"]
+    return result
+
+
+def ensure_present(
+    client,
+    module: AnsibleAWSModule,
+    template_options: Dict[str, Any],
+    existing: Optional[Dict[str, Any]],
+    existing_versions: List[Dict[str, Any]],
+) -> None:
+    template_name = module.params["template_name"]
+    tags = module.params["tags"]
+    tag_specifications = module.params.get("tag_specifications")
+    version_description = module.params.get("version_description")
+    iam_instance_profile = module.params.get("iam_instance_profile")
+    # IAM instance profile
+    if iam_instance_profile:
+        iam_instance_profile = determine_iam_arn_from_name(module.client("iam"), iam_instance_profile)
+    # Convert Launch template data
+    launch_template_data = params_to_launch_data(
+        dict((k, v) for k, v in module.params.items() if k in template_options), iam_instance_profile
+    )
+    # Tag specifications
+    if tag_specifications:
+        boto3_tag_specs = []
+        for tag_spec in tag_specifications:
+            boto3_tag_specs.extend(boto3_tag_specifications(tag_spec["tags"], types=tag_spec["resource_type"]))
+        launch_template_data["TagSpecifications"] = boto3_tag_specs
+    launch_template_data = scrub_none_parameters(launch_template_data, descend_into_lists=True)
+    changed = False
+
+    if not (existing or existing_versions):
+        # Create Launch template
+        if module.check_mode:
+            module.exit_json(changed=True, msg="Would have created launch template if not in check mode.")
+        create_launch_template(
+            client,
+            launch_template_name=template_name,
+            launch_template_data=launch_template_data,
+            tags=tags,
+            ClientToken=uuid4().hex,
+            VersionDescription=version_description,
+        )
+        changed = True
+    else:
+        launch_template_id = existing["LaunchTemplateId"]
+        default_version_number = existing["DefaultVersionNumber"]
+        most_recent = sorted(existing_versions, key=lambda x: x["VersionNumber"])[-1]
+        most_recent_version_number = most_recent["VersionNumber"]
+        if not (
+            launch_template_data == most_recent["LaunchTemplateData"]
+            and version_description == most_recent.get("VersionDescription", "")
+        ):
+            changed = True
+            most_recent_version_number = add_launch_template_version(
+                client,
+                module,
+                launch_template_id,
+                launch_template_data,
+                existing_versions,
+                str(most_recent["VersionNumber"]),
+            )
+
+        # Ensure default version
+        changed |= ensure_default_version(
+            client, module, launch_template_id, default_version_number, most_recent_version_number
+        )
+        # Ensure tags
+        changed |= ensure_ec2_tags(
+            client,
+            module,
+            launch_template_id,
+            resource_type="launch-template",
+            tags=tags,
+            purge_tags=module.params["purge_tags"],
+        )
+
+    module.exit_json(changed=changed, **format_module_output(client, module))
 
 
 def main():
@@ -712,6 +1481,7 @@ def main():
                         snapshot_id=dict(),
                         volume_size=dict(type="int"),
                         volume_type=dict(),
+                        throughput=dict(type="int"),
                     ),
                 ),
                 no_device=dict(),
@@ -738,7 +1508,6 @@ def main():
             type="list",
             elements="dict",
         ),
-        iam_instance_profile=dict(),
         image_id=dict(),
         instance_initiated_shutdown_behavior=dict(choices=["stop", "terminate"]),
         instance_market_options=dict(
@@ -802,42 +1571,55 @@ def main():
         ram_disk_id=dict(),
         security_group_ids=dict(type="list", elements="str"),
         security_groups=dict(type="list", elements="str"),
-        tags=dict(type="dict", aliases=["resource_tags"]),
         user_data=dict(),
     )
 
-    arg_spec = dict(
+    argument_spec = dict(
         state=dict(choices=["present", "absent"], default="present"),
         template_name=dict(aliases=["name"]),
         template_id=dict(aliases=["id"]),
         default_version=dict(default="latest"),
         source_version=dict(default="latest"),
         version_description=dict(default=""),
+        iam_instance_profile=dict(),
+        tags=dict(type="dict", aliases=["resource_tags"]),
+        purge_tags=dict(type="bool", default=True),
+        versions_to_delete=dict(type="list", elements="int"),
+        tag_specifications=dict(
+            type="list",
+            elements="dict",
+            options=dict(
+                resource_type=dict(
+                    type="str",
+                    default="instance",
+                    choices=["instance", "volume", "network-interface", "spot-instances-request"],
+                ),
+                tags=dict(type="dict"),
+            ),
+        ),
     )
 
-    arg_spec.update(template_options)
+    argument_spec.update(template_options)
 
     module = AnsibleAWSModule(
-        argument_spec=arg_spec,
+        argument_spec=argument_spec,
         required_one_of=[
             ("template_name", "template_id"),
         ],
         supports_check_mode=True,
     )
 
-    for interface in module.params.get("network_interfaces") or []:
-        if interface.get("ipv6_addresses"):
-            interface["ipv6_addresses"] = [{"ipv6_address": x} for x in interface["ipv6_addresses"]]
-
-    if module.params.get("state") == "present":
-        out = create_or_update(module, template_options)
-        out.update(format_module_output(module))
-    elif module.params.get("state") == "absent":
-        out = delete_template(module)
-    else:
-        module.fail_json(msg=f"Unsupported value \"{module.params.get('state')}\" for `state` parameter")
+    state = module.params.get("state")
+    client = module.client("ec2")
+    launch_template, launch_template_versions = find_existing(client, module)
 
-    module.exit_json(**out)
+    try:
+        if state == "present":
+            ensure_present(client, module, template_options, launch_template, launch_template_versions)
+        else:
+            ensure_absent(client, module, launch_template, launch_template_versions)
+    except AnsibleEC2Error as e:
+        module.fail_json_aws_error(e)
 
 
 if __name__ == "__main__":


Test patch:
diff --git a/tests/integration/targets/ec2_launch_template/aliases b/tests/integration/targets/ec2_launch_template/aliases
index 4ef4b2067d0..42f0f3c880b 100644
--- a/tests/integration/targets/ec2_launch_template/aliases
+++ b/tests/integration/targets/ec2_launch_template/aliases
@@ -1,1 +1,3 @@
 cloud/aws
+time=3m
+ec2_launch_template_info
\ No newline at end of file
diff --git a/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml b/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml
deleted file mode 100644
index 92d7fac5fe1..00000000000
--- a/tests/integration/targets/ec2_launch_template/tasks/cpu_options.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-- block:
-  - name: delete a non-existent template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-not-a-real-template"
-      state: absent
-    register: del_fake_lt
-    ignore_errors: true
-  - assert:
-      that:
-        - del_fake_lt is not failed
-  - name: create c4.large instance with cpu_options
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-c4large-1-threads-per-core"
-      image_id: "{{ ec2_ami_id }}"
-      tags:
-        TestId: "{{ resource_prefix }}"
-      instance_type: c4.large
-      cpu_options:
-          core_count: 1
-          threads_per_core: 1
-    register: lt
-
-  - name: instance with cpu_options created with the right options
-    assert:
-      that:
-        - lt is success
-        - lt is changed
-        - "lt.latest_template.launch_template_data.cpu_options.core_count == 1"
-        - "lt.latest_template.launch_template_data.cpu_options.threads_per_core == 1"
-  always:
-  - name: delete the template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-c4large-1-threads-per-core"
-      state: absent
-    register: del_lt
-    retries: 10
-    until: del_lt is not failed
-    ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/deletion.yml b/tests/integration/targets/ec2_launch_template/tasks/deletion.yml
new file mode 100644
index 00000000000..3936888dc53
--- /dev/null
+++ b/tests/integration/targets/ec2_launch_template/tasks/deletion.yml
@@ -0,0 +1,404 @@
+- name: Test deletion of launch template
+  vars:
+    deletion_launch_template_name: "{{ resource_prefix }}-deletion"
+    deletion_launch_template_name_2: "{{ resource_prefix }}-deletion-2"
+    test_ec2_instance_types:
+      - t2.micro
+      - t2.small
+      - t2.medium
+      - t2.large
+      - t2.xlarge
+  block:
+    - name: Create multiple versions of the launch template
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        instance_type: "{{ item }}"
+      with_items: "{{ test_ec2_instance_types }}"
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Validate Launch template details
+      ansible.builtin.assert:
+        that:
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 5
+          - _templates.launch_templates[0].latest_version_number == 5
+          - _templates.launch_templates[0].default_version_number == 5
+  
+    #====================================================================
+    # Validate deletion errors
+    #====================================================================
+    - name: Delete a non-existent template
+      community.aws.ec2_launch_template:
+        name: "{{ resource_prefix }}-not-a-real-template"
+        state: absent
+      register: delete_fake_template
+      ignore_errors: true
+
+    - name: Ensure module did not failed while trying to delete non-existent template
+      assert:
+        that:
+          - delete_fake_template is not failed
+
+    - name: Trying to delete the default version
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: "{{ range(1, 6) }}"
+      ignore_errors: true
+      register: delete_default_v
+
+    - name: Ensure the module failed with proper message
+      ansible.builtin.assert:
+        that:
+          - delete_default_v is failed
+          - 'delete_default_v.msg == "Cannot delete the launch template default version."'
+
+    - name: Trying to delete a version and a non-existing version as default
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: "{{ range(3, 6) }}"
+        default_version: 6
+      ignore_errors: true
+      register: delete_set_non_existing_v
+
+    - name: Ensure the module failed with proper message
+      ansible.builtin.assert:
+        that:
+          - delete_set_non_existing_v is failed
+          - error_m in delete_set_non_existing_v.msg
+      vars:
+        error_m: "Could not set version '6' as default, the launch template version was not found"
+
+    - name: Trying to delete non-existing version (should failed)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: 10
+      ignore_errors: true
+      register: delete_non_existing_v
+
+    - name: Ensure the module failed with proper message
+      ansible.builtin.assert:
+        that:
+          - delete_non_existing_v is failed
+          - error_m in delete_non_existing_v.msg
+      vars:
+        error_m: "The following versions [10] do not exist for launch template id"
+
+    #====================================================================
+    # Delete default version and set latest version as new default
+    #====================================================================
+    - name: Delete default version and set a new default version (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: 5
+      check_mode: true
+      register: delete_default_v_check_mode
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_default_v_check_mode is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 5
+          - _templates.launch_templates[0].latest_version_number == 5
+          - _templates.launch_templates[0].default_version_number == 5
+
+    - name: Delete default version and set a new default version
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: 5
+      register: delete_default_v
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Validate deletion result
+      ansible.builtin.assert:
+        that:
+          - delete_default_v is changed
+          - delete_default_v.deleted_template == {}
+          - delete_default_v.deleted_versions | length == 1
+          - delete_default_v.deleted_versions.0.version_number == 5
+          - delete_default_v.deleted_versions.0.launch_template_name == deletion_launch_template_name
+          - delete_default_v.deleted_versions.0.launch_template_id == _templates.launch_templates[0].launch_template_id
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 4
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 4
+
+    #====================================================================
+    # Delete a single version and set a new default version different from the latest
+    #====================================================================
+    - name: Delete a single version and set a new default version different from the latest (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: 3
+        default_version: 1
+      check_mode: true
+      register: delete_single_v_check_mode
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_default_v_check_mode is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 4
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 4
+
+    - name: Delete a single version and set a new default version different from the latest
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: 3
+        default_version: 1
+      register: delete_single_v
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Validate deletion result
+      ansible.builtin.assert:
+        that:
+          - delete_single_v is changed
+          - delete_single_v.deleted_template == {}
+          - delete_single_v.deleted_versions | length == 1
+          - delete_single_v.deleted_versions.0.version_number == 3
+          - delete_single_v.deleted_versions.0.launch_template_name == deletion_launch_template_name
+          - delete_single_v.deleted_versions.0.launch_template_id == _templates.launch_templates[0].launch_template_id
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 3
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 1
+
+    #====================================================================
+    # Delete multiple versions
+    #====================================================================
+    - name: Delete multiple versions (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: [1, 2]
+      check_mode: true
+      register: delete_multiple_v_check_mode
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_multiple_v_check_mode is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 3
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 1
+
+    - name: Delete multiple versions
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+        versions_to_delete: [1, 2]
+      register: delete_multiple_v
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_multiple_v is changed
+          - delete_multiple_v.deleted_template == {}
+          - delete_multiple_v.deleted_versions | length == 2
+          - delete_multiple_v.deleted_versions | map(attribute='launch_template_name') | unique | list == [deletion_launch_template_name]
+          - delete_multiple_v.deleted_versions | map(attribute='launch_template_id') | unique | list == [_templates.launch_templates[0].launch_template_id]
+          - delete_multiple_v.deleted_versions | map(attribute='version_number') | sort | list == [1, 2]
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 4
+
+    #====================================================================
+    # Delete launch template
+    #====================================================================
+    - name: Delete launch template (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+      check_mode: true
+      register: delete_template_check_mode
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_template_check_mode is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].latest_version_number == 4
+          - _templates.launch_templates[0].default_version_number == 4
+
+    - name: Delete launch template
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+      register: delete_template
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name }}"
+      register: _templates_v
+
+    - name: Ensure the launch template was deleted
+      ansible.builtin.assert:
+        that:
+          - delete_template is changed
+          - delete_template.deleted_template.launch_template_id == _templates.launch_templates[0].launch_template_id
+          - delete_template.deleted_template.latest_version_number == _templates.launch_templates[0].latest_version_number
+          - delete_template.deleted_template.default_version_number == _templates.launch_templates[0].default_version_number
+          - delete_template.deleted_versions | length == 0
+          - _templates_v.launch_templates | length == 0
+
+    - name: Delete launch template once again (idempotency)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name }}"
+        state: absent
+      register: delete_template_idempotency
+
+    - name: Ensure module idempotency
+      ansible.builtin.assert:
+        that:
+          - delete_template_idempotency is not changed
+          - delete_template_idempotency.deleted_template == {}
+          - delete_template_idempotency.deleted_versions == []
+
+    #====================================================================
+    # Delete launch template with multiple versions
+    #====================================================================
+    - name: Create multiple versions of the launch template
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name_2 }}"
+        instance_type: "{{ item }}"
+      with_items: "{{ test_ec2_instance_types }}"
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name_2 }}"
+      register: _templates
+
+    - name: Validate Launch template details
+      ansible.builtin.assert:
+        that:
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 5
+          - _templates.launch_templates[0].latest_version_number == 5
+          - _templates.launch_templates[0].default_version_number == 5
+
+    - name: Delete launch template with multiple versions (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name_2 }}"
+        state: absent
+      check_mode: true
+      register: delete_template_check_mode
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name_2 }}"
+      register: _templates
+
+    - name: Ensure module reported change while no change was made
+      ansible.builtin.assert:
+        that:
+          - delete_template_check_mode is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 5
+
+    - name: Delete launch template with multiple versions
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name_2 }}"
+        state: absent
+      register: delete_template
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ deletion_launch_template_name_2 }}"
+      register: _templates_v
+
+    - name: Ensure the launch template was deleted
+      ansible.builtin.assert:
+        that:
+          - delete_template is changed
+          - delete_template.deleted_template.launch_template_id == _templates.launch_templates[0].launch_template_id
+          - delete_template.deleted_template.latest_version_number == _templates.launch_templates[0].latest_version_number
+          - delete_template.deleted_template.default_version_number == _templates.launch_templates[0].default_version_number
+          - delete_template.deleted_versions | length == 4
+          - delete_template.deleted_versions | map(attribute='launch_template_name') | unique | list == [deletion_launch_template_name_2]
+          - delete_template.deleted_versions | map(attribute='launch_template_id') | unique | list == [_templates.launch_templates[0].launch_template_id]
+          - delete_template.deleted_versions | map(attribute='version_number') | sort | list == [1, 2, 3, 4]
+          - _templates_v.launch_templates | length == 0
+
+    - name: Delete launch template with multiple versions once again (idempotency)
+      community.aws.ec2_launch_template:
+        name: "{{ deletion_launch_template_name_2 }}"
+        state: absent
+      register: delete_template_idempotency
+
+    - name: Ensure module idempotency
+      ansible.builtin.assert:
+        that:
+          - delete_template_idempotency is not changed
+          - delete_template_idempotency.deleted_template == {}
+          - delete_template_idempotency.deleted_versions == []
+
+  always:
+    - name: Delete the launch template
+      community.aws.ec2_launch_template:
+        name: "{{ item }}"
+        state: absent
+      with_items:
+        - "{{ deletion_launch_template_name }}"
+        - "{{ deletion_launch_template_name_2 }}"
+      ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml b/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml
index ad797fabb79..08a39dff855 100644
--- a/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml
+++ b/tests/integration/targets/ec2_launch_template/tasks/iam_instance_role.yml
@@ -1,6 +1,9 @@
-- block:
+- name: Test using IAM instance profile
+  vars:
+    test_launch_template_name: "{{ resource_prefix }}-test-instance-role"
+  block:
     - name: Create IAM role for test
-      iam_role:
+      amazon.aws.iam_role:
         name: "{{ test_role_name }}-1"
         assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
         state: present
@@ -10,7 +13,7 @@
       register: iam_role
 
     - name: Create second IAM role for test
-      iam_role:
+      amazon.aws.iam_role:
         name: "{{ test_role_name }}-2"
         assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
         state: present
@@ -20,115 +23,181 @@
       register: iam_role_2
 
     - name: Make instance with an instance_role
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         iam_instance_profile: "{{ test_role_name }}-1"
       register: template_with_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Ensure the launch template was created with IAM instance profile
+      ansible.builtin.assert:
         that:
           - 'template_with_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 1
+          - _template_info.launch_templates[0].versions[0].launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")
+          - _template_info.launch_templates[0].versions[0].launch_template_data.instance_type == "t2.micro"
+          - _template_info.launch_templates[0].versions[0].launch_template_data.image_id == ec2_ami_id
 
     - name: Create template again, with no change to instance_role
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         iam_instance_profile: "{{ test_role_name }}-1"
       register: template_with_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Validate idempotency
+      ansible.builtin.assert:
         that:
           - 'template_with_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")'
           - 'template_with_role is not changed'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 1
+          - _template_info.launch_templates[0].versions[0].launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")
+          - _template_info.launch_templates[0].versions[0].launch_template_data.instance_type == "t2.micro"
+          - _template_info.launch_templates[0].versions[0].launch_template_data.image_id == ec2_ami_id
 
     - name: Update instance with new instance_role
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         iam_instance_profile: "{{ test_role_name }}-2"
       register: template_with_updated_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Ensure that the launch template was updated with new IAM instance profile
+      ansible.builtin.assert:
         that:
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.iam_role.arn.replace(":role/", ":instance-profile/")'
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.iam_role.arn.replace(":role/", ":instance-profile/")'
           - 'template_with_role.default_template.version_number < template_with_updated_role.default_template.version_number'
           - 'template_with_updated_role is changed'
           - 'template_with_updated_role is not failed'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 2
+          - template_version.launch_template_data.iam_instance_profile.arn == iam_role_2.iam_role.arn.replace(":role/", ":instance-profile/")
+          - template_version.launch_template_data.instance_type == "t2.micro"
+          - template_version.launch_template_data.image_id == ec2_ami_id
+      vars:
+        template_version: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 2) | list | first }}"
 
     - name: Re-set with same new instance_role
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         iam_instance_profile: "{{ test_role_name }}-2"
       register: template_with_updated_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Ensure that module did not reported change
+      ansible.builtin.assert:
         that:
           - 'template_with_updated_role is not changed'
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role_2.iam_role.arn.replace(":role/", ":instance-profile/")'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 2
 
     - name: Update instance with original instance_role (pass profile ARN)
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         # By default an instance profile will be created with the same name as the role
         iam_instance_profile: '{{ iam_role.iam_role.arn.replace(":role/", ":instance-profile/") }}'
       register: template_with_updated_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Validate that the launch template was updated
+      ansible.builtin.assert:
         that:
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")'
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")'
           - 'template_with_role.default_template.version_number < template_with_updated_role.default_template.version_number'
           - 'template_with_updated_role is changed'
           - 'template_with_updated_role is not failed'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 3
+          - template_version.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")
+          - template_version.launch_template_data.instance_type == "t2.micro"
+          - template_version.launch_template_data.image_id == ec2_ami_id
+      vars:
+        template_version: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 3) | list | first }}"
 
     - name: Re-set with same new instance_role (pass profile ARN)
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         image_id: "{{ ec2_ami_id }}"
         instance_type: t2.micro
         iam_instance_profile: '{{ iam_role.iam_role.arn.replace(":role/", ":instance-profile/") }}'
       register: template_with_updated_role
 
-    - assert:
+    - name: Get launch template details
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _template_info
+
+    - name: Assert that the template was not updated
+      ansible.builtin.assert:
         that:
           - 'template_with_updated_role is not changed'
           - 'template_with_updated_role.default_template.launch_template_data.iam_instance_profile.arn == iam_role.iam_role.arn.replace(":role/", ":instance-profile/")'
+          - _template_info.launch_templates | length == 1
+          - _template_info.launch_templates[0].versions | length == 3
 
   always:
-    - name: delete launch template
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-instance-role"
+    - name: Delete launch template
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
         state: absent
       register: lt_removed
-      until: lt_removed is not failed
-      ignore_errors: yes
-      retries: 10
+      ignore_errors: true
+  
     - name: Delete IAM role for test
-      iam_role:
+      amazon.aws.iam_role:
         name: "{{ test_role_name }}-1"
         assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
         state: absent
-        delete_instance_profile: yes
+        delete_instance_profile: true
       register: iam_removed
-      until: iam_removed is not failed
-      ignore_errors: yes
-      retries: 10
+      ignore_errors: true
+
     - name: Delete IAM role for test
       iam_role:
         name: "{{ test_role_name }}-2"
         assume_role_policy_document: "{{ lookup('file','assume-role-policy.json') }}"
         state: absent
-        delete_instance_profile: yes
+        delete_instance_profile: true
       register: iam_2_removed
-      until: iam_2_removed is not failed
-      ignore_errors: yes
-      retries: 10
+      ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml b/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml
deleted file mode 100644
index 7648f00efb8..00000000000
--- a/tests/integration/targets/ec2_launch_template/tasks/instance-metadata.yml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-- name: instance_metadata_tags
-  block:
-    - name: metadata_options
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-metadata"
-        metadata_options:
-          http_put_response_hop_limit: 1
-          http_tokens: required
-          http_protocol_ipv6: enabled
-          instance_metadata_tags: enabled
-        state: present
-      register: metadata_options_launch_template
-    - name: instance with metadata_options created with the right options
-      assert:
-        that:
-          - metadata_options_launch_template is changed
-          - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_put_response_hop_limit == 1"
-          - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_tokens == 'required'"
-          - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.http_protocol_ipv6 == 'enabled'"
-          - "metadata_options_launch_template.latest_template.launch_template_data.metadata_options.instance_metadata_tags == 'enabled'"
-  always:
-    - name: delete the template
-      ec2_launch_template:
-        name: "{{ resource_prefix }}-test-metadata"
-        state: absent
-      register: del_lt
-      retries: 10
-      until: del_lt is not failed
-      ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/main.yml b/tests/integration/targets/ec2_launch_template/tasks/main.yml
index e89dfceb557..c8ea5f055da 100644
--- a/tests/integration/targets/ec2_launch_template/tasks/main.yml
+++ b/tests/integration/targets/ec2_launch_template/tasks/main.yml
@@ -6,8 +6,8 @@
       session_token: "{{ security_token | default(omit) }}"
       region: "{{ aws_region }}"
   block:
-    - include_tasks: cpu_options.yml
+    - include_tasks: template_data.yml
+    - include_tasks: tagging.yml
     - include_tasks: iam_instance_role.yml
     - include_tasks: versions.yml
-    - include_tasks: instance-metadata.yml
-    - include_tasks: network_interfaces.yml
+    - include_tasks: deletion.yml
diff --git a/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml b/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml
deleted file mode 100644
index a2ca0e5f6b9..00000000000
--- a/tests/integration/targets/ec2_launch_template/tasks/network_interfaces.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-- block:
-  - name: network_interfaces
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-test-nic"
-      state: present
-      network_interfaces:
-        - device_index: 0
-          associate_public_ip_address: false
-          delete_on_termination: true
-        - device_index: 1
-          associate_public_ip_address: true
-          delete_on_termination: false
-          ipv6_address_count: 1
-    register: nic_template
-  - name: instance with network_interfaces created with the right settings
-    assert:
-      that:
-        - nic_template is changed
-        - nic_template.default_template.launch_template_data.network_interfaces[0].associate_public_ip_address == False
-        - nic_template.default_template.launch_template_data.network_interfaces[0].delete_on_termination == True
-        - nic_template.default_template.launch_template_data.network_interfaces[0].device_index == 0
-        - nic_template.default_template.launch_template_data.network_interfaces[1].associate_public_ip_address == True
-        - nic_template.default_template.launch_template_data.network_interfaces[1].delete_on_termination == False
-        - nic_template.default_template.launch_template_data.network_interfaces[1].device_index == 1
-        - nic_template.default_template.launch_template_data.network_interfaces[1].ipv6_address_count == 1
-
-  - name: network_interfaces
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-test-nic"
-      state: present
-      network_interfaces:
-        - device_index: 0
-          associate_public_ip_address: false
-          delete_on_termination: true
-        - device_index: 1
-          associate_public_ip_address: true
-          delete_on_termination: false
-          ipv6_address_count: 1
-    register: nic_template
-  - name: instance with network_interfaces created with the right settings
-    assert:
-      that:
-        - nic_template is not changed
-
-  always:
-  - name: delete the template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-test-nic"
-      state: absent
-    register: del_lt
-    retries: 10
-    until: del_lt is not failed
-    ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/tagging.yml b/tests/integration/targets/ec2_launch_template/tasks/tagging.yml
new file mode 100644
index 00000000000..b4d38da70c9
--- /dev/null
+++ b/tests/integration/targets/ec2_launch_template/tasks/tagging.yml
@@ -0,0 +1,210 @@
+---
+- name: Test tagging
+  vars:
+    test_launch_template_name: "{{ resource_prefix }}-tagging"
+    launch_template_instance_tags:
+      - key: foo
+        value: bar
+      - key: environment
+        value: test
+    launch_template_network_tags:
+      - key: owner
+        value: ansible
+    ansible_instance_tags:
+      foo: bar
+      environment: test
+    ansible_network_tags:
+      owner: ansible
+  block:
+    # Create launch template with tags
+    - name: Create Launch template with tags
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        instance_type: t2.micro
+        network_interfaces:
+          - associate_public_ip_address: false
+            delete_on_termination: true
+            device_index: 0
+        tag_specifications:
+          - resource_type: instance
+            tags: "{{ ansible_instance_tags }}"
+          - resource_type: network-interface
+            tags: "{{ ansible_network_tags }}"
+        tags:
+          ResourcePrefix: "{{ resource_prefix }}"
+          InstanceType: "t2.micro"
+      register: _create_with_tags
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+    
+    - name: Ensure the launch template was created with tags
+      ansible.builtin.assert:
+        that:
+          - _create_with_tags is changed
+          - '"tags" in _create_with_tags.template'
+          - _create_with_tags.template.tags.InstanceType == "t2.micro"
+          - _create_with_tags.template.tags.ResourcePrefix == resource_prefix
+          - _templates.launch_templates[0].tags.InstanceType == "t2.micro"
+          - _templates.launch_templates[0].tags.ResourcePrefix == resource_prefix
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | length == 2
+          - instance_tags.tags == launch_template_instance_tags
+          - network_interface_tags.tags == launch_template_network_tags
+      vars:
+        instance_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'instance') | list | first }}"
+        network_interface_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'network-interface') | list | first }}"
+
+    # Create launch template once again with same tags (expected no change)
+    - name: Create launch template once again with same tags (expected no change)
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        instance_type: t2.micro
+        network_interfaces:
+          - associate_public_ip_address: false
+            delete_on_termination: true
+            device_index: 0
+        tag_specifications:
+          - resource_type: instance
+            tags: "{{ ansible_instance_tags }}"
+          - resource_type: network-interface
+            tags: "{{ ansible_network_tags }}"
+        tags:
+          ResourcePrefix: "{{ resource_prefix }}"
+          InstanceType: "t2.micro"
+      register: _create_with_tags_idempotency
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure the launch template was created with tags
+      ansible.builtin.assert:
+        that:
+          - _create_with_tags_idempotency is not changed
+          - '"tags" in _create_with_tags_idempotency.template'
+          - _create_with_tags_idempotency.template.tags.InstanceType == "t2.micro"
+          - _create_with_tags_idempotency.template.tags.ResourcePrefix == resource_prefix
+          - _templates.launch_templates[0].tags.InstanceType == "t2.micro"
+          - _templates.launch_templates[0].tags.ResourcePrefix == resource_prefix
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | length == 2
+          - instance_tags.tags == launch_template_instance_tags
+          - network_interface_tags.tags == launch_template_network_tags
+      vars:
+        instance_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'instance') | list | first }}"
+        network_interface_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'network-interface') | list | first }}"
+
+    # Add new tag
+    - name: Add new tag with purge_tags=false
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        instance_type: t2.micro
+        network_interfaces:
+          - associate_public_ip_address: false
+            delete_on_termination: true
+            device_index: 0
+        tag_specifications:
+          - resource_type: instance
+            tags: "{{ ansible_instance_tags }}"
+          - resource_type: network-interface
+            tags: "{{ ansible_network_tags }}"
+        tags:
+          Phase: integration
+        purge_tags: false
+      register: _add_tag
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure the launch template was created with tags
+      ansible.builtin.assert:
+        that:
+          - _add_tag is changed
+          - '"tags" in _add_tag.template'
+          - _add_tag.template.tags.InstanceType == "t2.micro"
+          - _add_tag.template.tags.ResourcePrefix == resource_prefix
+          - _add_tag.template.tags.Phase == "integration"
+          - _templates.launch_templates[0].tags.InstanceType == "t2.micro"
+          - _templates.launch_templates[0].tags.ResourcePrefix == resource_prefix
+          - _templates.launch_templates[0].tags.Phase == "integration"
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | length == 2
+          - instance_tags.tags == launch_template_instance_tags
+          - network_interface_tags.tags == launch_template_network_tags
+      vars:
+        instance_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'instance') | list | first }}"
+        network_interface_tags: "{{ _templates.launch_templates[0].versions[0].launch_template_data.tag_specifications | selectattr('resource_type', 'equalto', 'network-interface') | list | first }}"
+
+    # Add new launch template version and update tags
+    - name: Add new launch template version and update tags
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        instance_type: t3.micro
+        tags:
+          Team: Ansible
+        purge_tags: true
+        source_version: ""
+      register: _add_tag_and_version
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure the launch template was created with tags
+      ansible.builtin.assert:
+        that:
+          - _add_tag_and_version is changed
+          - '"tags" in _add_tag.template'
+          - '"InstanceType" not in _add_tag_and_version.template.tags'
+          - '"ResourcePrefix" not in _add_tag_and_version.template.tags'
+          - '"Phase" not in _add_tag_and_version.template.tags'
+          - _add_tag_and_version.template.tags.Team == "Ansible"
+          - '"InstanceType" not in _templates.launch_templates[0].tags'
+          - '"ResourcePrefix" not in _templates.launch_templates[0].tags'
+          - '"Phase" not in _templates.launch_templates[0].tags'
+          - _templates.launch_templates[0].tags.Team == "Ansible"
+          - _templates.launch_templates[0].versions | length == 2
+          - '"tag_specifications" not in latest_version_template_data.launch_template_data'
+      vars:
+        latest_version_template_data: '{{ _templates.launch_templates[0].versions | selectattr("version_number", "equalto", 2) | list | first }}'
+
+    # Purge tags
+    - name: Purge all tags from launch template
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        instance_type: t3.micro
+        tags: {}
+        purge_tags: true
+      register: _purge_tags
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure the launch template was created with tags
+      ansible.builtin.assert:
+        that:
+          - _purge_tags is changed
+          - '"tags" not in _purge_tags.template'
+          - _templates.launch_templates[0].tags == {}
+          - _templates.launch_templates[0].versions | length == 2
+
+  always:
+    - name: Delete launch template
+      community.aws.ec2_launch_template:
+        state: absent
+        name: "{{ test_launch_template_name }}"
+      ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml b/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml
deleted file mode 100644
index 41ff9082b76..00000000000
--- a/tests/integration/targets/ec2_launch_template/tasks/tags_and_vpc_settings.yml
+++ /dev/null
@@ -1,208 +0,0 @@
-- block:
-  # ============================================================
-  # set up VPC
-  - name: Create VPC for use in testing
-    ec2_vpc_net:
-      name: "{{ resource_prefix }}-vpc"
-      cidr_block: 10.99.0.0/16
-      tags:
-        Name: Ansible ec2_instance Testing VPC
-      tenancy: default
-    register: testing_vpc
-
-  - name: Create default subnet in zone A
-    ec2_vpc_subnet:
-      state: present
-      vpc_id: "{{ testing_vpc.vpc.id }}"
-      cidr: 10.99.0.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.99.1.0/24
-      az: "{{ aws_region }}b"
-      resource_tags:
-        Name: "{{ resource_prefix }}-subnet-b"
-    register: testing_subnet_b
-
-  - name: create a security group with the vpc
-    ec2_security_group:
-      name: "{{ resource_prefix }}-sg"
-      description: a security group for ansible tests
-      vpc_id: "{{ testing_vpc.vpc.id }}"
-      rules:
-        - proto: tcp
-          ports: [22, 80]
-          cidr_ip: 0.0.0.0/0
-    register: sg
-    # TODO: switch these tests from instances
-  - assert:
-      that:
-      - 1 == 0
-  # ============================================================
-  # start subnet/sg testing
-  - name: Make instance in the testing subnet created in the test VPC
-    ec2_instance:
-      name: "{{ resource_prefix }}-test-basic-vpc-create"
-      image_id: "{{ ec2_ami_id }}"
-      user_data: |
-        #cloud-config
-        package_upgrade: true
-        package_update: true
-      tags:
-        TestId: "{{ resource_prefix }}"
-        Something: else
-      security_groups: "{{ sg.group_id }}"
-      network:
-        source_dest_check: false
-      vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
-      instance_type: t2.micro
-      volumes:
-      - device_name: /dev/sda1
-        ebs:
-          delete_on_termination: true
-    register: in_test_vpc
-
-  - name: Try to re-make the instance, hopefully this shows changed=False
-    ec2_instance:
-      name: "{{ resource_prefix }}-test-basic-vpc-create"
-      image_id: "{{ ec2_ami_id }}"
-      user_data: |
-        #cloud-config
-        package_upgrade: true
-        package_update: true
-      tags:
-        TestId: "{{ resource_prefix }}"
-        Something: else
-      security_groups: "{{ sg.group_id }}"
-      vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
-      instance_type: t2.micro
-    register: remake_in_test_vpc
-  - name: "Remaking the same instance resulted in no changes"
-    assert:
-      that: not remake_in_test_vpc.changed
-  - name: check that instance IDs match anyway
-    assert:
-      that: 'remake_in_test_vpc.instance_ids[0] == in_test_vpc.instance_ids[0]'
-  - name: check that source_dest_check was set to false
-    assert:
-      that: 'not remake_in_test_vpc.instances[0].source_dest_check'
-
-  - name: Alter it by adding tags
-    ec2_instance:
-      name: "{{ resource_prefix }}-test-basic-vpc-create"
-      image_id: "{{ ec2_ami_id }}"
-      tags:
-        TestId: "{{ resource_prefix }}"
-        Another: thing
-      security_groups: "{{ sg.group_id }}"
-      vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
-      instance_type: t2.micro
-    register: add_another_tag
-
-  - ec2_instance_info:
-      instance_ids: "{{ add_another_tag.instance_ids }}"
-    register: check_tags
-  - name: "Remaking the same instance resulted in no changes"
-    assert:
-      that:
-        - check_tags.instances[0].tags.Another == 'thing'
-        - check_tags.instances[0].tags.Something == 'else'
-
-  - name: Purge a tag
-    ec2_instance:
-      name: "{{ resource_prefix }}-test-basic-vpc-create"
-      image_id: "{{ ec2_ami_id }}"
-      purge_tags: true
-      tags:
-        TestId: "{{ resource_prefix }}"
-        Another: thing
-      security_groups: "{{ sg.group_id }}"
-      vpc_subnet_id: "{{ testing_subnet_b.subnet.id }}"
-      instance_type: t2.micro
-  - ec2_instance_info:
-      instance_ids: "{{ add_another_tag.instance_ids }}"
-    register: check_tags
-  - name: "Remaking the same instance resulted in no changes"
-    assert:
-      that:
-        - "'Something' not in check_tags.instances[0].tags"
-
-  - name: Terminate instance
-    ec2_instance:
-      filters:
-        tag:TestId: "{{ resource_prefix }}"
-      state: absent
-    register: result
-  - assert:
-      that: result.changed
-
-  - name: Terminate instance
-    ec2_instance:
-      instance_ids: "{{ in_test_vpc.instance_ids }}"
-      state: absent
-    register: result
-  - assert:
-      that: not result.changed
-
-  - name: check that subnet-default public IP rule was followed
-    assert:
-      that:
-        - in_test_vpc.instances[0].public_dns_name == ""
-        - in_test_vpc.instances[0].private_ip_address.startswith("10.22.33")
-        - in_test_vpc.instances[0].subnet_id == testing_subnet_b.subnet.id
-  - name: check that tags were applied
-    assert:
-      that:
-        - in_test_vpc.instances[0].tags.Name.startswith(resource_prefix)
-        - in_test_vpc.instances[0].state.name == 'running'
-
-  always:
-  - name: remove the security group
-    ec2_security_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 subnet A
-    ec2_vpc_subnet:
-      state: absent
-      vpc_id: "{{ testing_vpc.vpc.id }}"
-      cidr: 10.99.0.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.99.1.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.99.0.0/16
-      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_launch_template/tasks/template_data.yml b/tests/integration/targets/ec2_launch_template/tasks/template_data.yml
new file mode 100644
index 00000000000..cc9a4962f62
--- /dev/null
+++ b/tests/integration/targets/ec2_launch_template/tasks/template_data.yml
@@ -0,0 +1,145 @@
+- name: Test launch template data
+  vars:
+    test_launch_template_name: "{{ resource_prefix }}-template-data"
+  block:
+    # Launch template meta data
+    - name: Create launch template (check mode)
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        image_id: "{{ ec2_ami_id }}"
+        instance_type: c4.large
+        cpu_options:
+            core_count: 1
+            threads_per_core: 1
+        network_interfaces:
+          - device_index: 0
+            associate_public_ip_address: false
+            delete_on_termination: true
+          - device_index: 1
+            associate_public_ip_address: true
+            delete_on_termination: false
+            ipv6_address_count: 1
+        metadata_options:
+          http_put_response_hop_limit: 1
+          http_tokens: required
+          http_protocol_ipv6: enabled
+          instance_metadata_tags: enabled
+      register: _create_check
+      check_mode: true
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while the template was not created
+      ansible.builtin.assert:
+        that:
+          - _create_check is changed
+          - _templates.launch_templates | length == 0
+
+    - name: Create launch template
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        image_id: "{{ ec2_ami_id }}"
+        instance_type: c4.large
+        cpu_options:
+            core_count: 1
+            threads_per_core: 3
+        network_interfaces:
+          - device_index: 0
+            associate_public_ip_address: false
+            delete_on_termination: true
+          - device_index: 1
+            associate_public_ip_address: true
+            delete_on_termination: false
+            ipv6_address_count: 1
+        metadata_options:
+          http_put_response_hop_limit: 1
+          http_tokens: required
+          http_protocol_ipv6: enabled
+          instance_metadata_tags: enabled
+      register: _create
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while the template was not created
+      ansible.builtin.assert:
+        that:
+          - _create is changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.cpu_options.core_count == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.cpu_options.threads_per_core == 3
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_put_response_hop_limit == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_tokens == 'required'
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_protocol_ipv6 == 'enabled'
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.instance_metadata_tags == 'enabled'
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].associate_public_ip_address == False
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].delete_on_termination == True
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].device_index == 0
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].associate_public_ip_address == True
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].delete_on_termination == False
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].device_index == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].ipv6_address_count == 1
+
+    - name: Create launch template once again with same parameters (idempotency)
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        image_id: "{{ ec2_ami_id }}"
+        instance_type: c4.large
+        cpu_options:
+            core_count: 1
+            threads_per_core: 3
+        network_interfaces:
+          - device_index: 0
+            associate_public_ip_address: false
+            delete_on_termination: true
+          - device_index: 1
+            associate_public_ip_address: true
+            delete_on_termination: false
+            ipv6_address_count: 1
+        metadata_options:
+          http_put_response_hop_limit: 1
+          http_tokens: required
+          http_protocol_ipv6: enabled
+          instance_metadata_tags: enabled
+      register: _create_idempotency
+
+    - name: Retrieve Launch template information
+      amazon.aws.ec2_launch_template_info:
+        filters:
+          launch-template-name: "{{ test_launch_template_name }}"
+      register: _templates
+
+    - name: Ensure module reported change while the template was not created
+      ansible.builtin.assert:
+        that:
+          - _create_idempotency is not changed
+          - _templates.launch_templates | length == 1
+          - _templates.launch_templates[0].versions | length == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.cpu_options.core_count == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.cpu_options.threads_per_core == 3
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_put_response_hop_limit == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_tokens == 'required'
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.http_protocol_ipv6 == 'enabled'
+          - _templates.launch_templates[0].versions[0].launch_template_data.metadata_options.instance_metadata_tags == 'enabled'
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].associate_public_ip_address == False
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].delete_on_termination == True
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[0].device_index == 0
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].associate_public_ip_address == True
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].delete_on_termination == False
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].device_index == 1
+          - _templates.launch_templates[0].versions[0].launch_template_data.network_interfaces[1].ipv6_address_count == 1
+
+  always:
+    - name: delete the template
+      community.aws.ec2_launch_template:
+        name: "{{ test_launch_template_name }}"
+        state: absent
+      ignore_errors: true
diff --git a/tests/integration/targets/ec2_launch_template/tasks/versions.yml b/tests/integration/targets/ec2_launch_template/tasks/versions.yml
index a9e40cd0843..666c5653836 100644
--- a/tests/integration/targets/ec2_launch_template/tasks/versions.yml
+++ b/tests/integration/targets/ec2_launch_template/tasks/versions.yml
@@ -1,95 +1,462 @@
-- block:
-  - name: create simple instance template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+- name: Test launch template versioning
+  vars:
+    test_launch_template_name: "{{ resource_prefix }}-versioning"
+  block:
+  #=====================================================================
+  # Create the launch template
+  #=====================================================================
+  - name: Create a launch template (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       image_id: "{{ ec2_ami_id }}"
       tags:
         TestId: "{{ resource_prefix }}"
       instance_type: c4.large
-    register: lt
+    register: _create_check
+    check_mode: true
 
-  - name: instance with cpu_options created with the right options
-    assert:
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure module reported changed while the template was not created
+    ansible.builtin.assert:
+      that:
+        - _create_check is changed
+        - '"default_version" not in _create_check'
+        - '"latest_version" not in _create_check'
+        - _template_info.launch_templates | length == 0
+
+  - name: Create a launch template
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: c4.large
+    register: _create
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
+
+  - name: Ensure the launch template was created with the right version
+    ansible.builtin.assert:
       that:
-        - lt is success
-        - lt is changed
-        - lt.default_version == 1
-        - lt.latest_version == 1
+        - _create is changed
+        - _create.default_version == 1
+        - _create.latest_version == 1
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 1
+        - _template_info.launch_templates[0].versions | length == 1
+        - _template_info.launch_templates[0].versions.0.launch_template_data.image_id == ec2_ami_id
+        - _template_info.launch_templates[0].versions.0.launch_template_data.instance_type == "c4.large"
 
-  - name: update simple instance template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+  - name: Create the same launch template once again
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: c4.large
+    register: _create_idempotency
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the module did not reported change (idempotency)
+    ansible.builtin.assert:
+      that:
+        - _create_idempotency is not changed
+        - _create.default_version == 1
+        - _create.latest_version == 1
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 1
+        - _template_info.launch_templates[0].versions | length == 1
+        - _template_info.launch_templates[0].versions.0.launch_template_data.image_id == ec2_ami_id
+        - _template_info.launch_templates[0].versions.0.launch_template_data.instance_type == "c4.large"
+
+  #=====================================================================
+  # Create a new version of the launch template (set first version as default)
+  #=====================================================================
+  - name: Create a new version of the launch template (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       default_version: 1
       image_id: "{{ ec2_ami_id }}"
       tags:
         TestId: "{{ resource_prefix }}"
       instance_type: m5.large
-    register: lt
+    register: _update_check
+    check_mode: true
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
 
-  - name: instance with cpu_options created with the right options
-    assert:
+  - name: Ensure the module reported change in check mode
+    ansible.builtin.assert:
       that:
-        - lt is success
-        - lt is changed
-        - lt.default_version == 1
-        - lt.latest_version == 2
+        - _update_check is changed
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 1
+        - _template_info.launch_templates[0].versions | length == 1
 
-  - name: update simple instance template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+  - name: Create a new version of the launch template
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      default_version: 1
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: m5.large
+    register: _update
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the launch template latest version has changed
+    ansible.builtin.assert:
+      that:
+        - _update is changed
+        - _update.default_version == 1
+        - _update.latest_version == 2
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+        - created_template.launch_template_data.image_id == ec2_ami_id
+        - created_template.launch_template_data.instance_type == "m5.large"
+    vars:
+      created_template: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 2) | first }}"
+
+  - name: Create a new version of the launch template (idempotency)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      default_version: 1
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: m5.large
+    register: _update_idempotency
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the module did not reported change (idempotency)
+    ansible.builtin.assert:
+      that:
+        - _update_idempotency is not changed
+        - _update_idempotency.default_version == 1
+        - _update_idempotency.latest_version == 2
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+
+  #=====================================================================
+  # Set the latest version of the launch template as default
+  #=====================================================================
+  - name: Set the latest version of the launch template as default (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      default_version: latest
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: m5.large
+    register: _set_version_check
+    check_mode: true
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the module reported change in check mode
+    ansible.builtin.assert:
+      that:
+        - _set_version_check is changed
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 1
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+
+  - name: Set the latest version of the launch template as default
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      default_version: latest
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: m5.large
+    register: _set_version
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the launch template latest version has changed
+    ansible.builtin.assert:
+      that:
+        - _set_version is changed
+        - _set_version.default_version == 2
+        - _set_version.latest_version == 2
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 2
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+        - created_template.launch_template_data.image_id == ec2_ami_id
+        - created_template.launch_template_data.instance_type == "m5.large"
+    vars:
+      created_template: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 2) | first }}"
+
+  - name: Set the latest version of the launch template as default (idempotency)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      default_version: latest
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: m5.large
+    register: _set_version_idempotency
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      filters:
+        launch-template-name: "{{ test_launch_template_name }}"
+    register: _template_info
+
+  - name: Ensure the module did not reported change (idempotency)
+    ansible.builtin.assert:
+      that:
+        - _set_version_idempotency is not changed
+        - _set_version_idempotency.default_version == 2
+        - _set_version_idempotency.latest_version == 2
+        - _template_info.launch_templates[0].default_version_number == 2
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+
+  #=====================================================================
+  # Create another version 
+  #=====================================================================
+  - name: Create a new launch template version (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: t3.medium
+    register: _another_version_check
+    check_mode: true
+  
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
+
+  - name: Ensure the module reported change in check_mode
+    ansible.builtin.assert:
+      that:
+        - _another_version_check is changed
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 2
+        - _template_info.launch_templates[0].latest_version_number == 2
+        - _template_info.launch_templates[0].versions | length == 2
+
+  - name: Create a new launch template version
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       image_id: "{{ ec2_ami_id }}"
       tags:
         TestId: "{{ resource_prefix }}"
       instance_type: t3.medium
-    register: lt
+    register: _another_version
+  
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
 
-  - name: instance with cpu_options created with the right options
-    assert:
+  - name: Ensure the launch template latest version has changed
+    ansible.builtin.assert:
       that:
-        - lt is success
-        - lt is changed
-        - lt.default_version == 3
-        - lt.latest_version == 3
+        - _another_version is changed
+        - _another_version.default_version == 3
+        - _another_version.latest_version == 3
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 3
+        - _template_info.launch_templates[0].latest_version_number == 3
+        - _template_info.launch_templates[0].versions | length == 3
+        - created_template.launch_template_data.image_id == ec2_ami_id
+        - created_template.launch_template_data.instance_type == "t3.medium"
+    vars:
+      created_template: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 3) | first }}"
 
-  - name: create new template version based on an old version
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+  - name: Create a new launch template version (idempotency)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      image_id: "{{ ec2_ami_id }}"
+      tags:
+        TestId: "{{ resource_prefix }}"
+      instance_type: t3.medium
+    register: _another_version_idempotency
+  
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
+
+  - name: Ensure the module did not reported change (idempotency)
+    ansible.builtin.assert:
+      that:
+        - _another_version_idempotency is not changed
+        - _another_version_idempotency.default_version == 3
+        - _another_version_idempotency.latest_version == 3
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 3
+        - _template_info.launch_templates[0].latest_version_number == 3
+        - _template_info.launch_templates[0].versions | length == 3
+
+  #=====================================================================
+  # Create another version based on an old version
+  #=====================================================================
+  - name: Create new template version based on an old version (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      cpu_options:
+          core_count: 1
+          threads_per_core: 1
+      source_version: 1
+    register: _version_based_on_old_version_check
+    check_mode: true
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
+
+  - name: Ensure module reported change in check mode
+    ansible.builtin.assert:
+      that:
+        - _version_based_on_old_version_check is changed
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 3
+        - _template_info.launch_templates[0].latest_version_number == 3
+        - _template_info.launch_templates[0].versions | length == 3
+
+  - name: Create new template version based on an old version
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       cpu_options:
           core_count: 1
           threads_per_core: 1
       source_version: 1
-    register: lt
+    register: _version_based_on_old_version
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
 
-  - name: instance with cpu_options created with the right options
-    assert:
+  - name: Ensure the new launch template has been created with the right options
+    ansible.builtin.assert:
       that:
-        - lt is success
-        - lt is changed
-        - lt.default_version == 4
-        - lt.latest_version == 4
-        - lt.latest_template.launch_template_data.instance_type == "c4.large"
+        - _version_based_on_old_version is changed
+        - _version_based_on_old_version.default_version == 4
+        - _version_based_on_old_version.latest_version == 4
+        - _version_based_on_old_version.latest_template.launch_template_data.instance_type == "c4.large"
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 4
+        - _template_info.launch_templates[0].latest_version_number == 4
+        - _template_info.launch_templates[0].versions | length == 4
+        - created_template.launch_template_data.image_id == ec2_ami_id
+        - created_template.launch_template_data.cpu_options.core_count == 1
+        - created_template.launch_template_data.cpu_options.threads_per_core == 1
+    vars:
+      created_template: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 4) | first }}"
 
-  - name: update simple instance template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+  #=====================================================================
+  # Create another version with updated description
+  #=====================================================================
+  - name: Create a launch template version with another description (check mode)
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       version_description: "Fix something."
-    register: lt
+    register: _version_description_check
+    check_mode: true
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
+
+  - name: Ensure module reported change in check mode
+    ansible.builtin.assert:
+      that:
+        - _version_description_check is changed
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 4
+        - _template_info.launch_templates[0].latest_version_number == 4
+        - _template_info.launch_templates[0].versions | length == 4
+
+  - name: Create a launch template version with another description
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
+      version_description: "Fix something."
+    register: _version_description
+
+  - name: Read launch template information
+    amazon.aws.ec2_launch_template_info:
+      launch_template_ids:
+        - "{{ _create.template.launch_template_id }}"
+    register: _template_info
 
-  - name: instance with cpu_options created with the right options
-    assert:
+  - name: Ensure module reported change
+    ansible.builtin.assert:
       that:
-        - lt is success
-        - lt is changed
-        - lt.default_version == 5
-        - lt.latest_version == 5
-        - lt.latest_template.version_description == "Fix something."
+        - _version_description is changed
+        - _version_description.default_version == 5
+        - _version_description.latest_version == 5
+        - _version_description.latest_template.version_description == "Fix something."
+        - _template_info.launch_templates | length == 1
+        - _template_info.launch_templates[0].default_version_number == 5
+        - _template_info.launch_templates[0].latest_version_number == 5
+        - _template_info.launch_templates[0].versions | length == 5
+        - created_template.version_description == "Fix something."
+    vars:
+      created_template: "{{ _template_info.launch_templates[0].versions | selectattr('version_number', 'equalto', 5) | first }}"
 
   always:
-  - name: delete the template
-    ec2_launch_template:
-      name: "{{ resource_prefix }}-simple"
+  - name: Delete the template
+    community.aws.ec2_launch_template:
+      name: "{{ test_launch_template_name }}"
       state: absent
-    register: del_lt
-    retries: 10
-    until: del_lt is not failed
     ignore_errors: true