summaryrefslogtreecommitdiffstats
path: root/ansible_collections/amazon/aws/tests/unit/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/amazon/aws/tests/unit/plugins')
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py514
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py31
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py126
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_determine_iam_role.py102
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem31
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem47
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert121
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert69
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert113
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert124
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert86
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert121
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert18
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert18
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zipbin0 -> 162 bytes
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep0
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json17
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json16
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json38
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json80
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json80
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json80
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json80
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json100
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json119
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json40
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json45
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json17
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json16
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json83
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json83
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json83
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json83
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json104
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json124
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json40
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json39
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json45
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json22
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json22
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json22
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json22
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json22
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json17
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json38
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json101
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json121
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json180
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json180
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json42
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json41
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json52
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json51
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json50
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json17
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json16
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json38
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json121
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json42
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json42
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json17
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json16
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json38
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json121
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json180
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json42
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json52
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json51
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py227
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_ami.py44
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_key.py654
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_security_group.py83
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_vpc_dhcp_option.py71
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_kms_key.py82
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer.py493
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer_info.py358
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/test_s3_object.py29
-rw-r--r--ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py50
88 files changed, 7190 insertions, 0 deletions
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py b/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py
new file mode 100644
index 000000000..5386fe6c7
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py
@@ -0,0 +1,514 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2017 Sloane Hertel <shertel@redhat.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+import datetime
+from unittest.mock import Mock, MagicMock
+
+from ansible.errors import AnsibleError
+from ansible.parsing.dataloader import DataLoader
+from ansible_collections.amazon.aws.plugins.inventory.aws_ec2 import InventoryModule, instance_data_filter_to_boto_attr
+
+
+instances = {
+ 'Instances': [
+ {'Monitoring': {'State': 'disabled'},
+ 'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com',
+ 'State': {'Code': 16, 'Name': 'running'},
+ 'EbsOptimized': False,
+ 'LaunchTime': datetime.datetime(2017, 10, 31, 12, 59, 25),
+ 'PublicIpAddress': '12.345.67.890',
+ 'PrivateIpAddress': '098.76.54.321',
+ 'ProductCodes': [],
+ 'VpcId': 'vpc-12345678',
+ 'StateTransitionReason': '',
+ 'InstanceId': 'i-00000000000000000',
+ 'EnaSupport': True,
+ 'ImageId': 'ami-12345678',
+ 'PrivateDnsName': 'ip-098-76-54-321.ec2.internal',
+ 'KeyName': 'testkey',
+ 'SecurityGroups': [{'GroupName': 'default', 'GroupId': 'sg-12345678'}],
+ 'ClientToken': '',
+ 'SubnetId': 'subnet-12345678',
+ 'InstanceType': 't2.micro',
+ 'NetworkInterfaces': [
+ {'Status': 'in-use',
+ 'MacAddress': '12:a0:50:42:3d:a4',
+ 'SourceDestCheck': True,
+ 'VpcId': 'vpc-12345678',
+ 'Description': '',
+ 'NetworkInterfaceId': 'eni-12345678',
+ 'PrivateIpAddresses': [
+ {'PrivateDnsName': 'ip-098-76-54-321.ec2.internal',
+ 'PrivateIpAddress': '098.76.54.321',
+ 'Primary': True,
+ 'Association':
+ {'PublicIp': '12.345.67.890',
+ 'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com',
+ 'IpOwnerId': 'amazon'}}],
+ 'PrivateDnsName': 'ip-098-76-54-321.ec2.internal',
+ 'Attachment':
+ {'Status': 'attached',
+ 'DeviceIndex': 0,
+ 'DeleteOnTermination': True,
+ 'AttachmentId': 'eni-attach-12345678',
+ 'AttachTime': datetime.datetime(2017, 10, 31, 12, 59, 25)},
+ 'Groups': [
+ {'GroupName': 'default',
+ 'GroupId': 'sg-12345678'}],
+ 'Ipv6Addresses': [],
+ 'OwnerId': '123456789012',
+ 'PrivateIpAddress': '098.76.54.321',
+ 'SubnetId': 'subnet-12345678',
+ 'Association':
+ {'PublicIp': '12.345.67.890',
+ 'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com',
+ 'IpOwnerId': 'amazon'}}],
+ 'SourceDestCheck': True,
+ 'Placement':
+ {'Tenancy': 'default',
+ 'GroupName': '',
+ 'AvailabilityZone': 'us-east-1c'},
+ 'Hypervisor': 'xen',
+ 'BlockDeviceMappings': [
+ {'DeviceName': '/dev/xvda',
+ 'Ebs':
+ {'Status': 'attached',
+ 'DeleteOnTermination': True,
+ 'VolumeId': 'vol-01234567890000000',
+ 'AttachTime': datetime.datetime(2017, 10, 31, 12, 59, 26)}}],
+ 'Architecture': 'x86_64',
+ 'RootDeviceType': 'ebs',
+ 'RootDeviceName': '/dev/xvda',
+ 'VirtualizationType': 'hvm',
+ 'Tags': [{'Value': 'test', 'Key': 'ansible'}, {'Value': 'aws_ec2', 'Key': 'Name'}],
+ 'AmiLaunchIndex': 0}],
+ 'ReservationId': 'r-01234567890000000',
+ 'Groups': [],
+ 'OwnerId': '123456789012'
+}
+
+
+@pytest.fixture()
+def inventory():
+ inventory = InventoryModule()
+ inventory._options = {
+ "aws_profile": "first_precedence",
+ "aws_access_key": "test_access_key",
+ "aws_secret_key": "test_secret_key",
+ "aws_security_token": "test_security_token",
+ "iam_role_arn": None,
+ "use_contrib_script_compatible_ec2_tag_keys": False,
+ "hostvars_prefix": "",
+ "hostvars_suffix": "",
+ "strict": True,
+ "compose": {},
+ "groups": {},
+ "keyed_groups": [],
+ "regions": ["us-east-1"],
+ "filters": [],
+ "include_filters": [],
+ "exclude_filters": [],
+ "hostnames": [],
+ "strict_permissions": False,
+ "allow_duplicated_hosts": False,
+ "cache": False,
+ "include_extra_api_calls": False,
+ "use_contrib_script_compatible_sanitization": False,
+ }
+ inventory.inventory = MagicMock()
+ return inventory
+
+
+def test_compile_values(inventory):
+ found_value = instances['Instances'][0]
+ chain_of_keys = instance_data_filter_to_boto_attr['instance.group-id']
+ for attr in chain_of_keys:
+ found_value = inventory._compile_values(found_value, attr)
+ assert found_value == "sg-12345678"
+
+
+def test_get_boto_attr_chain(inventory):
+ instance = instances['Instances'][0]
+ assert inventory._get_boto_attr_chain('network-interface.addresses.private-ip-address', instance) == "098.76.54.321"
+
+
+def test_boto3_conn(inventory):
+ inventory._options = {"aws_profile": "first_precedence",
+ "aws_access_key": "test_access_key",
+ "aws_secret_key": "test_secret_key",
+ "aws_security_token": "test_security_token",
+ "iam_role_arn": None}
+ loader = DataLoader()
+ inventory._set_credentials(loader)
+ with pytest.raises(AnsibleError) as error_message:
+ for _connection, _region in inventory._boto3_conn(regions=['us-east-1']):
+ assert "Insufficient credentials found" in error_message
+
+
+def testget_all_hostnames_default(inventory):
+ instance = instances['Instances'][0]
+ assert inventory.get_all_hostnames(instance, hostnames=None) == ["ec2-12-345-67-890.compute-1.amazonaws.com", "ip-098-76-54-321.ec2.internal"]
+
+
+def testget_all_hostnames(inventory):
+ hostnames = ['ip-address', 'dns-name']
+ instance = instances['Instances'][0]
+ assert inventory.get_all_hostnames(instance, hostnames) == ["12.345.67.890", "ec2-12-345-67-890.compute-1.amazonaws.com"]
+
+
+def testget_all_hostnames_dict(inventory):
+ hostnames = [{'name': 'private-ip-address', 'separator': '_', 'prefix': 'tag:Name'}]
+ instance = instances['Instances'][0]
+ assert inventory.get_all_hostnames(instance, hostnames) == ["aws_ec2_098.76.54.321"]
+
+
+def testget_all_hostnames_with_2_tags(inventory):
+ hostnames = ['tag:ansible', 'tag:Name']
+ instance = instances['Instances'][0]
+ assert inventory.get_all_hostnames(instance, hostnames) == ["test", "aws_ec2"]
+
+
+def test_get_preferred_hostname_default(inventory):
+ instance = instances['Instances'][0]
+ assert inventory._get_preferred_hostname(instance, hostnames=None) == "ec2-12-345-67-890.compute-1.amazonaws.com"
+
+
+def test_get_preferred_hostname(inventory):
+ hostnames = ['ip-address', 'dns-name']
+ instance = instances['Instances'][0]
+ assert inventory._get_preferred_hostname(instance, hostnames) == "12.345.67.890"
+
+
+def test_get_preferred_hostname_dict(inventory):
+ hostnames = [{'name': 'private-ip-address', 'separator': '_', 'prefix': 'tag:Name'}]
+ instance = instances['Instances'][0]
+ assert inventory._get_preferred_hostname(instance, hostnames) == "aws_ec2_098.76.54.321"
+
+
+def test_get_preferred_hostname_with_2_tags(inventory):
+ hostnames = ['tag:ansible', 'tag:Name']
+ instance = instances['Instances'][0]
+ assert inventory._get_preferred_hostname(instance, hostnames) == "test"
+
+
+def test_set_credentials(inventory):
+ inventory._options = {'aws_access_key': 'test_access_key',
+ 'aws_secret_key': 'test_secret_key',
+ 'aws_security_token': 'test_security_token',
+ 'aws_profile': 'test_profile',
+ 'iam_role_arn': 'arn:aws:iam::123456789012:role/test-role'}
+ loader = DataLoader()
+ inventory._set_credentials(loader)
+
+ assert inventory.boto_profile == "test_profile"
+ assert inventory.aws_access_key_id == "test_access_key"
+ assert inventory.aws_secret_access_key == "test_secret_key"
+ assert inventory.aws_security_token == "test_security_token"
+ assert inventory.iam_role_arn == "arn:aws:iam::123456789012:role/test-role"
+
+
+def test_insufficient_credentials(inventory):
+ inventory._options = {
+ 'aws_access_key': None,
+ 'aws_secret_key': None,
+ 'aws_security_token': None,
+ 'aws_profile': None,
+ 'iam_role_arn': None
+ }
+ with pytest.raises(AnsibleError) as error_message:
+ loader = DataLoader()
+ inventory._set_credentials(loader)
+ assert "Insufficient credentials found" in error_message
+
+
+def test_verify_file_bad_config(inventory):
+ assert inventory.verify_file('not_aws_config.yml') is False
+
+
+def test_include_filters_with_no_filter(inventory):
+ inventory._options = {
+ 'filters': {},
+ 'include_filters': [],
+ }
+ print(inventory.build_include_filters())
+ assert inventory.build_include_filters() == [{}]
+
+
+def test_include_filters_with_include_filters_only(inventory):
+ inventory._options = {
+ 'filters': {},
+ 'include_filters': [{"foo": "bar"}],
+ }
+ assert inventory.build_include_filters() == [{"foo": "bar"}]
+
+
+def test_include_filters_with_filter_and_include_filters(inventory):
+ inventory._options = {
+ 'filters': {"from_filter": 1},
+ 'include_filters': [{"from_include_filter": "bar"}],
+ }
+ print(inventory.build_include_filters())
+ assert inventory.build_include_filters() == [
+ {"from_filter": 1},
+ {"from_include_filter": "bar"}]
+
+
+def test_add_host_empty_hostnames(inventory):
+ hosts = [
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "ip-10-85-0-4.ec2.internal"
+ },
+ ]
+ inventory._add_hosts(hosts, "aws_ec2", [])
+ inventory.inventory.add_host.assert_called_with("ip-10-85-0-4.ec2.internal", group="aws_ec2")
+
+
+def test_add_host_with_hostnames_no_criteria(inventory):
+ hosts = [{}]
+
+ inventory._add_hosts(
+ hosts, "aws_ec2", hostnames=["tag:Name", "private-dns-name", "dns-name"]
+ )
+ assert inventory.inventory.add_host.call_count == 0
+
+
+def test_add_host_with_hostnames_and_one_criteria(inventory):
+ hosts = [
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "sample-host",
+ }
+ ]
+
+ inventory._add_hosts(
+ hosts, "aws_ec2", hostnames=["tag:Name", "private-dns-name", "dns-name"]
+ )
+ assert inventory.inventory.add_host.call_count == 1
+ inventory.inventory.add_host.assert_called_with("sample-host", group="aws_ec2")
+
+
+def test_add_host_with_hostnames_and_two_matching_criteria(inventory):
+ hosts = [
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "name-from-PublicDnsName",
+ "Tags": [{"Value": "name-from-tag-Name", "Key": "Name"}],
+ }
+ ]
+
+ inventory._add_hosts(
+ hosts, "aws_ec2", hostnames=["tag:Name", "private-dns-name", "dns-name"]
+ )
+ assert inventory.inventory.add_host.call_count == 1
+ inventory.inventory.add_host.assert_called_with(
+ "name-from-tag-Name", group="aws_ec2"
+ )
+
+
+def test_add_host_with_hostnames_and_two_matching_criteria_and_allow_duplicated_hosts(
+ inventory,
+):
+ hosts = [
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "name-from-PublicDnsName",
+ "Tags": [{"Value": "name-from-tag-Name", "Key": "Name"}],
+ }
+ ]
+
+ inventory._add_hosts(
+ hosts,
+ "aws_ec2",
+ hostnames=["tag:Name", "private-dns-name", "dns-name"],
+ allow_duplicated_hosts=True,
+ )
+ assert inventory.inventory.add_host.call_count == 2
+ inventory.inventory.add_host.assert_any_call(
+ "name-from-PublicDnsName", group="aws_ec2"
+ )
+ inventory.inventory.add_host.assert_any_call("name-from-tag-Name", group="aws_ec2")
+
+
+def test_sanitize_hostname(inventory):
+ assert inventory._sanitize_hostname(1) == "1"
+ assert inventory._sanitize_hostname("a:b") == "a_b"
+ assert inventory._sanitize_hostname("a:/b") == "a__b"
+ assert inventory._sanitize_hostname("example") == "example"
+
+
+def test_sanitize_hostname_legacy(inventory):
+ inventory._sanitize_group_name = (
+ inventory._legacy_script_compatible_group_sanitization
+ )
+ assert inventory._sanitize_hostname("a:/b") == "a__b"
+
+
+@pytest.mark.parametrize(
+ "hostvars_prefix,hostvars_suffix,use_contrib_script_compatible_ec2_tag_keys,expectation",
+ [
+ (
+ None,
+ None,
+ False,
+ {
+ "my_var": 1,
+ "placement": {"availability_zone": "us-east-1a", "region": "us-east-1"},
+ "tags": {"Name": "my-name"},
+ },
+ ),
+ (
+ "pre",
+ "post",
+ False,
+ {
+ "premy_varpost": 1,
+ "preplacementpost": {
+ "availability_zone": "us-east-1a",
+ "region": "us-east-1",
+ },
+ "pretagspost": {"Name": "my-name"},
+ },
+ ),
+ (
+ None,
+ None,
+ True,
+ {
+ "my_var": 1,
+ "ec2_tag_Name": "my-name",
+ "placement": {"availability_zone": "us-east-1a", "region": "us-east-1"},
+ "tags": {"Name": "my-name"},
+ },
+ ),
+ ],
+)
+def test_prepare_host_vars(
+ inventory,
+ hostvars_prefix,
+ hostvars_suffix,
+ use_contrib_script_compatible_ec2_tag_keys,
+ expectation,
+):
+ original_host_vars = {
+ "my_var": 1,
+ "placement": {"availability_zone": "us-east-1a"},
+ "Tags": [{"Key": "Name", "Value": "my-name"}],
+ }
+ assert (
+ inventory.prepare_host_vars(
+ original_host_vars,
+ hostvars_prefix,
+ hostvars_suffix,
+ use_contrib_script_compatible_ec2_tag_keys,
+ )
+ == expectation
+ )
+
+
+def test_iter_entry(inventory):
+ hosts = [
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "first-host://",
+ },
+ {
+ "Placement": {
+ "AvailabilityZone": "us-east-1a",
+ },
+ "PublicDnsName": "second-host",
+ "Tags": [{"Key": "Name", "Value": "my-name"}],
+ },
+ ]
+
+ entries = list(inventory.iter_entry(hosts, hostnames=[]))
+ assert len(entries) == 2
+ assert entries[0][0] == "first_host___"
+ assert entries[1][0] == "second-host"
+ assert entries[1][1]["tags"]["Name"] == "my-name"
+
+ entries = list(
+ inventory.iter_entry(
+ hosts,
+ hostnames=[],
+ hostvars_prefix="a_",
+ hostvars_suffix="_b",
+ use_contrib_script_compatible_ec2_tag_keys=True,
+ )
+ )
+ assert len(entries) == 2
+ assert entries[0][0] == "first_host___"
+ assert entries[1][1]["a_tags_b"]["Name"] == "my-name"
+
+
+def test_query_empty(inventory):
+ result = inventory._query("us-east-1", [], [], strict_permissions=True)
+ assert result == {"aws_ec2": []}
+
+
+instance_foobar = {"InstanceId": "foobar"}
+instance_barfoo = {"InstanceId": "barfoo"}
+
+
+def test_query_empty_include_only(inventory):
+ inventory._get_instances_by_region = Mock(side_effect=[[instance_foobar]])
+ result = inventory._query("us-east-1", [{"tag:Name": ["foobar"]}], [], strict_permissions=True)
+ assert result == {"aws_ec2": [instance_foobar]}
+
+
+def test_query_empty_include_ordered(inventory):
+ inventory._get_instances_by_region = Mock(side_effect=[[instance_foobar], [instance_barfoo]])
+ result = inventory._query("us-east-1", [{"tag:Name": ["foobar"]}, {"tag:Name": ["barfoo"]}], [], strict_permissions=True)
+ assert result == {"aws_ec2": [instance_barfoo, instance_foobar]}
+ inventory._get_instances_by_region.assert_called_with('us-east-1', [{'Name': 'tag:Name', 'Values': ['barfoo']}], True)
+
+
+def test_query_empty_include_exclude(inventory):
+ inventory._get_instances_by_region = Mock(side_effect=[[instance_foobar], [instance_foobar]])
+ result = inventory._query("us-east-1", [{"tag:Name": ["foobar"]}], [{"tag:Name": ["foobar"]}], strict_permissions=True)
+ assert result == {"aws_ec2": []}
+
+
+def test_include_extra_api_calls_deprecated(inventory):
+ inventory.display.deprecate = Mock()
+ inventory._read_config_data = Mock()
+ inventory._set_credentials = Mock()
+ inventory._query = Mock(return_value=[])
+
+ inventory.parse(inventory=[], loader=None, path=None)
+ assert inventory.display.deprecate.call_count == 0
+
+ inventory._options["include_extra_api_calls"] = True
+ inventory.parse(inventory=[], loader=None, path=None)
+ assert inventory.display.deprecate.call_count == 1
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py
new file mode 100644
index 000000000..a7d1e0475
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2017 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+import pytest
+
+from ansible.module_utils.six import string_types
+from ansible.module_utils._text import to_bytes
+from ansible.module_utils.common._collections_compat import MutableMapping
+
+
+@pytest.fixture
+def patch_ansible_module(request, mocker):
+ if isinstance(request.param, string_types):
+ args = request.param
+ elif isinstance(request.param, MutableMapping):
+ if 'ANSIBLE_MODULE_ARGS' not in request.param:
+ request.param = {'ANSIBLE_MODULE_ARGS': request.param}
+ if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False
+ args = json.dumps(request.param)
+ else:
+ raise Exception('Malformed data to the patch_ansible_module pytest fixture')
+
+ mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args))
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py
new file mode 100644
index 000000000..e889b676a
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py
@@ -0,0 +1,126 @@
+# (c) 2022 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.amazon.aws.tests.unit.compat.mock import sentinel
+import ansible_collections.amazon.aws.plugins.modules.ec2_instance as ec2_instance_module
+
+
+@pytest.fixture
+def params_object():
+ params = {
+ 'iam_instance_profile': None,
+ 'exact_count': None,
+ 'count': None,
+ 'launch_template': None,
+ 'instance_type': None,
+ }
+ return params
+
+
+@pytest.fixture
+def ec2_instance(monkeypatch):
+ # monkey patches various ec2_instance module functions, we'll separately test the operation of
+ # these functions, we just care that it's passing the results into the right place in the
+ # instance spec.
+ monkeypatch.setattr(ec2_instance_module, 'build_top_level_options', lambda params: {'TOP_LEVEL_OPTIONS': sentinel.TOP_LEVEL})
+ monkeypatch.setattr(ec2_instance_module, 'build_network_spec', lambda params: sentinel.NETWORK_SPEC)
+ monkeypatch.setattr(ec2_instance_module, 'build_volume_spec', lambda params: sentinel.VOlUME_SPEC)
+ monkeypatch.setattr(ec2_instance_module, 'build_instance_tags', lambda params: sentinel.TAG_SPEC)
+ monkeypatch.setattr(ec2_instance_module, 'determine_iam_role', lambda params: sentinel.IAM_PROFILE_ARN)
+ return ec2_instance_module
+
+
+def _assert_defaults(instance_spec, to_skip=None):
+ if not to_skip:
+ to_skip = []
+
+ assert isinstance(instance_spec, dict)
+
+ if 'TagSpecifications' not in to_skip:
+ assert 'TagSpecifications' in instance_spec
+ assert instance_spec['TagSpecifications'] is sentinel.TAG_SPEC
+
+ if 'NetworkInterfaces' not in to_skip:
+ assert 'NetworkInterfaces' in instance_spec
+ assert instance_spec['NetworkInterfaces'] is sentinel.NETWORK_SPEC
+
+ if 'BlockDeviceMappings' not in to_skip:
+ assert 'BlockDeviceMappings' in instance_spec
+ assert instance_spec['BlockDeviceMappings'] is sentinel.VOlUME_SPEC
+
+ if 'IamInstanceProfile' not in to_skip:
+ # By default, this shouldn't be returned
+ assert 'IamInstanceProfile' not in instance_spec
+
+ if 'MinCount' not in to_skip:
+ assert 'MinCount' in instance_spec
+ instance_spec['MinCount'] == 1
+
+ if 'MaxCount' not in to_skip:
+ assert 'MaxCount' in instance_spec
+ instance_spec['MaxCount'] == 1
+
+ if 'TOP_LEVEL_OPTIONS' not in to_skip:
+ assert 'TOP_LEVEL_OPTIONS' in instance_spec
+ assert instance_spec['TOP_LEVEL_OPTIONS'] is sentinel.TOP_LEVEL
+
+
+def test_build_run_instance_spec_defaults(params_object, ec2_instance):
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+ _assert_defaults(instance_spec)
+
+
+def test_build_run_instance_spec_tagging(params_object, ec2_instance, monkeypatch):
+ # build_instance_tags can return None, RunInstance doesn't like this
+ monkeypatch.setattr(ec2_instance_module, 'build_instance_tags', lambda params: None)
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+ _assert_defaults(instance_spec, ['TagSpecifications'])
+ assert 'TagSpecifications' not in instance_spec
+
+ # if someone *explicitly* passes {} (rather than not setting it), then [] can be returned
+ monkeypatch.setattr(ec2_instance_module, 'build_instance_tags', lambda params: [])
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+ _assert_defaults(instance_spec, ['TagSpecifications'])
+ assert 'TagSpecifications' in instance_spec
+ assert instance_spec['TagSpecifications'] == []
+
+
+def test_build_run_instance_spec_instance_profile(params_object, ec2_instance):
+ params_object['iam_instance_profile'] = sentinel.INSTANCE_PROFILE_NAME
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+ _assert_defaults(instance_spec, ['IamInstanceProfile'])
+ assert 'IamInstanceProfile' in instance_spec
+ assert instance_spec['IamInstanceProfile'] == {'Arn': sentinel.IAM_PROFILE_ARN}
+
+
+def test_build_run_instance_spec_count(params_object, ec2_instance):
+ # When someone passes 'count', that number of instances will be *launched*
+ params_object['count'] = sentinel.COUNT
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+ _assert_defaults(instance_spec, ['MaxCount', 'MinCount'])
+ assert 'MaxCount' in instance_spec
+ assert 'MinCount' in instance_spec
+ assert instance_spec['MaxCount'] == sentinel.COUNT
+ assert instance_spec['MinCount'] == sentinel.COUNT
+
+
+def test_build_run_instance_spec_exact_count(params_object, ec2_instance):
+ # The "exact_count" logic relies on enforce_count doing the math to figure out how many
+ # instances to start/stop. The enforce_count call is responsible for ensuring that 'to_launch'
+ # is set and is a positive integer.
+ params_object['exact_count'] = sentinel.EXACT_COUNT
+ params_object['to_launch'] = sentinel.TO_LAUNCH
+ instance_spec = ec2_instance.build_run_instance_spec(params_object)
+
+ _assert_defaults(instance_spec, ['MaxCount', 'MinCount'])
+ assert 'MaxCount' in instance_spec
+ assert 'MinCount' in instance_spec
+ assert instance_spec['MaxCount'] == sentinel.TO_LAUNCH
+ assert instance_spec['MinCount'] == sentinel.TO_LAUNCH
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_determine_iam_role.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_determine_iam_role.py
new file mode 100644
index 000000000..cdde74c97
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/ec2_instance/test_determine_iam_role.py
@@ -0,0 +1,102 @@
+# (c) 2022 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+import sys
+
+from ansible_collections.amazon.aws.tests.unit.compat.mock import MagicMock
+from ansible_collections.amazon.aws.tests.unit.compat.mock import sentinel
+import ansible_collections.amazon.aws.plugins.modules.ec2_instance as ec2_instance_module
+import ansible_collections.amazon.aws.plugins.module_utils.arn as utils_arn
+from ansible_collections.amazon.aws.plugins.module_utils.botocore import HAS_BOTO3
+
+try:
+ import botocore
+except ImportError:
+ pass
+
+pytest.mark.skipif(not HAS_BOTO3, reason="test_determine_iam_role.py requires the python modules 'boto3' and 'botocore'")
+
+
+def _client_error(code='GenericError'):
+ return botocore.exceptions.ClientError(
+ {'Error': {'Code': code, 'Message': 'Something went wrong'},
+ 'ResponseMetadata': {'RequestId': '01234567-89ab-cdef-0123-456789abcdef'}},
+ 'some_called_method')
+
+
+@pytest.fixture
+def params_object():
+ params = {
+ 'instance_role': None,
+ 'exact_count': None,
+ 'count': None,
+ 'launch_template': None,
+ 'instance_type': None,
+ }
+ return params
+
+
+class FailJsonException(Exception):
+ def __init__(self):
+ pass
+
+
+@pytest.fixture
+def ec2_instance(monkeypatch):
+ monkeypatch.setattr(ec2_instance_module, 'parse_aws_arn', lambda arn: None)
+ monkeypatch.setattr(ec2_instance_module, 'module', MagicMock())
+ ec2_instance_module.module.fail_json.side_effect = FailJsonException()
+ ec2_instance_module.module.fail_json_aws.side_effect = FailJsonException()
+ return ec2_instance_module
+
+
+def test_determine_iam_role_arn(params_object, ec2_instance, monkeypatch):
+ # Revert the default monkey patch to make it simple to try passing a valid ARNs
+ monkeypatch.setattr(ec2_instance, 'parse_aws_arn', utils_arn.parse_aws_arn)
+
+ # Simplest example, someone passes a valid instance profile ARN
+ arn = ec2_instance.determine_iam_role('arn:aws:iam::123456789012:instance-profile/myprofile')
+ assert arn == 'arn:aws:iam::123456789012:instance-profile/myprofile'
+
+
+def test_determine_iam_role_name(params_object, ec2_instance):
+ profile_description = {'InstanceProfile': {'Arn': sentinel.IAM_PROFILE_ARN}}
+ iam_client = MagicMock(**{"get_instance_profile.return_value": profile_description})
+ ec2_instance_module.module.client.return_value = iam_client
+
+ arn = ec2_instance.determine_iam_role(sentinel.IAM_PROFILE_NAME)
+ assert arn == sentinel.IAM_PROFILE_ARN
+
+
+def test_determine_iam_role_missing(params_object, ec2_instance):
+ missing_exception = _client_error('NoSuchEntity')
+ iam_client = MagicMock(**{"get_instance_profile.side_effect": missing_exception})
+ ec2_instance_module.module.client.return_value = iam_client
+
+ with pytest.raises(FailJsonException) as exception:
+ arn = ec2_instance.determine_iam_role(sentinel.IAM_PROFILE_NAME)
+
+ assert ec2_instance_module.module.fail_json_aws.call_count == 1
+ assert ec2_instance_module.module.fail_json_aws.call_args.args[0] is missing_exception
+ assert 'Could not find' in ec2_instance_module.module.fail_json_aws.call_args.kwargs['msg']
+
+
+@pytest.mark.skipif(sys.version_info < (3, 8), reason='call_args behaviour changed in Python 3.8')
+def test_determine_iam_role_missing(params_object, ec2_instance):
+ missing_exception = _client_error()
+ iam_client = MagicMock(**{"get_instance_profile.side_effect": missing_exception})
+ ec2_instance_module.module.client.return_value = iam_client
+
+ with pytest.raises(FailJsonException) as exception:
+ arn = ec2_instance.determine_iam_role(sentinel.IAM_PROFILE_NAME)
+
+ assert ec2_instance_module.module.fail_json_aws.call_count == 1
+ assert ec2_instance_module.module.fail_json_aws.call_args.args[0] is missing_exception
+ assert 'An error occurred while searching' in ec2_instance_module.module.fail_json_aws.call_args.kwargs['msg']
+ assert 'Please try supplying the full ARN' in ec2_instance_module.module.fail_json_aws.call_args.kwargs['msg']
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem
new file mode 100644
index 000000000..4412f3258
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFVTCCBD2gAwIBAgISAx4pnfwvGxYrrQhr/UXiN7HCMA0GCSqGSIb3DQEBCwUA
+MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
+ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA3MjUwMDI4NTdaFw0x
+OTEwMjMwMDI4NTdaMBoxGDAWBgNVBAMTD2NyeXB0b2dyYXBoeS5pbzCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJDpCL99DVo83587MrVp6gunmKRoUfY
+vcgk5u2v0tB9OmZkcIY37z6AunHWr18Yj55zHmm6G8Nf35hmu3ql2A26WThCbmOe
+WXbxhgarkningZI9opUWnI2dIllguVIsq99GzhpNnDdCb26s5+SRhJI4cr4hYaKC
+XGDKooKWyXUX09SJTq7nW/1+pq3y9ZMvldRKjJALeAdwnC7kmUB6pK7q8J2VlpfQ
+wqGu6q/WHVdgnhWARw3GEFJWDn9wkxBAF08CpzhVaEj+iK+Ut/1HBgNYwqI47h7S
+q+qv0G2qklRVUtEM0zYRsp+y/6vivdbFLlPw8VaerbpJN3gLtpVNcGECAwEAAaOC
+AmMwggJfMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
+BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjbe0bE1aZ8HiqtwqUfCe15bF
+V8UwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE
+YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu
+b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu
+b3JnLzAaBgNVHREEEzARgg9jcnlwdG9ncmFwaHkuaW8wTAYDVR0gBEUwQzAIBgZn
+gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
+ZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgB0ftqDMa0z
+EJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWwmvtnXAAAEAwBHMEUCIFXHYX/E
+xtbYCvjjQ3dN0HOLW1d8+aduktmax4mu3KszAiEAvTpxuSVVXJnVGA4tU2GOnI60
+sqTh/IK6hvrFN1k1HBUAdQApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0
+eAAAAWwmvtm9AAAEAwBGMEQCIDn7sgzD+7JzR+XTvjKf7VyLWwX37O8uwCfCTKo7
++tEhAiB05bHiICU5wkfRBrwcvqXf4bPF7NT5LVlRQYzJ/hbpvzANBgkqhkiG9w0B
+AQsFAAOCAQEAcMU8E6D+5WC07QSeTppRTboC++7YgQg5NiSWm7OE2FlyiRZXnu0Y
+uBoaqAkZIqj7dom9wy1c1UauxOfM9lUZKhYnDTBu9tIhBAvCS0J0avv1j1KQygQ1
+qV+urJsunUwqV/vPWo1GfWophvyXVN6MAycv34ZXZvAjtG7oDcoQVLLvK1SIo2vu
+4/dNkOQzaeZez8q6Ij9762TbBWaK5C789VMdUWZCADWoToPIK533cWbDEp4IhBU/
+K73d7lGGl7S59SjT2V/XE6eJS9Zlj0M+A8pf/8tjM/ImHAjlOHB02sM/VfZ7HAuZ
+61TPxohL+e+X1FYeqIXYGXJmCEuB8WEmBg==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem
new file mode 100644
index 000000000..2be4bca53
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem
@@ -0,0 +1,47 @@
+-----BEGIN CERTIFICATE-----
+MIIIUjCCB/egAwIBAgIRALiJR3zQjp0MevT/Hk89sfAwCgYIKoZIzj0EAwIwgZIx
+CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV
+BAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYDVQQD
+Ey9DT01PRE8gRUNDIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0Eg
+MjAeFw0xOTA3MzEwMDAwMDBaFw0yMDAyMDYyMzU5NTlaMGwxITAfBgNVBAsTGERv
+bWFpbiBDb250cm9sIFZhbGlkYXRlZDEhMB8GA1UECxMYUG9zaXRpdmVTU0wgTXVs
+dGktRG9tYWluMSQwIgYDVQQDExtzc2wzODczMzcuY2xvdWRmbGFyZXNzbC5jb20w
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPFdjdnBIJRPnHCPsCBJ/MmPytXnZX
+KV6lD2bbG5EVNuUQln4Na8heCY+sfpV+SPuuiNzZxgDA46GvyzdRYFhxo4IGUTCC
+Bk0wHwYDVR0jBBgwFoAUQAlhZ/C8g3FP3hIILG/U1Ct2PZYwHQYDVR0OBBYEFGLh
+bHk1KAYIRfVwXA3L+yDf0CxjMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA
+MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysG
+AQQBsjEBAgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5j
+b20vQ1BTMAgGBmeBDAECATBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLmNv
+bW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZl
+ckNBMi5jcmwwgYgGCCsGAQUFBwEBBHwwejBRBggrBgEFBQcwAoZFaHR0cDovL2Ny
+dC5jb21vZG9jYTQuY29tL0NPTU9ET0VDQ0RvbWFpblZhbGlkYXRpb25TZWN1cmVT
+ZXJ2ZXJDQTIuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQu
+Y29tMIIDkAYDVR0RBIIDhzCCA4OCG3NzbDM4NzMzNy5jbG91ZGZsYXJlc3NsLmNv
+bYIMKi5hanJ0Y3QuY29tghMqLmFrcmVwYnVyY3UuZ2VuLnRyghUqLmFuZHJlYXNr
+YW5lbGxvcy5jb22CDSouYW5zaWJsZS5jb22CGSouYXJ0b2Z0b3VjaC1raW5nd29v
+ZC5jb22CFyouYm91bGRlcnN3YXRlcmhvbGUuY29tghcqLmJyb2Nrc3RlY2hzdXBw
+b3J0LmNvbYIQKi5idXJjbGFyLndlYi50coIcKi5ob3Blc29uZ2ZyZW5jaGJ1bGxk
+b2dzLm5ldIIMKi5odXJyZW0uY29tghAqLmh5dmVsaWNvbnMuY29tghAqLmthcm1h
+Zml0LmNvLnVrghUqLmxvd3J5c3lzdGVtc2luYy5jb22CDioubWFuaWNydW4uY29t
+ghUqLm11dHVvZmluYW5jaWVyYS5jb22CDyoucGlsZ3JpbWFnZS5waIINKi5wa2dh
+bWVzLm9yZ4IbKi5ybHBjb25zdWx0aW5nc2VydmljZXMuY29tghYqLnJ1eWF0YWJp
+cmxlcmkuZ2VuLnRyghQqLnJ5YW5hcHBoeXNpY3NjLmNvbYIVKi53ZWFyaXRiYWNr
+d2FyZHMub3Jngg8qLnlldGlzbmFjay5jb22CCmFqcnRjdC5jb22CEWFrcmVwYnVy
+Y3UuZ2VuLnRyghNhbmRyZWFza2FuZWxsb3MuY29tggthbnNpYmxlLmNvbYIXYXJ0
+b2Z0b3VjaC1raW5nd29vZC5jb22CFWJvdWxkZXJzd2F0ZXJob2xlLmNvbYIVYnJv
+Y2tzdGVjaHN1cHBvcnQuY29tgg5idXJjbGFyLndlYi50coIaaG9wZXNvbmdmcmVu
+Y2hidWxsZG9ncy5uZXSCCmh1cnJlbS5jb22CDmh5dmVsaWNvbnMuY29tgg5rYXJt
+YWZpdC5jby51a4ITbG93cnlzeXN0ZW1zaW5jLmNvbYIMbWFuaWNydW4uY29tghNt
+dXR1b2ZpbmFuY2llcmEuY29tgg1waWxncmltYWdlLnBoggtwa2dhbWVzLm9yZ4IZ
+cmxwY29uc3VsdGluZ3NlcnZpY2VzLmNvbYIUcnV5YXRhYmlybGVyaS5nZW4udHKC
+EnJ5YW5hcHBoeXNpY3NjLmNvbYITd2Vhcml0YmFja3dhcmRzLm9yZ4INeWV0aXNu
+YWNrLmNvbTCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALIeBcyLos2KIE6HZvkr
+uYolIGdr2vpw57JJUy3vi5BeAAABbEVw8SgAAAQDAEcwRQIgE2YeTfb/d4BBUwpZ
+ihWXSR+vRyNNUg8GlOak2MFMHv0CIQCLBvtU401m5/Psg9KirQZs321BSxgUKgSQ
+m9M691d3eQB2AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKESEoQYdZaBcUVYAAABbEVw
+8VgAAAQDAEcwRQIgGYsGfr3/mekjzMS9+ALAjx1ryfIfhXB/+UghTcw4Y8ICIQDS
+K2L18WX3+Oh4TjJhjh5tV1iYyZVYivcwwbr7mtmOqjAKBggqhkjOPQQDAgNJADBG
+AiEAjNt7LF78GV7snky9jwFcBsLH55ndzduvsrkJ7Ne1SgYCIQDsMJsTr9VP6kar
+4Kv8V9zNBmpGrGNuE7A1GixBvzNaHA==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert
new file mode 100644
index 000000000..6997766ac
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert
@@ -0,0 +1,121 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy
+OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv
+cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD
+DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB
+O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz
+KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2
+mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU
+gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX
+B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG
+A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB
+BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92
+YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu
+dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j
+b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv
+c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr
+BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds
+b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j
+b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G
+A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X
+wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6
+O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh
+bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ
+EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH
+88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g
+uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp
+CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX
+L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF
+rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70
+ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2
+DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD
+hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz
+A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT
+F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1
+hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB
+RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac
+e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q
+q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0
+qkHrBgwo1zjuTMf3QOg6Z5Q=
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y
+MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK
+EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan
+H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz
+kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B
+xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD
+JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV
+HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E
+FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A
+Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs
+Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48
+gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/
+TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb
+SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
+MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
+n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
+IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
+8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
+7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
+W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
+dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
+HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert
new file mode 100644
index 000000000..51f64f08d
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert
@@ -0,0 +1,69 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIyOFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBvcmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD
+DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB
+O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuzKhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2
+mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsUgdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX
+B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB
+BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9iYWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92
+YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j
+b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdvc3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr
+BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2dsb2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5jb20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G
+A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6
+O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkhbxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ
+EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g
+uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFpCJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX
+L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LFrJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70
+ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD
+hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zozA20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT
+F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB
+RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVace2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q
+q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0qkHrBgwo1zjuTMf3QOg6Z5Q=
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0yMjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vKEymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPanH05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jzkE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5BxxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uDJGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNVHSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4EFgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/AVn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTsY4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMbSXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert
new file mode 100644
index 000000000..ce2992411
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert
@@ -0,0 +1,113 @@
+-----BEGIN CERTIFICATE-----
+MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy
+OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv
+cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD
+DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB
+O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz
+KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2
+mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU
+gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX
+B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG
+A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB
+BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92
+YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu
+dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j
+b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv
+c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr
+BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds
+b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j
+b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G
+A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X
+wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6
+O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh
+bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ
+EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH
+88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g
+uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp
+CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX
+L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF
+rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70
+ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2
+DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD
+hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz
+A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT
+F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1
+hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB
+RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac
+e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q
+q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0
+qkHrBgwo1zjuTMf3QOg6Z5Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y
+MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK
+EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan
+H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz
+kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B
+xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD
+JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV
+HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E
+FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A
+Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs
+Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48
+gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/
+TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb
+SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
+MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
+n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
+IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
+8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
+7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
+W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
+dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
+HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert
new file mode 100644
index 000000000..0c947b17b
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert
@@ -0,0 +1,124 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy
+OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv
+cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD
+DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB
+O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz
+KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2
+mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU
+gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX
+B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG
+A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB
+BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92
+YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu
+dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j
+b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv
+c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr
+BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds
+b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j
+b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G
+A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X
+wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6
+O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh
+bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ
+EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH
+88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g
+uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp
+CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX
+L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF
+rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70
+ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2
+DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD
+hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz
+A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT
+F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1
+hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB
+RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac
+e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q
+q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0
+qkHrBgwo1zjuTMf3QOg6Z5Q=
+-----END CERTIFICATE-----
+
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
+MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
+n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
+IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
+8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
+7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
+W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
+dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
+HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
+
+
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y
+MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK
+EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan
+H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz
+kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B
+xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD
+JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV
+HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E
+FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A
+Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs
+Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48
+gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/
+TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb
+SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert
new file mode 100644
index 000000000..adbb8edca
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert
@@ -0,0 +1,86 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy
+OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv
+cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD
+DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB
+O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz
+KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2
+mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU
+gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX
+B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG
+A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB
+BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i
+YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92
+YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu
+dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j
+b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv
+c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr
+BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds
+b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
+AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j
+b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G
+A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X
+wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6
+O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh
+bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ
+EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH
+88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g
+uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp
+CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX
+L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF
+rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70
+ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2
+DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD
+hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz
+A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT
+F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1
+hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB
+RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac
+e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q
+q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0
+qkHrBgwo1zjuTMf3QOg6Z5Q=
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y
+MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK
+EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan
+H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz
+kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B
+xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD
+JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV
+HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E
+FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A
+Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs
+Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48
+gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/
+TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb
+SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert
new file mode 100644
index 000000000..2b82edf6c
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert
@@ -0,0 +1,121 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=prod.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+MIIIJDCCBgygAwIBAgIUP9S/56XvOFzWk1vp1+7JJT17brEwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh
+BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyNzAzMTU1
+NFoXDTIxMDgyNzAzMjUwMFowgZwxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0
+b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv
+cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MScwJQYDVQQD
+DB5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCrRouNZFOZwM1qyAU6v6ag9fzSx3y8zz36nR8HuqbA
+/wqrbMmnpofwdx/9u1bilsHfJzIODv0hm7aGk+neTK3DIapiII3m0HKW0v+GLsl7
+JkDuc2o3XlakcXlA45qDKCZXbXZtY4/kdxKG0OSUZi7oQqohhYl/c/ojrTiey+4G
+KhEVqWwOuQ1OC1DRw4qMH54d0koFxxSLPJ8JiiztLlK/e9n8BoJikj5fBqWy5R1F
+bGXCdzjcfmPV6iSOzJShpUgj4ga91mO6j3S6LLfK5ibbTlY+pmUxUT+m9nKMon3h
+mFptTYo9t9vUF/a/owjRxNLg01fJLNjYn8QV2vQvODGfAgMBAAGjggOqMIIDpjAJ
+BgNVHRMEAjAAMB8GA1UdIwQYMBaAFLMSibWpSzW8FQDwgOnYeIfxE3x2MHMGCCsG
+AQUFBwEBBGcwZTA3BggrBgEFBQcwAoYraHR0cDovL3RydXN0LnF1b3ZhZGlzZ2xv
+YmFsLmNvbS9xdnNzbGczLmNydDAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVv
+dmFkaXNnbG9iYWwuY29tMIGjBgNVHREEgZswgZiCHnByb2QuZW5lcmd5Lmluc2lk
+ZS50ZWxzdHJhLmNvbYImcmVwb3J0cy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3Ry
+YS5jb22CKGdyZWVuc3luYy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22C
+JG5nb3NzLnByb2QuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTBRBgNVHSAESjBI
+MEYGDCsGAQQBvlgAAmQBATA2MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1b3Zh
+ZGlzZ2xvYmFsLmNvbS9yZXBvc2l0b3J5MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
+BgEFBQcDATA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnF1b3ZhZGlzZ2xv
+YmFsLmNvbS9xdnNzbGczLmNybDAdBgNVHQ4EFgQUoIME5TykVAI8VF5g0zeh0xdv
+i3owDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgBW
+FAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAAAWzRG8r0AAAEAwBHMEUC
+IQDShuQyYMiy7KKxWOzffolVIcPRgWD7ClNEbIcUATHKyQIgXnTZBXcpcbXBQXLs
+tFuvY36TbKIYc2ql2nmdydGQ9wcAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jj
+d80OyA3cEAAAAWzRG8sAAAAEAwBHMEUCIGsLEoA9S7pNE3VoNZHxl2IAdeP3Dy2Q
+Mk0rM46hp6CRAiEA08rOjswSdcn7qgDEoiyvlcrOTIFJAEcMlxSY65yLVUwAdgBV
+gdTCFpA2AUrqC5tXPFPwwOQ4eHAlCBcvo6odBxPTDAAAAWzRG8q7AAAEAwBHMEUC
+IAkVCcTFG8MBDI58JKIhMlPbzkdrKnYY3Kp9KqWuTAvMAiEAipeI7RCLBk8+T/p+
+gY7+vtFZxKDthcJMUpZz7qmica0wDQYJKoZIhvcNAQELBQADggIBAESe0U1qArxL
+F2uk65q6x6HBcZuSocpceokzcUBv07Kxs6UJU9ybTbl8VYPuC+OUdpvut1kOJCJm
+1TRrr5KMh+9as42xkbKRZnh5TQt7aHmVcLHLfA4x0UrELfNX3fVTDxwDAPAhE5oM
+0w+d1foLakh7dXKKSxobEI3KRwFp19iuZeIqwI8XMWMr9ajhTC0T7D2QvKotpNBS
+sNDHiIE3IXoa9o7UiOG8IfW0wAt7CEygv0F7ctHRTcQSP/SJIGYOUZ7uotULVL5i
+elG31Y83Jx3sPNCy4IZfCip6Gw7MgsN2CZGApqi49edSqDWyRIfmCeXtMc7XI7Md
+kqqWxbqGGTdYJCucoGqahqRR+BI9anEqTD9T5Gy0TpCi2pgp1i7czza71nfz0PcN
+R0pw/1lqb9AqmJ2XELpBpo82B9XGple9thpincai7jPk3ezY5eEvDTmkHRlUFCp8
+8M66Ga19hZTgnHPWDKZYZzuZ7Lcl2WbapFOYYHJggSpBRy4GkH6eTSkUB9G9k8vU
+gbvtS7sR5ggecbCBu0M4TWYmnUojR8UXtr0oOTlXysTHVGs5Tx9ChhOLyUqhX8tM
+1zSDT8JJvbbw4RqpGzBKTNaO5nxRLgKVQOQdM8f1kjMr9/U58Lc4UiaTkJM14VfK
+8GfV8+K/vRCBtME53ILvm1l18jtakG3c
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y
+MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y
+Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK
+EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM
+7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan
+H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3
+2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz
+kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N
+UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B
+xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ
+yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD
+JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc
+1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV
+HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw
+Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0
+dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E
+FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp
+RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A
+Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto
+qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs
+Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl
+qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48
+gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV
+7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/
+TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J
+ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb
+SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh
+8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
+BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
+BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
+MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
+SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
+qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
+n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
+c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
+o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
+IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
+IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
+8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
+vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
+7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
+cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
+ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
+roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
+W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
+lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
+csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
+dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
+KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
+HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
+WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert
new file mode 100644
index 000000000..1d9bbe213
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert
@@ -0,0 +1,18 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+aaa
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+bbb
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+ccc
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert
new file mode 100644
index 000000000..1d9bbe213
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert
@@ -0,0 +1,18 @@
+subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+-----BEGIN CERTIFICATE-----
+aaa
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+bbb
+-----END CERTIFICATE-----
+
+subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3
+-----BEGIN CERTIFICATE-----
+ccc
+-----END CERTIFICATE-----
+
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip
new file mode 100644
index 000000000..6eaefdd5e
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip
Binary files differ
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..36f1489ba
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json
@@ -0,0 +1,17 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:07 GMT",
+ "content-length": "393",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json
new file mode 100644
index 000000000..d526155a5
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json
@@ -0,0 +1,16 @@
+{
+ "status_code": 200,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "170d1e02-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "170d1e02-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT",
+ "content-length": "212",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..3758c77b7
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,38 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "043d4a05-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "043d4a05-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:08 GMT",
+ "content-length": "1183",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..2c5a7655e
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,80 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "075d9d71-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "075d9d71-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:13 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json
new file mode 100644
index 000000000..cf2c24502
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json
@@ -0,0 +1,80 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:19 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json
new file mode 100644
index 000000000..32ee9c1c5
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json
@@ -0,0 +1,80 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:24 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json
new file mode 100644
index 000000000..b547cd4d8
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json
@@ -0,0 +1,80 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:29 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json
new file mode 100644
index 000000000..15bd043ab
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json
@@ -0,0 +1,100 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 33,
+ "microsecond": 200000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "3490",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:34 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json
new file mode 100644
index 000000000..87db7c59e
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json
@@ -0,0 +1,119 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "140d7220-b5d0-11e7-933f-50a686be7356",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 35,
+ "microsecond": 121000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 33,
+ "microsecond": 200000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-basic-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "LogicalResourceId": "ansible-test-basic-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "16faf590-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "16faf590-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "4276",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..7acdb3acf
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,40 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EnableTerminationProtection": false,
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "StackStatusReason": "User Initiated",
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "042974db-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "042974db-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:08 GMT",
+ "content-length": "975",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json
new file mode 100644
index 000000000..0ed674b20
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "074b26dc-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "074b26dc-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:13 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json
new file mode 100644
index 000000000..633c5e159
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:18 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json
new file mode 100644
index 000000000..e5ca69dda
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:23 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json
new file mode 100644
index 000000000..31a3057cd
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:28 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json
new file mode 100644
index 000000000..90ca7467c
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:33 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json
new file mode 100644
index 000000000..905c04f48
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json
@@ -0,0 +1,45 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "Outputs": [
+ {
+ "OutputKey": "TheName",
+ "OutputValue": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4"
+ }
+ ],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-basic-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_COMPLETE",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT",
+ "content-length": "1115",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..9084936a4
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json
@@ -0,0 +1,17 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:07 GMT",
+ "content-length": "393",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json
new file mode 100644
index 000000000..d526155a5
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json
@@ -0,0 +1,16 @@
+{
+ "status_code": 200,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "170d1e02-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "170d1e02-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT",
+ "content-length": "212",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..399eab496
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "043d4a05-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "043d4a05-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:08 GMT",
+ "content-length": "1183",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..f57dbf536
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,83 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "075d9d71-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "075d9d71-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:13 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json
new file mode 100644
index 000000000..c8b4d694d
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json
@@ -0,0 +1,83 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:19 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json
new file mode 100644
index 000000000..8bb03eded
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json
@@ -0,0 +1,83 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:24 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json
new file mode 100644
index 000000000..311949d08
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json
@@ -0,0 +1,83 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "2730",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:29 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json
new file mode 100644
index 000000000..ddab94a51
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json
@@ -0,0 +1,104 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 33,
+ "microsecond": 200000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "3490",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:34 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json
new file mode 100644
index 000000000..86da5fb45
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json
@@ -0,0 +1,124 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "140d7220-b5d0-11e7-933f-50a686be7356",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 35,
+ "microsecond": 121000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z",
+ "ResourceStatus": "CREATE_COMPLETE",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 33,
+ "microsecond": 200000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 12,
+ "microsecond": 754000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "Resource creation Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::S3::Bucket",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 11,
+ "microsecond": 159000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "ResourceProperties": "{}\n",
+ "PhysicalResourceId": "",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "MyBucket"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5",
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "ResourceStatusReason": "User Initiated",
+ "StackName": "ansible-test-client-request-token-yaml",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf",
+ "LogicalResourceId": "ansible-test-client-request-token-yaml"
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "16faf590-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "16faf590-b5d0-11e7-ae09-550cfe4b2358",
+ "vary": "Accept-Encoding",
+ "content-length": "4276",
+ "content-type": "text/xml",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..7734b0ca3
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,40 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "EnableTerminationProtection": false,
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "StackStatusReason": "User Initiated",
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "042974db-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "042974db-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:08 GMT",
+ "content-length": "975",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json
new file mode 100644
index 000000000..0a1e74d70
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "074b26dc-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "074b26dc-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:13 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json
new file mode 100644
index 000000000..12d5839f8
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:18 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json
new file mode 100644
index 000000000..a3cb0a8ca
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:23 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json
new file mode 100644
index 000000000..251d71fa1
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:28 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json
new file mode 100644
index 000000000..2251125f6
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json
@@ -0,0 +1,39 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:33 GMT",
+ "content-length": "913",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json
new file mode 100644
index 000000000..aa8c7fd09
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json
@@ -0,0 +1,45 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5",
+ "Description": "Basic template that creates an S3 bucket",
+ "Tags": [],
+ "Outputs": [
+ {
+ "OutputKey": "TheName",
+ "OutputValue": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4"
+ }
+ ],
+ "EnableTerminationProtection": false,
+ "CreationTime": {
+ "hour": 19,
+ "__class__": "datetime",
+ "month": 10,
+ "second": 8,
+ "microsecond": 324000,
+ "year": 2017,
+ "day": 20,
+ "minute": 51
+ },
+ "StackName": "ansible-test-client-request-token-yaml",
+ "NotificationARNs": [],
+ "StackStatus": "CREATE_COMPLETE",
+ "DisableRollback": false,
+ "RollbackConfiguration": {}
+ }
+ ],
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 200,
+ "RequestId": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:39 GMT",
+ "content-length": "1115",
+ "content-type": "text/xml"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..109feacd9
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,22 @@
+{
+ "status_code": 400,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 400,
+ "RequestId": "179d9e46-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "179d9e46-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:40 GMT",
+ "content-length": "301",
+ "content-type": "text/xml",
+ "connection": "close"
+ }
+ },
+ "Error": {
+ "Message": "Stack [ansible-test-nonexist] does not exist",
+ "Code": "ValidationError",
+ "Type": "Sender"
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..589f92cc6
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,22 @@
+{
+ "status_code": 400,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 400,
+ "RequestId": "17d80f44-b5d0-11e7-80c4-9f499f779cdb",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "17d80f44-b5d0-11e7-80c4-9f499f779cdb",
+ "date": "Fri, 20 Oct 2017 19:51:40 GMT",
+ "content-length": "301",
+ "content-type": "text/xml",
+ "connection": "close"
+ }
+ },
+ "Error": {
+ "Message": "Stack [ansible-test-nonexist] does not exist",
+ "Code": "ValidationError",
+ "Type": "Sender"
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..ea227415c
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,22 @@
+{
+ "status_code": 400,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 400,
+ "RequestId": "175fab26-b5d0-11e7-9d9b-45815c77100a",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "175fab26-b5d0-11e7-9d9b-45815c77100a",
+ "date": "Fri, 20 Oct 2017 19:51:40 GMT",
+ "content-length": "307",
+ "content-type": "text/xml",
+ "connection": "close"
+ }
+ },
+ "Error": {
+ "Message": "Stack with id ansible-test-nonexist does not exist",
+ "Code": "ValidationError",
+ "Type": "Sender"
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..cf29c6c76
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,22 @@
+{
+ "status_code": 400,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 400,
+ "RequestId": "181566c8-b5d0-11e7-9d9b-45815c77100a",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "181566c8-b5d0-11e7-9d9b-45815c77100a",
+ "date": "Fri, 20 Oct 2017 19:51:41 GMT",
+ "content-length": "307",
+ "content-type": "text/xml",
+ "connection": "close"
+ }
+ },
+ "Error": {
+ "Message": "Stack with id ansible-test-nonexist does not exist",
+ "Code": "ValidationError",
+ "Type": "Sender"
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..7ad6cac96
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json
@@ -0,0 +1,22 @@
+{
+ "status_code": 400,
+ "data": {
+ "ResponseMetadata": {
+ "RetryAttempts": 0,
+ "HTTPStatusCode": 400,
+ "RequestId": "03b1107f-b5d0-11e7-ae09-550cfe4b2358",
+ "HTTPHeaders": {
+ "x-amzn-requestid": "03b1107f-b5d0-11e7-ae09-550cfe4b2358",
+ "date": "Fri, 20 Oct 2017 19:51:07 GMT",
+ "content-length": "320",
+ "content-type": "text/xml",
+ "connection": "close"
+ }
+ },
+ "Error": {
+ "Message": "Template format error: JSON not well-formed. (line 4, column 4)",
+ "Code": "ValidationError",
+ "Type": "Sender"
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..64c8e1f23
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json
@@ -0,0 +1,17 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResponseMetadata": {
+ "RequestId": "c741ebcd-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "c741ebcd-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "407",
+ "date": "Tue, 26 Feb 2019 21:37:55 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..7a6a49644
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,38 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "c74b1310-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "c7b0b337-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "c7b0b337-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "1153",
+ "date": "Tue, 26 Feb 2019 21:37:56 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..6218ed8b8
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,101 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 1,
+ "microsecond": 107000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 657000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 221000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "c74b1310-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "caf667e9-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "caf667e9-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "4312",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:38:01 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json
new file mode 100644
index 000000000..cde6beb8e
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json
@@ -0,0 +1,121 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user."
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 1,
+ "microsecond": 107000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 657000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 221000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "c74b1310-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "ce498af1-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "ce498af1-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "5207",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:38:06 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json
new file mode 100644
index 000000000..4f35d6ddc
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json
@@ -0,0 +1,180 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "d19c8600-3a0e-11e9-a4ba-0a3524ef8042",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 13,
+ "microsecond": 177000
+ },
+ "ResourceStatus": "DELETE_COMPLETE"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:38:12.486Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 12,
+ "microsecond": 486000
+ },
+ "ResourceStatus": "DELETE_COMPLETE",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:38:12.139Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 12,
+ "microsecond": 139000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user."
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 1,
+ "microsecond": 107000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 657000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 221000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "c74b1310-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "d19fbb1b-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "d19fbb1b-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "7857",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:38:12 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json
new file mode 100644
index 000000000..68a743f89
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json
@@ -0,0 +1,180 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "d19c8600-3a0e-11e9-a4ba-0a3524ef8042",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 13,
+ "microsecond": 177000
+ },
+ "ResourceStatus": "DELETE_COMPLETE"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:38:12.486Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 12,
+ "microsecond": 486000
+ },
+ "ResourceStatus": "DELETE_COMPLETE",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:38:12.139Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 12,
+ "microsecond": 139000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user."
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 1,
+ "microsecond": 107000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 657000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 0,
+ "microsecond": 221000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "EventId": "c74b1310-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "LogicalResourceId": "ansible-test-on-create-failure-delete",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "d4fbddab-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "d4fbddab-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "7857",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:38:18 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..cf5f86acb
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,42 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "StackStatusReason": "User Initiated",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "c77fb823-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "c77fb823-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "1041",
+ "date": "Tue, 26 Feb 2019 21:37:56 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json
new file mode 100644
index 000000000..71a9f54b6
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json
@@ -0,0 +1,41 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "cad153b2-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "cad153b2-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "979",
+ "date": "Tue, 26 Feb 2019 21:38:01 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json
new file mode 100644
index 000000000..c2028183b
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json
@@ -0,0 +1,52 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "DeletionTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "DELETE_IN_PROGRESS",
+ "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user.",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "ce24289a-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "ce24289a-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "1171",
+ "date": "Tue, 26 Feb 2019 21:38:06 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json
new file mode 100644
index 000000000..89f835531
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json
@@ -0,0 +1,51 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "DeletionTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "DELETE_IN_PROGRESS",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "d16c27f2-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "d16c27f2-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "1041",
+ "date": "Tue, 26 Feb 2019 21:38:12 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json
new file mode 100644
index 000000000..739c82937
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json
@@ -0,0 +1,50 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828",
+ "StackName": "ansible-test-on-create-failure-delete",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 37,
+ "second": 55,
+ "microsecond": 909000
+ },
+ "DeletionTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 38,
+ "second": 2,
+ "microsecond": 76000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "DELETE_COMPLETE",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "d4c90dd6-3a0e-11e9-b25f-d1217e6893bf",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "d4c90dd6-3a0e-11e9-b25f-d1217e6893bf",
+ "content-type": "text/xml",
+ "content-length": "965",
+ "date": "Tue, 26 Feb 2019 21:38:18 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..86f1945fd
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json
@@ -0,0 +1,17 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "ResponseMetadata": {
+ "RequestId": "a396a58a-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a396a58a-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "411",
+ "date": "Tue, 26 Feb 2019 21:44:05 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json
new file mode 100644
index 000000000..1a3a67c64
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json
@@ -0,0 +1,16 @@
+{
+ "status_code": 200,
+ "data": {
+ "ResponseMetadata": {
+ "RequestId": "a78f0832-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a78f0832-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "212",
+ "date": "Tue, 26 Feb 2019 21:44:11 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..58d7a89e4
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,38 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "a39e6ce0-3a0f-11e9-96ca-02f46dd00950",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ansible-test-on-create-failure-do-nothing",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 5,
+ "microsecond": 553000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "a406cc84-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a406cc84-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "1169",
+ "date": "Tue, 26 Feb 2019 21:44:06 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..0a7e32e46
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,121 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "a6c32c80-3a0f-11e9-ac5e-06deb474fa52",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ansible-test-on-create-failure-do-nothing",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 10,
+ "microsecond": 804000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. "
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:44:09.905Z",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-a8g0mh5il4t5",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 9,
+ "microsecond": 905000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: a62a6f71-3a0f-11e9-9164-457e0a3a5e1b)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:44:09.497Z",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-a8g0mh5il4t5",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 9,
+ "microsecond": 497000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:44:09.076Z",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 9,
+ "microsecond": 76000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "EventId": "a39e6ce0-3a0f-11e9-96ca-02f46dd00950",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "LogicalResourceId": "ansible-test-on-create-failure-do-nothing",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 5,
+ "microsecond": 553000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "a75fbad0-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a75fbad0-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "5231",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:44:11 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..532143313
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,42 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 5,
+ "microsecond": 553000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "StackStatusReason": "User Initiated",
+ "DisableRollback": true,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "a3d44acf-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a3d44acf-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "1048",
+ "date": "Tue, 26 Feb 2019 21:44:05 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json
new file mode 100644
index 000000000..df17f5a73
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json
@@ -0,0 +1,42 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950",
+ "StackName": "ansible-test-on-create-failure-do-nothing",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 44,
+ "second": 5,
+ "microsecond": 553000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "CREATE_FAILED",
+ "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. ",
+ "DisableRollback": true,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "a7301f4a-3a0f-11e9-b7db-3fe3824c73cb",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "a7301f4a-3a0f-11e9-b7db-3fe3824c73cb",
+ "content-type": "text/xml",
+ "content-length": "1084",
+ "date": "Tue, 26 Feb 2019 21:44:11 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json
new file mode 100644
index 000000000..f71422b92
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json
@@ -0,0 +1,17 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResponseMetadata": {
+ "RequestId": "9139de54-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "9139de54-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "409",
+ "date": "Tue, 26 Feb 2019 21:43:34 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json
new file mode 100644
index 000000000..111dc90d8
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json
@@ -0,0 +1,16 @@
+{
+ "status_code": 200,
+ "data": {
+ "ResponseMetadata": {
+ "RequestId": "988b3097-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "988b3097-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "212",
+ "date": "Tue, 26 Feb 2019 21:43:46 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+} \ No newline at end of file
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json
new file mode 100644
index 000000000..2bcac7f0e
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json
@@ -0,0 +1,38 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "9199b1a7-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "9199b1a7-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "1161",
+ "date": "Tue, 26 Feb 2019 21:43:35 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json
new file mode 100644
index 000000000..3992fd397
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json
@@ -0,0 +1,121 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "945b90a0-3a0f-11e9-adaf-0211d8bec7e2",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 920000
+ },
+ "ResourceStatus": "ROLLBACK_IN_PROGRESS",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user."
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:43:39.210Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 210000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: 93e0bb60-3a0f-11e9-a53c-7162bb423e4d)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.793Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 38,
+ "microsecond": 793000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.266Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 38,
+ "microsecond": 266000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "94e16307-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "94e16307-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "5241",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:43:40 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json
new file mode 100644
index 000000000..e272c734b
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json
@@ -0,0 +1,180 @@
+{
+ "status_code": 200,
+ "data": {
+ "StackEvents": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "9743bc70-3a0f-11e9-b335-0ade61d04ee6",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 44,
+ "microsecond": 797000
+ },
+ "ResourceStatus": "ROLLBACK_COMPLETE"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:43:43.908Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 43,
+ "microsecond": 908000
+ },
+ "ResourceStatus": "DELETE_COMPLETE",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:43:43.478Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 43,
+ "microsecond": 478000
+ },
+ "ResourceStatus": "DELETE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "945b90a0-3a0f-11e9-adaf-0211d8bec7e2",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 920000
+ },
+ "ResourceStatus": "ROLLBACK_IN_PROGRESS",
+ "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user."
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:43:39.210Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 210000
+ },
+ "ResourceStatus": "CREATE_FAILED",
+ "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: 93e0bb60-3a0f-11e9-a53c-7162bb423e4d)",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.793Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 38,
+ "microsecond": 793000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "Resource creation Initiated",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.266Z",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ECRRepo",
+ "PhysicalResourceId": "",
+ "ResourceType": "AWS::ECR::Repository",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 38,
+ "microsecond": 266000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}"
+ },
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "LogicalResourceId": "ansible-test-on-create-failure-rollback",
+ "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "ResourceType": "AWS::CloudFormation::Stack",
+ "Timestamp": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "ResourceStatus": "CREATE_IN_PROGRESS",
+ "ResourceStatusReason": "User Initiated"
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "982d0bff-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "982d0bff-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "7911",
+ "vary": "Accept-Encoding",
+ "date": "Tue, 26 Feb 2019 21:43:45 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json
new file mode 100644
index 000000000..25facea18
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json
@@ -0,0 +1,42 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "CREATE_IN_PROGRESS",
+ "StackStatusReason": "User Initiated",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "91725383-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "91725383-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "1045",
+ "date": "Tue, 26 Feb 2019 21:43:35 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json
new file mode 100644
index 000000000..55a80d8af
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json
@@ -0,0 +1,52 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "DeletionTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 920000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "ROLLBACK_IN_PROGRESS",
+ "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user.",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "94bb1651-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "94bb1651-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "1179",
+ "date": "Tue, 26 Feb 2019 21:43:40 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json
new file mode 100644
index 000000000..7c00a8364
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json
@@ -0,0 +1,51 @@
+{
+ "status_code": 200,
+ "data": {
+ "Stacks": [
+ {
+ "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014",
+ "StackName": "ansible-test-on-create-failure-rollback",
+ "CreationTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 34,
+ "microsecond": 740000
+ },
+ "DeletionTime": {
+ "__class__": "datetime",
+ "year": 2019,
+ "month": 2,
+ "day": 26,
+ "hour": 21,
+ "minute": 43,
+ "second": 39,
+ "microsecond": 920000
+ },
+ "RollbackConfiguration": {},
+ "StackStatus": "ROLLBACK_COMPLETE",
+ "DisableRollback": false,
+ "NotificationARNs": [],
+ "Tags": [],
+ "EnableTerminationProtection": false,
+ "DriftInformation": {
+ "StackDriftStatus": "NOT_CHECKED"
+ }
+ }
+ ],
+ "ResponseMetadata": {
+ "RequestId": "98016814-3a0f-11e9-b938-97983b40cabe",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {
+ "x-amzn-requestid": "98016814-3a0f-11e9-b938-97983b40cabe",
+ "content-type": "text/xml",
+ "content-length": "1044",
+ "date": "Tue, 26 Feb 2019 21:43:45 GMT"
+ },
+ "RetryAttempts": 0
+ }
+ }
+}
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py
new file mode 100644
index 000000000..f46bc1113
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py
@@ -0,0 +1,227 @@
+# (c) 2017 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+# Magic...
+from ansible_collections.amazon.aws.tests.unit.utils.amazon_placebo_fixtures import maybe_sleep, placeboify # pylint: disable=unused-import
+
+from ansible_collections.amazon.aws.plugins.module_utils.botocore import boto_exception
+from ansible_collections.amazon.aws.plugins.module_utils.modules import _RetryingBotoClientWrapper
+from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry
+
+from ansible_collections.amazon.aws.plugins.modules import cloudformation as cfn_module
+
+basic_yaml_tpl = """
+---
+AWSTemplateFormatVersion: '2010-09-09'
+Description: 'Basic template that creates an S3 bucket'
+Resources:
+ MyBucket:
+ Type: "AWS::S3::Bucket"
+Outputs:
+ TheName:
+ Value:
+ !Ref MyBucket
+"""
+
+bad_json_tpl = """{
+ "AWSTemplateFormatVersion": "2010-09-09",
+ "Description": "Broken template, no comma here ->"
+ "Resources": {
+ "MyBucket": {
+ "Type": "AWS::S3::Bucket"
+ }
+ }
+}"""
+
+failing_yaml_tpl = """
+---
+AWSTemplateFormatVersion: 2010-09-09
+Resources:
+ ECRRepo:
+ Type: AWS::ECR::Repository
+ Properties:
+ RepositoryPolicyText:
+ Version: 3000-10-17 # <--- invalid version
+ Statement:
+ - Effect: Allow
+ Action:
+ - 'ecr:*'
+ Principal:
+ AWS: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root
+"""
+
+default_events_limit = 10
+
+
+class FakeModule(object):
+ def __init__(self, **kwargs):
+ self.params = kwargs
+
+ def fail_json(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('FAIL')
+
+ def fail_json_aws(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('FAIL')
+
+ def exit_json(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('EXIT')
+
+
+def _create_wrapped_client(placeboify):
+ connection = placeboify.client('cloudformation')
+ retry_decorator = AWSRetry.jittered_backoff()
+ wrapped_conn = _RetryingBotoClientWrapper(connection, retry_decorator)
+ return wrapped_conn
+
+
+def test_invalid_template_json(placeboify):
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-wrong-json',
+ 'TemplateBody': bad_json_tpl,
+ }
+ m = FakeModule(disable_rollback=False)
+ with pytest.raises(Exception) as exc_info:
+ cfn_module.create_stack(m, params, connection, default_events_limit)
+ pytest.fail('Expected malformed JSON to have caused the call to fail')
+
+ assert exc_info.match('FAIL')
+ assert "ValidationError" in boto_exception(m.exit_args[0])
+
+
+def test_client_request_token_s3_stack(maybe_sleep, placeboify):
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-client-request-token-yaml',
+ 'TemplateBody': basic_yaml_tpl,
+ 'ClientRequestToken': '3faf3fb5-b289-41fc-b940-44151828f6cf',
+ }
+ m = FakeModule(disable_rollback=False)
+ result = cfn_module.create_stack(m, params, connection, default_events_limit)
+ assert result['changed']
+ assert len(result['events']) > 1
+ # require that the final recorded stack state was CREATE_COMPLETE
+ # events are retrieved newest-first, so 0 is the latest
+ assert 'CREATE_COMPLETE' in result['events'][0]
+ connection.delete_stack(StackName='ansible-test-client-request-token-yaml')
+
+
+def test_basic_s3_stack(maybe_sleep, placeboify):
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-basic-yaml',
+ 'TemplateBody': basic_yaml_tpl
+ }
+ m = FakeModule(disable_rollback=False)
+ result = cfn_module.create_stack(m, params, connection, default_events_limit)
+ assert result['changed']
+ assert len(result['events']) > 1
+ # require that the final recorded stack state was CREATE_COMPLETE
+ # events are retrieved newest-first, so 0 is the latest
+ assert 'CREATE_COMPLETE' in result['events'][0]
+ connection.delete_stack(StackName='ansible-test-basic-yaml')
+
+
+def test_delete_nonexistent_stack(maybe_sleep, placeboify):
+ connection = _create_wrapped_client(placeboify)
+ # module is only used if we threw an unexpected error
+ module = None
+ result = cfn_module.stack_operation(module, connection, 'ansible-test-nonexist', 'DELETE', default_events_limit)
+ assert result['changed']
+ assert 'Stack does not exist.' in result['log']
+
+
+def test_get_nonexistent_stack(placeboify):
+ connection = _create_wrapped_client(placeboify)
+ # module is only used if we threw an unexpected error
+ module = None
+ assert cfn_module.get_stack_facts(module, connection, 'ansible-test-nonexist') is None
+
+
+def test_missing_template_body():
+ m = FakeModule()
+ with pytest.raises(Exception) as exc_info:
+ cfn_module.create_stack(
+ module=m,
+ stack_params={},
+ cfn=None,
+ events_limit=default_events_limit
+ )
+ pytest.fail('Expected module to have failed with no template')
+
+ assert exc_info.match('FAIL')
+ assert not m.exit_args
+ assert "Either 'template', 'template_body' or 'template_url' is required when the stack does not exist." == m.exit_kwargs['msg']
+
+
+def test_on_create_failure_delete(maybe_sleep, placeboify):
+ m = FakeModule(
+ on_create_failure='DELETE',
+ disable_rollback=False,
+ )
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-on-create-failure-delete',
+ 'TemplateBody': failing_yaml_tpl
+ }
+ result = cfn_module.create_stack(m, params, connection, default_events_limit)
+ assert result['changed']
+ assert result['failed']
+ assert len(result['events']) > 1
+ # require that the final recorded stack state was DELETE_COMPLETE
+ # events are retrieved newest-first, so 0 is the latest
+ assert 'DELETE_COMPLETE' in result['events'][0]
+
+
+def test_on_create_failure_rollback(maybe_sleep, placeboify):
+ m = FakeModule(
+ on_create_failure='ROLLBACK',
+ disable_rollback=False,
+ )
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-on-create-failure-rollback',
+ 'TemplateBody': failing_yaml_tpl
+ }
+ result = cfn_module.create_stack(m, params, connection, default_events_limit)
+ assert result['changed']
+ assert result['failed']
+ assert len(result['events']) > 1
+ # require that the final recorded stack state was ROLLBACK_COMPLETE
+ # events are retrieved newest-first, so 0 is the latest
+ assert 'ROLLBACK_COMPLETE' in result['events'][0]
+ connection.delete_stack(StackName=params['StackName'])
+
+
+def test_on_create_failure_do_nothing(maybe_sleep, placeboify):
+ m = FakeModule(
+ on_create_failure='DO_NOTHING',
+ disable_rollback=False,
+ )
+ connection = _create_wrapped_client(placeboify)
+ params = {
+ 'StackName': 'ansible-test-on-create-failure-do-nothing',
+ 'TemplateBody': failing_yaml_tpl
+ }
+ result = cfn_module.create_stack(m, params, connection, default_events_limit)
+ assert result['changed']
+ assert result['failed']
+ assert len(result['events']) > 1
+ # require that the final recorded stack state was CREATE_FAILED
+ # events are retrieved newest-first, so 0 is the latest
+ assert 'CREATE_FAILED' in result['events'][0]
+ connection.delete_stack(StackName=params['StackName'])
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_ami.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_ami.py
new file mode 100644
index 000000000..5e8140d4a
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_ami.py
@@ -0,0 +1,44 @@
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from unittest.mock import MagicMock, Mock, patch, call
+
+import pytest
+
+from ansible_collections.amazon.aws.plugins.modules import ec2_ami
+
+module_name = "ansible_collections.amazon.aws.plugins.modules.ec2_ami"
+
+
+@patch(module_name + ".get_image_by_id")
+def test_create_image_uefi_data(m_get_image_by_id):
+ module = MagicMock()
+ connection = MagicMock()
+
+ m_get_image_by_id.return_value = {
+ "ImageId": "ami-0c7a795306730b288",
+ "BootMode": "uefi",
+ "TpmSupport": "v2.0",
+ }
+
+ module.params = {
+ "name": "my-image",
+ "boot_mode": "uefi",
+ "tpm_support": "v2.0",
+ "uefi_data": "QU1aTlVFRkk9xcN0AAAAAHj5a7fZ9+3aT2gcVRgA8Ek3NipiPST0pCiCIlTJtj20FzENCcQa",
+ }
+
+ ec2_ami.create_image(module, connection)
+ assert connection.register_image.call_count == 1
+ connection.register_image.assert_has_calls(
+ [
+ call(
+ aws_retry=True,
+ Description=None,
+ Name="my-image",
+ BootMode="uefi",
+ TpmSupport="v2.0",
+ UefiData="QU1aTlVFRkk9xcN0AAAAAHj5a7fZ9+3aT2gcVRgA8Ek3NipiPST0pCiCIlTJtj20FzENCcQa"
+ )
+ ]
+ )
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_key.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_key.py
new file mode 100644
index 000000000..2660ced63
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_key.py
@@ -0,0 +1,654 @@
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from unittest.mock import MagicMock
+from unittest.mock import patch
+from unittest.mock import call, ANY
+
+import pytest
+import botocore
+import datetime
+from dateutil.tz import tzutc
+from ansible.module_utils._text import to_bytes
+
+from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
+
+from ansible_collections.amazon.aws.plugins.modules import ec2_key
+
+module_name = "ansible_collections.amazon.aws.plugins.modules.ec2_key"
+
+
+def raise_botocore_exception_clienterror(action):
+
+ params = {
+ 'Error': {
+ 'Code': 1,
+ 'Message': 'error creating key'
+ },
+ 'ResponseMetadata': {
+ 'RequestId': '01234567-89ab-cdef-0123-456789abcdef'
+ }
+ }
+
+ if action == 'create_key_pair':
+ params['Error']['Message'] = 'error creating key'
+
+ elif action == 'describe_key_pair':
+ params['Error']['Code'] = 'InvalidKeyPair.NotFound'
+ params['Error']['Message'] = 'The key pair does not exist'
+
+ elif action == 'import_key_pair':
+ params['Error']['Message'] = 'error importing key'
+
+ elif action == 'delete_key_pair':
+ params['Error']['Message'] = 'error deleting key'
+
+ return botocore.exceptions.ClientError(params, action)
+
+
+def test__import_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+ key_material = "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com"
+
+ expected_params = {
+ 'KeyName': name,
+ 'PublicKeyMaterial': to_bytes(key_material),
+ }
+
+ ec2_client.import_key_pair.return_value = {
+ 'KeyFingerprint': 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-012345678905a208d'
+ }
+
+ result = ec2_key._import_key_pair(ec2_client, name, key_material)
+
+ assert result == ec2_client.import_key_pair.return_value
+ assert ec2_client.import_key_pair.call_count == 1
+ ec2_client.import_key_pair.assert_called_with(aws_retry=True, **expected_params)
+
+
+def test_api_failure__import_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+ key_material = "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com"
+
+ expected_params = {
+ 'KeyName': name,
+ 'PublicKeyMaterial': to_bytes(key_material),
+ }
+
+ ec2_client.import_key_pair.side_effect = raise_botocore_exception_clienterror('import_key_pair')
+
+ with pytest.raises(ec2_key.Ec2KeyFailure):
+ ec2_key._import_key_pair(ec2_client, name, key_material)
+
+
+def test_extract_key_data_describe_key_pairs():
+
+ key = {
+ "CreateTime": datetime.datetime(2022, 9, 15, 20, 10, 15, tzinfo=tzutc()),
+ "KeyFingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "KeyName": "my_keypair",
+ "KeyPairId": "key-043046ef2a9a80b56",
+ "Tags": [],
+ }
+
+ key_type = "rsa"
+
+ expected_result = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ result = ec2_key.extract_key_data(key, key_type)
+
+ assert result == expected_result
+
+
+def test_extract_key_data_create_key_pair():
+
+ key = {
+ 'KeyFingerprint': '11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-043046ef2a9a80b56'
+ }
+
+ key_type = "rsa"
+
+ expected_result = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ result = ec2_key.extract_key_data(key, key_type)
+
+ assert result == expected_result
+
+
+@patch(module_name + '.delete_key_pair')
+@patch(module_name + '._import_key_pair')
+@patch(module_name + '.find_key_pair')
+def test_get_key_fingerprint(m_find_key_pair, m_import_key_pair, m_delete_key_pair):
+
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ m_find_key_pair.return_value = None
+
+ m_import_key_pair.return_value = {
+ 'KeyFingerprint': 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-043046ef2a9a80b56'
+ }
+
+ m_delete_key_pair.return_value = {
+ 'changed': True,
+ 'key': None,
+ 'msg': 'key deleted'
+ }
+
+ expected_result = 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62'
+
+ key_material = "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com"
+
+ result = ec2_key.get_key_fingerprint(module, ec2_client, key_material)
+
+ assert result == expected_result
+ assert m_find_key_pair.call_count == 1
+ assert m_import_key_pair.call_count == 1
+ assert m_delete_key_pair.call_count == 1
+
+
+def test_find_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+
+ ec2_client.describe_key_pairs.return_value = {
+ 'KeyPairs': [
+ {
+ 'CreateTime': datetime.datetime(2022, 9, 15, 20, 10, 15, tzinfo=tzutc()),
+ 'KeyFingerprint': '11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-043046ef2a9a80b56',
+ 'KeyType': 'rsa',
+ 'Tags': []
+ }
+ ],
+ }
+
+ ec2_key.find_key_pair(ec2_client, name)
+
+ assert ec2_client.describe_key_pairs.call_count == 1
+ ec2_client.describe_key_pairs.assert_called_with(aws_retry=True, KeyNames=[name])
+
+
+def test_api_failure_find_key_pair():
+ ec2_client = MagicMock()
+ name = 'non_existing_keypair'
+
+ ec2_client.describe_key_pairs.side_effect = botocore.exceptions.BotoCoreError
+
+ with pytest.raises(ec2_key.Ec2KeyFailure):
+ ec2_key.find_key_pair(ec2_client, name)
+
+
+def test_invalid_key_pair_find_key_pair():
+ ec2_client = MagicMock()
+ name = 'non_existing_keypair'
+
+ ec2_client.describe_key_pairs.side_effect = raise_botocore_exception_clienterror('describe_key_pair')
+
+ result = ec2_key.find_key_pair(ec2_client, name)
+
+ assert result is None
+
+
+def test__create_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+ tag_spec = None
+ key_type = None
+
+ expected_params = {'KeyName': name}
+
+ ec2_client.create_key_pair.return_value = {
+ "KeyFingerprint": "d7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62",
+ "KeyMaterial": (
+ "-----BEGIN RSA PRIVATE KEY-----\n" # gitleaks:allow
+ "MIIEXm7/Bi9wba2m0Qtclu\nCXQw2paSIZb\n"
+ "-----END RSA PRIVATE KEY-----"
+ ),
+ "KeyName": "my_keypair",
+ "KeyPairId": "key-012345678905a208d",
+ }
+
+ result = ec2_key._create_key_pair(ec2_client, name, tag_spec, key_type)
+
+ assert result == ec2_client.create_key_pair.return_value
+ assert ec2_client.create_key_pair.call_count == 1
+ ec2_client.create_key_pair.assert_called_with(aws_retry=True, **expected_params)
+
+
+def test_api_failure__create_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+ tag_spec = None
+ key_type = None
+
+ ec2_client.create_key_pair.side_effect = raise_botocore_exception_clienterror('create_key_pair')
+
+ with pytest.raises(ec2_key.Ec2KeyFailure):
+ ec2_key._create_key_pair(ec2_client, name, tag_spec, key_type)
+
+
+@patch(module_name + '.extract_key_data')
+@patch(module_name + '._import_key_pair')
+def test_create_new_key_pair_key_material(m_import_key_pair, m_extract_key_data):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key_material = "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com"
+ key_type = 'rsa'
+ tags = None
+
+ module.check_mode = False
+
+ m_import_key_pair.return_value = {
+ 'KeyFingerprint': 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-012345678905a208d'
+ }
+
+ m_extract_key_data.return_value = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ expected_result = {'changed': True, 'key': m_extract_key_data.return_value, 'msg': 'key pair created'}
+
+ result = ec2_key.create_new_key_pair(ec2_client, name, key_material, key_type, tags, module.check_mode)
+
+ assert result == expected_result
+ assert m_import_key_pair.call_count == 1
+ assert m_extract_key_data.call_count == 1
+
+
+@patch(module_name + '.extract_key_data')
+@patch(module_name + '._create_key_pair')
+def test_create_new_key_pair_no_key_material(m_create_key_pair, m_extract_key_data):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key_type = 'rsa'
+ key_material = None
+ tags = None
+
+ module.check_mode = False
+
+ m_create_key_pair.return_value = {
+ 'KeyFingerprint': 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-012345678905a208d'
+ }
+
+ m_extract_key_data.return_value = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ expected_result = {'changed': True, 'key': m_extract_key_data.return_value, 'msg': 'key pair created'}
+
+ result = ec2_key.create_new_key_pair(ec2_client, name, key_material, key_type, tags, module.check_mode)
+
+ assert result == expected_result
+ assert m_create_key_pair.call_count == 1
+ assert m_extract_key_data.call_count == 1
+
+
+def test__delete_key_pair():
+ ec2_client = MagicMock()
+
+ key_name = 'my_keypair'
+ ec2_key._delete_key_pair(ec2_client, key_name)
+
+ assert ec2_client.delete_key_pair.call_count == 1
+ ec2_client.delete_key_pair.assert_called_with(aws_retry=True, KeyName=key_name)
+
+
+def test_api_failure__delete_key_pair():
+ ec2_client = MagicMock()
+ name = 'my_keypair'
+
+ ec2_client.delete_key_pair.side_effect = raise_botocore_exception_clienterror('delete_key_pair')
+
+ with pytest.raises(ec2_key.Ec2KeyFailure):
+ ec2_key._delete_key_pair(ec2_client, name)
+
+
+@patch(module_name + '.extract_key_data')
+@patch(module_name + '._import_key_pair')
+@patch(module_name + '.delete_key_pair')
+@patch(module_name + '.get_key_fingerprint')
+def test_update_key_pair_by_key_material_update_needed(m_get_key_fingerprint, m_delete_key_pair, m__import_key_pair, m_extract_key_data):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key_material = "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com"
+ tag_spec = None
+ key = {
+ "KeyName": "my_keypair",
+ "KeyFingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "KeyPairId": "key-043046ef2a9a80b56",
+ "Tags": {},
+ }
+
+ module.check_mode = False
+
+ m_get_key_fingerprint.return_value = 'd7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62'
+ m_delete_key_pair.return_value = None
+ m__import_key_pair.return_value = {
+ 'KeyFingerprint': '11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-043046ef2a9a80b56',
+ 'Tags': {},
+ }
+ m_extract_key_data.return_value = {
+ "name": "my_keypair",
+ "fingerprint": "d7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62",
+ "id": "key-012345678905a208d",
+ "tags": {},
+ }
+
+ expected_result = {'changed': True, 'key': m_extract_key_data.return_value, 'msg': "key pair updated"}
+
+ result = ec2_key.update_key_pair_by_key_material(module.check_mode, ec2_client, name, key, key_material, tag_spec)
+
+ assert result == expected_result
+ assert m_get_key_fingerprint.call_count == 1
+ assert m_delete_key_pair.call_count == 1
+ assert m__import_key_pair.call_count == 1
+ assert m_extract_key_data.call_count == 1
+ m_get_key_fingerprint.assert_called_with(module.check_mode, ec2_client, key_material)
+ m_delete_key_pair.assert_called_with(module.check_mode, ec2_client, name, finish_task=False)
+ m__import_key_pair.assert_called_with(ec2_client, name, key_material, tag_spec)
+ m_extract_key_data.assert_called_with(key)
+
+
+@patch(module_name + ".extract_key_data")
+@patch(module_name + ".get_key_fingerprint")
+def test_update_key_pair_by_key_material_key_exists(m_get_key_fingerprint, m_extract_key_data):
+ ec2_client = MagicMock()
+
+ key_material = MagicMock()
+ key_fingerprint = MagicMock()
+ tag_spec = MagicMock()
+ key_id = MagicMock()
+ key_name = MagicMock()
+ key = {
+ "KeyName": key_name,
+ "KeyFingerprint": key_fingerprint,
+ "KeyPairId": key_id,
+ "Tags": {},
+ }
+
+ check_mode = False
+ m_get_key_fingerprint.return_value = key_fingerprint
+ m_extract_key_data.return_value = {
+ "name": key_name,
+ "fingerprint": key_fingerprint,
+ "id": key_id,
+ "tags": {},
+ }
+
+ expected_result = {"changed": False, "key": m_extract_key_data.return_value, "msg": "key pair already exists"}
+
+ assert expected_result == ec2_key.update_key_pair_by_key_material(
+ check_mode, ec2_client, key_name, key, key_material, tag_spec
+ )
+
+ m_get_key_fingerprint.assert_called_once_with(check_mode, ec2_client, key_material)
+ m_extract_key_data.assert_called_once_with(key)
+
+
+@patch(module_name + ".extract_key_data")
+@patch(module_name + "._create_key_pair")
+@patch(module_name + ".delete_key_pair")
+def test_update_key_pair_by_key_type_update_needed(m_delete_key_pair, m__create_key_pair, m_extract_key_data):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key_type = 'rsa'
+ tag_spec = None
+
+ module.check_mode = False
+
+ m_delete_key_pair.return_value = None
+ m__create_key_pair.return_value = {
+ 'KeyFingerprint': '11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa',
+ 'Name': 'my_keypair',
+ 'Id': 'key-043046ef2a9a80b56',
+ 'Tags': {},
+ 'Type': 'rsa'
+ }
+ m_extract_key_data.return_value = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ expected_result = {"changed": True, "key": m_extract_key_data.return_value, "msg": "key pair updated"}
+
+ result = ec2_key.update_key_pair_by_key_type(module.check_mode, ec2_client, name, key_type, tag_spec)
+
+ assert result == expected_result
+ assert m_delete_key_pair.call_count == 1
+ assert m__create_key_pair.call_count == 1
+ assert m_extract_key_data.call_count == 1
+ m_delete_key_pair.assert_called_with(module.check_mode, ec2_client, name, finish_task=False)
+ m__create_key_pair.assert_called_with(ec2_client, name, tag_spec, key_type)
+ m_extract_key_data.assert_called_with(m__create_key_pair.return_value, key_type)
+
+
+@patch(module_name + '.update_key_pair_by_key_material')
+def test_handle_existing_key_pair_update_key_matrial_with_force(m_update_key_pair_by_key_material):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key = {
+ "KeyName": "my_keypair",
+ "KeyFingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "KeyPairId": "key-043046ef2a9a80b56",
+ "Tags": {},
+ "KeyType": "rsa"
+ }
+
+ module.params = {
+ 'key_material': "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com",
+ 'force': True,
+ 'key_type': 'rsa',
+ 'tags': None,
+ 'purge_tags': True,
+ 'tag_spec': None
+ }
+
+ key_data = {
+ "name": "my_keypair",
+ "fingerprint": "d7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62",
+ "id": "key-012345678905a208d",
+ "tags": {},
+ }
+
+ m_update_key_pair_by_key_material.return_value = {'changed': True, 'key': key_data, 'msg': "key pair updated"}
+
+ expected_result = {'changed': True, 'key': key_data, 'msg': "key pair updated"}
+
+ result = ec2_key.handle_existing_key_pair_update(module, ec2_client, name, key)
+
+ assert result == expected_result
+ assert m_update_key_pair_by_key_material.call_count == 1
+
+
+@patch(module_name + '.update_key_pair_by_key_type')
+def test_handle_existing_key_pair_update_key_type(m_update_key_pair_by_key_type):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key = {
+ "KeyName": "my_keypair",
+ "KeyFingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "KeyPairId": "key-043046ef2a9a80b56",
+ "Tags": {},
+ "KeyType": "ed25519"
+ }
+
+ module.params = {
+ 'key_material': "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com",
+ 'force': False,
+ 'key_type': 'rsa',
+ 'tags': None,
+ 'purge_tags': True,
+ 'tag_spec': None
+ }
+
+ key_data = {
+ "name": "my_keypair",
+ "fingerprint": "d7:ff:a6:63:18:64:9c:57:a1:ee:ca:a4:ad:c2:81:62",
+ "id": "key-012345678905a208d",
+ "tags": {},
+ }
+
+ m_update_key_pair_by_key_type.return_value = {'changed': True, 'key': key_data, 'msg': "key pair updated"}
+
+ expected_result = {'changed': True, 'key': key_data, 'msg': "key pair updated"}
+
+ result = ec2_key.handle_existing_key_pair_update(module, ec2_client, name, key)
+
+ assert result == expected_result
+ assert m_update_key_pair_by_key_type.call_count == 1
+
+
+@patch(module_name + '.extract_key_data')
+def test_handle_existing_key_pair_else(m_extract_key_data):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+ key = {
+ "KeyName": "my_keypair",
+ "KeyFingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "KeyPairId": "key-043046ef2a9a80b56",
+ "Tags": {},
+ "KeyType": "rsa"
+ }
+
+ module.params = {
+ 'key_material': "ssh-rsa AAAAB3NzaC1yc2EAA email@example.com",
+ 'force': False,
+ 'key_type': 'rsa',
+ 'tags': None,
+ 'purge_tags': True,
+ 'tag_spec': None
+ }
+
+ m_extract_key_data.return_value = {
+ "name": "my_keypair",
+ "fingerprint": "11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa",
+ "id": "key-043046ef2a9a80b56",
+ "tags": {},
+ "type": "rsa"
+ }
+
+ expected_result = {"changed": False, "key": m_extract_key_data.return_value, "msg": "key pair already exists"}
+
+ result = ec2_key.handle_existing_key_pair_update(module, ec2_client, name, key)
+
+ assert result == expected_result
+ assert m_extract_key_data.call_count == 1
+
+
+@patch(module_name + '._delete_key_pair')
+@patch(module_name + '.find_key_pair')
+def test_delete_key_pair_key_exists(m_find_key_pair, m_delete_key_pair):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+
+ module.check_mode = False
+
+ m_find_key_pair.return_value = {
+ 'KeyPairs': [
+ {
+ 'CreateTime': datetime.datetime(2022, 9, 15, 20, 10, 15, tzinfo=tzutc()),
+ 'KeyFingerprint': '11:12:13:14:bb:26:85:b2:e8:39:27:bc:ee:aa:ff:ee:dd:cc:bb:aa',
+ 'KeyName': 'my_keypair',
+ 'KeyPairId': 'key-043046ef2a9a80b56',
+ 'KeyType': 'rsa',
+ 'Tags': []
+ }
+ ],
+ }
+
+ expected_result = {'changed': True, 'key': None, 'msg': 'key deleted'}
+
+ result = ec2_key.delete_key_pair(module.check_mode, ec2_client, name)
+
+ assert m_find_key_pair.call_count == 1
+ m_find_key_pair.assert_called_with(ec2_client, name)
+ assert m_delete_key_pair.call_count == 1
+ m_delete_key_pair.assert_called_with(ec2_client, name)
+ assert result == expected_result
+
+
+@patch(module_name + '._delete_key_pair')
+@patch(module_name + '.find_key_pair')
+def test_delete_key_pair_key_not_exist(m_find_key_pair, m_delete_key_pair):
+ module = MagicMock()
+ ec2_client = MagicMock()
+
+ name = 'my_keypair'
+
+ module.check_mode = False
+
+ m_find_key_pair.return_value = None
+
+ expected_result = {'key': None, 'msg': 'key did not exist'}
+
+ result = ec2_key.delete_key_pair(module.check_mode, ec2_client, name)
+
+ assert m_find_key_pair.call_count == 1
+ m_find_key_pair.assert_called_with(ec2_client, name)
+ assert m_delete_key_pair.call_count == 0
+ assert result == expected_result
+
+
+@patch(module_name + ".AnsibleAWSModule")
+def test_main_success(m_AnsibleAWSModule):
+ m_module = MagicMock()
+ m_AnsibleAWSModule.return_value = m_module
+
+ ec2_key.main()
+
+ m_module.client.assert_called_with("ec2", retry_decorator=ANY)
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_security_group.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_security_group.py
new file mode 100644
index 000000000..1ebbe86c6
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_security_group.py
@@ -0,0 +1,83 @@
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible_collections.amazon.aws.plugins.modules import ec2_security_group as group_module
+
+
+def test_from_permission():
+ internal_http = {
+ 'FromPort': 80,
+ 'IpProtocol': 'tcp',
+ 'IpRanges': [
+ {
+ 'CidrIp': '10.0.0.0/8',
+ 'Description': 'Foo Bar Baz'
+ },
+ ],
+ 'Ipv6Ranges': [
+ {'CidrIpv6': 'fe80::94cc:8aff:fef6:9cc/64'},
+ ],
+ 'PrefixListIds': [],
+ 'ToPort': 80,
+ 'UserIdGroupPairs': [],
+ }
+ perms = list(group_module.rule_from_group_permission(internal_http))
+ assert len(perms) == 2
+ assert perms[0].target == '10.0.0.0/8'
+ assert perms[0].target_type == 'ipv4'
+ assert perms[0].description == 'Foo Bar Baz'
+ assert perms[1].target == 'fe80::94cc:8aff:fef6:9cc/64'
+
+ global_egress = {
+ 'IpProtocol': '-1',
+ 'IpRanges': [{'CidrIp': '0.0.0.0/0'}],
+ 'Ipv6Ranges': [],
+ 'PrefixListIds': [],
+ 'UserIdGroupPairs': []
+ }
+ perms = list(group_module.rule_from_group_permission(global_egress))
+ assert len(perms) == 1
+ assert perms[0].target == '0.0.0.0/0'
+ assert perms[0].port_range == (None, None)
+
+ internal_prefix_http = {
+ 'FromPort': 80,
+ 'IpProtocol': 'tcp',
+ 'PrefixListIds': [
+ {'PrefixListId': 'p-1234'}
+ ],
+ 'ToPort': 80,
+ 'UserIdGroupPairs': [],
+ }
+ perms = list(group_module.rule_from_group_permission(internal_prefix_http))
+ assert len(perms) == 1
+ assert perms[0].target == 'p-1234'
+
+
+def test_rule_to_permission():
+ tests = [
+ group_module.Rule((22, 22), 'udp', 'sg-1234567890', 'group', None),
+ group_module.Rule((1, 65535), 'tcp', '0.0.0.0/0', 'ipv4', "All TCP from everywhere"),
+ group_module.Rule((443, 443), 'tcp', 'ip-123456', 'ip_prefix', "Traffic to privatelink IPs"),
+ group_module.Rule((443, 443), 'tcp', 'feed:dead:::beef/64', 'ipv6', None),
+ ]
+ for test in tests:
+ perm = group_module.to_permission(test)
+ assert perm['FromPort'], perm['ToPort'] == test.port_range
+ assert perm['IpProtocol'] == test.protocol
+
+
+def test_validate_ip():
+ class Warner(object):
+ def warn(self, msg):
+ return
+ ips = [
+ ('10.1.1.1/24', '10.1.1.0/24'),
+ ('192.168.56.101/16', '192.168.0.0/16'),
+ # Don't modify IPv6 CIDRs, AWS supports /128 and device ranges
+ ('fc00:8fe0:fe80:b897:8990:8a7c:99bf:323d/128', 'fc00:8fe0:fe80:b897:8990:8a7c:99bf:323d/128'),
+ ]
+
+ for ip, net in ips:
+ assert group_module.validate_ip(Warner(), ip) == net
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_vpc_dhcp_option.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_vpc_dhcp_option.py
new file mode 100644
index 000000000..73726590f
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_vpc_dhcp_option.py
@@ -0,0 +1,71 @@
+# (c) 2021 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+# Magic... Incorrectly identified by pylint as unused
+from ansible_collections.amazon.aws.tests.unit.utils.amazon_placebo_fixtures import placeboify # pylint: disable=unused-import
+from ansible_collections.amazon.aws.tests.unit.compat.mock import patch
+
+from ansible_collections.amazon.aws.plugins.modules import ec2_vpc_dhcp_option as dhcp_module
+from ansible_collections.amazon.aws.tests.unit.plugins.modules.utils import ModuleTestCase
+
+test_module_params = {'domain_name': 'us-west-2.compute.internal',
+ 'dns_servers': ['AmazonProvidedDNS'],
+ 'ntp_servers': ['10.10.2.3', '10.10.4.5'],
+ 'netbios_name_servers': ['10.20.2.3', '10.20.4.5'],
+ 'netbios_node_type': 2}
+
+test_create_config = [{'Key': 'domain-name', 'Values': [{'Value': 'us-west-2.compute.internal'}]},
+ {'Key': 'domain-name-servers', 'Values': [{'Value': 'AmazonProvidedDNS'}]},
+ {'Key': 'ntp-servers', 'Values': [{'Value': '10.10.2.3'}, {'Value': '10.10.4.5'}]},
+ {'Key': 'netbios-name-servers', 'Values': [{'Value': '10.20.2.3'}, {'Value': '10.20.4.5'}]},
+ {'Key': 'netbios-node-type', 'Values': 2}]
+
+
+test_create_option_set = [{'Key': 'domain-name', 'Values': ['us-west-2.compute.internal']},
+ {'Key': 'domain-name-servers', 'Values': ['AmazonProvidedDNS']},
+ {'Key': 'ntp-servers', 'Values': ['10.10.2.3', '10.10.4.5']},
+ {'Key': 'netbios-name-servers', 'Values': ['10.20.2.3', '10.20.4.5']},
+ {'Key': 'netbios-node-type', 'Values': ['2']}]
+
+test_normalize_config = {'domain-name': ['us-west-2.compute.internal'],
+ 'domain-name-servers': ['AmazonProvidedDNS'],
+ 'ntp-servers': ['10.10.2.3', '10.10.4.5'],
+ 'netbios-name-servers': ['10.20.2.3', '10.20.4.5'],
+ 'netbios-node-type': '2'
+ }
+
+
+class FakeModule(object):
+ def __init__(self, **kwargs):
+ self.params = kwargs
+
+ def fail_json(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('FAIL')
+
+ def fail_json_aws(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('FAIL')
+
+ def exit_json(self, *args, **kwargs):
+ self.exit_args = args
+ self.exit_kwargs = kwargs
+ raise Exception('EXIT')
+
+
+@patch.object(dhcp_module.AnsibleAWSModule, 'client')
+class TestDhcpModule(ModuleTestCase):
+
+ def test_normalize_config(self, client_mock):
+ result = dhcp_module.normalize_ec2_vpc_dhcp_config(test_create_config)
+
+ print(result)
+ print(test_normalize_config)
+ assert result == test_normalize_config
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_kms_key.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_kms_key.py
new file mode 100644
index 000000000..5a53e2ddb
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_kms_key.py
@@ -0,0 +1,82 @@
+#
+# (c) 2022 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+import pytest
+
+from unittest.mock import MagicMock, call, patch
+from ansible_collections.amazon.aws.plugins.modules import kms_key
+
+
+module_name = "ansible_collections.amazon.aws.plugins.modules.kms_key"
+key_details = {
+ "KeyMetadata": {
+ "aliases": ["mykey"],
+ "Arn": "arn:aws:kms:us-east-1:12345678:key/mrk-12345678",
+ "customer_master_key_spec": "SYMMETRIC_DEFAULT",
+ "description": "",
+ "enable_key_rotation": False,
+ "enabled": True,
+ "encryption_algorithms": ["SYMMETRIC_DEFAULT"],
+ "grants": [],
+ "key_arn": "arn:aws:kms:us-east-1:12345678:key/mrk-12345678",
+ "key_id": "mrk-12345678",
+ "key_manager": "CUSTOMER",
+ "key_policies": [
+ {
+ "Id": "key-default-1",
+ "Statement": [
+ {
+ "Action": "kms:*",
+ "Effect": "Allow",
+ "Principal": {"AWS": "arn:aws:iam::12345678:root"},
+ "Resource": "*",
+ "Sid": "Enable IAM User Permissions",
+ }
+ ],
+ "Version": "2012-10-17",
+ }
+ ],
+ "key_spec": "SYMMETRIC_DEFAULT",
+ "key_state": "Enabled",
+ "key_usage": "ENCRYPT_DECRYPT",
+ "multi_region": True,
+ "multi_region_configuration": {
+ "multi_region_key_type": "PRIM ARY",
+ "primary_key": {
+ "arn": "arn:aws:kms:us-east-1:12345678:key/mrk-12345678",
+ "region": "us-east-1",
+ },
+ "replica_keys": [],
+ },
+ "origin": "AWS_KMS",
+ "tags": {"Hello": "World2"},
+ }
+}
+
+
+@patch(module_name + ".get_kms_metadata_with_backoff")
+def test_fetch_key_metadata(m_get_kms_metadata_with_backoff):
+
+ module = MagicMock()
+ kms_client = MagicMock()
+
+ m_get_kms_metadata_with_backoff.return_value = key_details
+ kms_key.fetch_key_metadata(kms_client, module, "mrk-12345678", "mykey")
+ assert m_get_kms_metadata_with_backoff.call_count == 1
+
+
+def test_validate_params():
+
+ module = MagicMock()
+ module.params = {
+ "state": "present",
+ "multi_region": True
+ }
+
+ result = kms_key.validate_params(module, key_details["KeyMetadata"])
+ module.fail_json.assert_called_with(
+ msg="You cannot change the multi-region property on an existing key."
+ )
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer.py
new file mode 100644
index 000000000..451a61766
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer.py
@@ -0,0 +1,493 @@
+#
+# (c) 2022 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from unittest.mock import MagicMock, call, patch
+from ansible_collections.amazon.aws.plugins.modules import lambda_layer
+
+
+def raise_lambdalayer_exception(e=None, m=None):
+ e = e or "lambda layer exc"
+ m = m or "unit testing"
+ return lambda_layer.LambdaLayerFailure(exc=e, msg=m)
+
+
+mod_list_layer = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer.list_layer_versions'
+mod_create_layer = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer.create_layer_version'
+mod_delete_layer = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer.delete_layer_version'
+
+
+@pytest.mark.parametrize(
+ "params,api_result,calls,ansible_result",
+ [
+ (
+ {
+ "name": "testlayer",
+ "version": 4
+ },
+ [],
+ [],
+ {"changed": False, "layer_versions": []}
+ ),
+ (
+ {
+ "name": "testlayer",
+ "version": 4
+ },
+ [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ],
+ [],
+ {"changed": False, "layer_versions": []}
+ ),
+ (
+ {
+ "name": "testlayer",
+ "version": 2
+ },
+ [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ],
+ [
+ call(LayerName='testlayer', VersionNumber=2)
+ ],
+ {
+ "changed": True,
+ "layer_versions": [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ }
+ ]
+ }
+ ),
+ (
+ {
+ "name": "testlayer",
+ "version": -1
+ },
+ [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ],
+ [
+ call(LayerName='testlayer', VersionNumber=2),
+ call(LayerName='testlayer', VersionNumber=1)
+ ],
+ {
+ "changed": True,
+ "layer_versions": [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ]
+ }
+ )
+ ]
+)
+@patch(mod_list_layer)
+def test_delete_layer(m_list_layer, params, api_result, calls, ansible_result):
+
+ lambda_client = MagicMock()
+ lambda_client.delete_layer_version.return_value = None
+
+ m_list_layer.return_value = api_result
+ result = lambda_layer.delete_layer_version(lambda_client, params)
+ assert result == ansible_result
+
+ m_list_layer.assert_called_once_with(
+ lambda_client, params.get("name")
+ )
+
+ if not calls:
+ lambda_client.delete_layer_version.assert_not_called()
+ else:
+ lambda_client.delete_layer_version.assert_has_calls(calls, any_order=True)
+
+
+@patch(mod_list_layer)
+def test_delete_layer_check_mode(m_list_layer):
+
+ lambda_client = MagicMock()
+ lambda_client.delete_layer_version.return_value = None
+
+ m_list_layer.return_value = [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ]
+ params = {"name": "testlayer", "version": -1}
+ result = lambda_layer.delete_layer_version(lambda_client, params, check_mode=True)
+ ansible_result = {
+ "changed": True,
+ "layer_versions": [
+ {
+ 'compatible_runtimes': ["python3.7"],
+ 'created_date': "2022-09-29T10:31:35.977+0000",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:2",
+ "license_info": "MIT",
+ 'version': 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ]
+ }
+ assert result == ansible_result
+
+ m_list_layer.assert_called_once_with(
+ lambda_client, params.get("name")
+ )
+ lambda_client.delete_layer_version.assert_not_called()
+
+
+@patch(mod_list_layer)
+def test_delete_layer_failure(m_list_layer):
+
+ lambda_client = MagicMock()
+ lambda_client.delete_layer_version.side_effect = raise_lambdalayer_exception()
+
+ m_list_layer.return_value = [
+ {
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:testlayer:1",
+ "version": 1
+ }
+ ]
+ params = {"name": "testlayer", "version": 1}
+ with pytest.raises(lambda_layer.LambdaLayerFailure):
+ lambda_layer.delete_layer_version(lambda_client, params)
+
+
+@pytest.mark.parametrize(
+ "b_s3content",
+ [
+ (True),
+ (False)
+ ]
+)
+@patch(mod_list_layer)
+def test_create_layer(m_list_layer, b_s3content, tmp_path):
+ params = {
+ "name": "testlayer",
+ "description": "ansible units testing sample layer",
+ "content": {},
+ "license_info": "MIT"
+ }
+
+ lambda_client = MagicMock()
+
+ lambda_client.publish_layer_version.return_value = {
+ 'CompatibleRuntimes': [
+ 'python3.6',
+ 'python3.7',
+ ],
+ 'Content': {
+ 'CodeSha256': 'tv9jJO+rPbXUUXuRKi7CwHzKtLDkDRJLB3cC3Z/ouXo=',
+ 'CodeSize': 169,
+ 'Location': 'https://awslambda-us-west-2-layers.s3.us-west-2.amazonaws.com/snapshots/123456789012/my-layer-4aaa2fbb',
+ },
+ 'CreatedDate': '2018-11-14T23:03:52.894+0000',
+ 'Description': "ansible units testing sample layer",
+ 'LayerArn': 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer',
+ 'LayerVersionArn': 'arn:aws:lambda:us-west-2:123456789012:layer:testlayer:1',
+ 'LicenseInfo': 'MIT',
+ 'Version': 1,
+ 'ResponseMetadata': {
+ 'http_header': 'true',
+ },
+ }
+
+ expected = {
+ "changed": True,
+ "layer_versions": [
+ {
+ 'compatible_runtimes': ['python3.6', 'python3.7'],
+ 'content': {
+ 'code_sha256': 'tv9jJO+rPbXUUXuRKi7CwHzKtLDkDRJLB3cC3Z/ouXo=',
+ 'code_size': 169,
+ 'location': 'https://awslambda-us-west-2-layers.s3.us-west-2.amazonaws.com/snapshots/123456789012/my-layer-4aaa2fbb'
+ },
+ 'created_date': '2018-11-14T23:03:52.894+0000',
+ 'description': 'ansible units testing sample layer',
+ 'layer_arn': 'arn:aws:lambda:us-west-2:123456789012:layer:my-layer',
+ 'layer_version_arn': 'arn:aws:lambda:us-west-2:123456789012:layer:testlayer:1',
+ 'license_info': 'MIT',
+ 'version': 1
+ }
+ ]
+ }
+
+ if b_s3content:
+ params["content"] = {
+ "s3_bucket": "mybucket",
+ "s3_key": "mybucket-key",
+ "s3_object_version": "v1"
+ }
+ content_arg = {
+ "S3Bucket": "mybucket",
+ "S3Key": "mybucket-key",
+ "S3ObjectVersion": "v1"
+ }
+ else:
+ binary_data = b"simple lambda layer content"
+ test_dir = tmp_path / "lambda_layer"
+ test_dir.mkdir()
+ zipfile = test_dir / "lambda.zip"
+ zipfile.write_bytes(binary_data)
+
+ params["content"] = {"zip_file": str(zipfile)}
+ content_arg = {
+ "ZipFile": binary_data,
+ }
+
+ result = lambda_layer.create_layer_version(lambda_client, params)
+
+ assert result == expected
+
+ lambda_client.publish_layer_version.assert_called_with(
+ LayerName="testlayer",
+ Description="ansible units testing sample layer",
+ LicenseInfo="MIT",
+ Content=content_arg,
+ )
+
+ m_list_layer.assert_not_called()
+
+
+@patch(mod_list_layer)
+def test_create_layer_check_mode(m_list_layer):
+ params = {
+ "name": "testlayer",
+ "description": "ansible units testing sample layer",
+ "content": {
+ "s3_bucket": "mybucket",
+ "s3_key": "mybucket-key",
+ "s3_object_version": "v1"
+ },
+ "license_info": "MIT"
+ }
+
+ lambda_client = MagicMock()
+
+ result = lambda_layer.create_layer_version(lambda_client, params, check_mode=True)
+ assert result == {"msg": "Create operation skipped - running in check mode", "changed": True}
+
+ m_list_layer.assert_not_called()
+ lambda_client.publish_layer_version.assert_not_called()
+
+
+def test_create_layer_failure():
+ params = {
+ "name": "testlayer",
+ "description": "ansible units testing sample layer",
+ "content": {
+ "s3_bucket": "mybucket",
+ "s3_key": "mybucket-key",
+ "s3_object_version": "v1"
+ },
+ "compatible_runtimes": [
+ "nodejs",
+ "python3.9"
+ ],
+ "compatible_architectures": [
+ 'x86_64',
+ 'arm64'
+ ]
+ }
+ lambda_client = MagicMock()
+ lambda_client.publish_layer_version.side_effect = raise_lambdalayer_exception()
+
+ with pytest.raises(lambda_layer.LambdaLayerFailure):
+ lambda_layer.create_layer_version(lambda_client, params)
+
+
+def test_create_layer_using_unexisting_file():
+ params = {
+ "name": "testlayer",
+ "description": "ansible units testing sample layer",
+ "content": {
+ "zip_file": "this_file_does_not_exist",
+ },
+ "compatible_runtimes": [
+ "nodejs",
+ "python3.9"
+ ],
+ "compatible_architectures": [
+ 'x86_64',
+ 'arm64'
+ ]
+ }
+
+ lambda_client = MagicMock()
+
+ lambda_client.publish_layer_version.return_value = {}
+ with pytest.raises(FileNotFoundError):
+ lambda_layer.create_layer_version(lambda_client, params)
+
+ lambda_client.publish_layer_version.assert_not_called()
+
+
+@pytest.mark.parametrize(
+ "params,failure",
+ [
+ (
+ {"name": "test-layer"},
+ False
+ ),
+ (
+ {"name": "test-layer", "state": "absent"},
+ False
+ ),
+ (
+ {"name": "test-layer"},
+ True
+ ),
+ (
+ {"name": "test-layer", "state": "absent"},
+ True
+ ),
+ ]
+)
+@patch(mod_create_layer)
+@patch(mod_delete_layer)
+def test_execute_module(m_delete_layer, m_create_layer, params, failure):
+
+ module = MagicMock()
+ module.params = params
+ module.check_mode = False
+ module.exit_json.side_effect = SystemExit(1)
+ module.fail_json_aws.side_effect = SystemExit(2)
+
+ lambda_client = MagicMock()
+
+ state = params.get("state", "present")
+ result = {"changed": True, "layers_versions": {}}
+
+ if not failure:
+ if state == "present":
+ m_create_layer.return_value = result
+ with pytest.raises(SystemExit):
+ lambda_layer.execute_module(module, lambda_client)
+
+ module.exit_json.assert_called_with(**result)
+ module.fail_json_aws.assert_not_called()
+ m_create_layer.assert_called_with(
+ lambda_client, params, module.check_mode
+ )
+ m_delete_layer.assert_not_called()
+
+ elif state == "absent":
+ m_delete_layer.return_value = result
+ with pytest.raises(SystemExit):
+ lambda_layer.execute_module(module, lambda_client)
+
+ module.exit_json.assert_called_with(**result)
+ module.fail_json_aws.assert_not_called()
+ m_delete_layer.assert_called_with(
+ lambda_client, params, module.check_mode
+ )
+ m_create_layer.assert_not_called()
+ else:
+ exc = "lambdalayer_execute_module_exception"
+ msg = "this_exception_is_used_for_unit_testing"
+ m_create_layer.side_effect = raise_lambdalayer_exception(exc, msg)
+ m_delete_layer.side_effect = raise_lambdalayer_exception(exc, msg)
+
+ with pytest.raises(SystemExit):
+ lambda_layer.execute_module(module, lambda_client)
+
+ module.exit_json.assert_not_called()
+ module.fail_json_aws.assert_called_with(
+ exc, msg=msg
+ )
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer_info.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer_info.py
new file mode 100644
index 000000000..25a1f15ac
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_lambda_layer_info.py
@@ -0,0 +1,358 @@
+#
+# (c) 2022 Red Hat Inc.
+#
+# This file is part of Ansible
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+from botocore.exceptions import BotoCoreError
+
+from unittest.mock import MagicMock, call, patch
+from ansible_collections.amazon.aws.plugins.modules import lambda_layer_info
+
+
+mod__list_layer_versions = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer_info._list_layer_versions'
+mod__list_layers = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer_info._list_layers'
+mod_list_layer_versions = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer_info.list_layer_versions'
+mod_list_layers = 'ansible_collections.amazon.aws.plugins.modules.lambda_layer_info.list_layers'
+
+
+list_layers_paginate_result = {
+ 'NextMarker': '002',
+ 'Layers': [
+ {
+ 'LayerName': "test-layer-01",
+ 'LayerArn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-01",
+ 'LatestMatchingVersion': {
+ 'LayerVersionArn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-01:1",
+ 'Version': 1,
+ 'Description': "lambda layer created for unit tests",
+ 'CreatedDate': "2022-09-29T10:31:26.341+0000",
+ 'CompatibleRuntimes': [
+ 'nodejs',
+ 'nodejs4.3',
+ 'nodejs6.10'
+ ],
+ 'LicenseInfo': 'MIT',
+ 'CompatibleArchitectures': [
+ 'arm64'
+ ]
+ }
+ },
+ {
+ 'LayerName': "test-layer-02",
+ 'LayerArn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-02",
+ 'LatestMatchingVersion': {
+ 'LayerVersionArn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-02:1",
+ 'Version': 1,
+ 'CreatedDate': "2022-09-29T10:31:26.341+0000",
+ 'CompatibleArchitectures': [
+ 'arm64'
+ ]
+ }
+ },
+ ],
+ 'ResponseMetadata': {
+ 'http': 'true',
+ },
+}
+
+list_layers_result = [
+ {
+ 'layer_name': "test-layer-01",
+ 'layer_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-01",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-01:1",
+ 'version': 1,
+ 'description': "lambda layer created for unit tests",
+ 'created_date': "2022-09-29T10:31:26.341+0000",
+ 'compatible_runtimes': [
+ 'nodejs',
+ 'nodejs4.3',
+ 'nodejs6.10'
+ ],
+ 'license_info': 'MIT',
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ 'layer_name': "test-layer-02",
+ 'layer_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-02",
+ 'layer_version_arn': "arn:aws:lambda:eu-west-2:123456789012:layer:test-layer-02:1",
+ 'version': 1,
+ 'created_date': "2022-09-29T10:31:26.341+0000",
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ }
+]
+
+
+list_layers_versions_paginate_result = {
+ 'LayerVersions': [
+ {
+ 'CompatibleRuntimes': ["python3.7"],
+ 'CreatedDate': "2022-09-29T10:31:35.977+0000",
+ 'LayerVersionArn': "arn:aws:lambda:eu-west-2:123456789012:layer:layer-01:2",
+ "LicenseInfo": "MIT",
+ 'Version': 2,
+ 'CompatibleArchitectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "CompatibleRuntimes": ["python3.7"],
+ "CreatedDate": "2022-09-29T10:31:26.341+0000",
+ "Description": "lambda layer first version",
+ "LayerVersionArn": "arn:aws:lambda:eu-west-2:123456789012:layer:layer-01:1",
+ "LicenseInfo": "GPL-3.0-only",
+ "Version": 1
+ }
+ ],
+ 'ResponseMetadata': {
+ 'http': 'true',
+ },
+ 'NextMarker': '001',
+}
+
+
+list_layers_versions_result = [
+ {
+ "compatible_runtimes": ["python3.7"],
+ "created_date": "2022-09-29T10:31:35.977+0000",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:layer-01:2",
+ "license_info": "MIT",
+ "version": 2,
+ 'compatible_architectures': [
+ 'arm64'
+ ]
+ },
+ {
+ "compatible_runtimes": ["python3.7"],
+ "created_date": "2022-09-29T10:31:26.341+0000",
+ "description": "lambda layer first version",
+ "layer_version_arn": "arn:aws:lambda:eu-west-2:123456789012:layer:layer-01:1",
+ "license_info": "GPL-3.0-only",
+ "version": 1
+ }
+]
+
+
+@pytest.mark.parametrize(
+ "params,call_args",
+ [
+ (
+ {
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ },
+ {
+ "CompatibleRuntime": "nodejs",
+ "CompatibleArchitecture": "arm64"
+ }
+ ),
+ (
+ {
+ "compatible_runtime": "nodejs",
+ },
+ {
+ "CompatibleRuntime": "nodejs",
+ }
+ ),
+ (
+ {
+ "compatible_architecture": "arm64"
+ },
+ {
+ "CompatibleArchitecture": "arm64"
+ }
+ ),
+ (
+ {}, {}
+ )
+ ]
+)
+@patch(mod__list_layers)
+def test_list_layers_with_latest_version(m__list_layers, params, call_args):
+
+ lambda_client = MagicMock()
+
+ m__list_layers.return_value = list_layers_paginate_result
+ layers = lambda_layer_info.list_layers(lambda_client, **params)
+
+ m__list_layers.assert_has_calls(
+ [
+ call(lambda_client, **call_args)
+ ]
+ )
+ assert layers == list_layers_result
+
+
+@pytest.mark.parametrize(
+ "params,call_args",
+ [
+ (
+ {
+ "name": "layer-01",
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ },
+ {
+ "LayerName": "layer-01",
+ "CompatibleRuntime": "nodejs",
+ "CompatibleArchitecture": "arm64"
+ }
+ ),
+ (
+ {
+ "name": "layer-01",
+ "compatible_runtime": "nodejs",
+ },
+ {
+ "LayerName": "layer-01",
+ "CompatibleRuntime": "nodejs",
+ }
+ ),
+ (
+ {
+ "name": "layer-01",
+ "compatible_architecture": "arm64"
+ },
+ {
+ "LayerName": "layer-01",
+ "CompatibleArchitecture": "arm64"
+ }
+ ),
+ (
+ {"name": "layer-01"}, {"LayerName": "layer-01"}
+ )
+ ]
+)
+@patch(mod__list_layer_versions)
+def test_list_layer_versions(m__list_layer_versions, params, call_args):
+
+ lambda_client = MagicMock()
+
+ m__list_layer_versions.return_value = list_layers_versions_paginate_result
+ layers = lambda_layer_info.list_layer_versions(lambda_client, **params)
+
+ m__list_layer_versions.assert_has_calls(
+ [
+ call(lambda_client, **call_args)
+ ]
+ )
+ assert layers == list_layers_versions_result
+
+
+def raise_botocore_exception():
+ return BotoCoreError(error="failed", operation="list_layers")
+
+
+@pytest.mark.parametrize(
+ "params",
+ [
+ (
+ {
+ "name": "test-layer",
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ }
+ ),
+ (
+ {
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ }
+ )
+ ]
+)
+@patch(mod__list_layers)
+@patch(mod__list_layer_versions)
+def test_list_layers_with_failure(m__list_layer_versions, m__list_layers, params):
+
+ lambda_client = MagicMock()
+
+ if "name" in params:
+ m__list_layer_versions.side_effect = raise_botocore_exception()
+ test_function = lambda_layer_info.list_layer_versions
+ else:
+ m__list_layers.side_effect = raise_botocore_exception()
+ test_function = lambda_layer_info.list_layers
+
+ with pytest.raises(lambda_layer_info.LambdaLayerInfoFailure):
+ test_function(lambda_client, **params)
+
+
+def raise_layer_info_exception(exc, msg):
+ return lambda_layer_info.LambdaLayerInfoFailure(exc=exc, msg=msg)
+
+
+@pytest.mark.parametrize(
+ "params,failure",
+ [
+ (
+ {
+ "name": "test-layer",
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ },
+ False
+ ),
+ (
+ {
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ },
+ False
+ ),
+ (
+ {
+ "name": "test-layer",
+ "compatible_runtime": "nodejs",
+ "compatible_architecture": "arm64"
+ },
+ True
+ )
+ ]
+)
+@patch(mod_list_layers)
+@patch(mod_list_layer_versions)
+def test_execute_module(m_list_layer_versions, m_list_layers, params, failure):
+
+ lambda_client = MagicMock()
+
+ module = MagicMock()
+ module.params = params
+ module.exit_json.side_effect = SystemExit(1)
+ module.fail_json_aws.side_effect = SystemExit(2)
+
+ method_called, method_not_called = m_list_layers, m_list_layer_versions
+ if "name" in params:
+ method_not_called, method_called = m_list_layers, m_list_layer_versions
+
+ if failure:
+ exc = "lambda_layer_exception"
+ msg = "this exception has been generated for unit tests"
+
+ method_called.side_effect = raise_layer_info_exception(exc, msg)
+
+ with pytest.raises(SystemExit):
+ lambda_layer_info.execute_module(module, lambda_client)
+
+ module.fail_json_aws.assert_called_with(exception=exc, msg=msg)
+
+ else:
+ result = {"A": "valueA", "B": "valueB"}
+ method_called.return_value = result
+
+ with pytest.raises(SystemExit):
+ lambda_layer_info.execute_module(module, lambda_client)
+
+ module.exit_json.assert_called_with(
+ changed=False, layers_versions=result
+ )
+ method_called.assert_called_with(lambda_client, **params)
+ method_not_called.list_layers.assert_not_called()
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_s3_object.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_s3_object.py
new file mode 100644
index 000000000..b02513072
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_s3_object.py
@@ -0,0 +1,29 @@
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.module_utils.six.moves.urllib.parse import urlparse
+
+from ansible_collections.amazon.aws.plugins.modules import s3_object
+
+
+class TestUrlparse():
+
+ def test_urlparse(self):
+ actual = urlparse("http://test.com/here")
+ assert actual.scheme == "http"
+ assert actual.netloc == "test.com"
+ assert actual.path == "/here"
+
+ def test_is_fakes3(self):
+ actual = s3_object.is_fakes3("fakes3://bla.blubb")
+ assert actual is True
+
+ def test_get_s3_connection(self):
+ aws_connect_kwargs = dict(aws_access_key_id="access_key",
+ aws_secret_access_key="secret_key")
+ location = None
+ rgw = True
+ s3_url = "http://bla.blubb"
+ actual = s3_object.get_s3_connection(None, aws_connect_kwargs, location, rgw, s3_url)
+ assert "bla.blubb" in str(actual._endpoint)
diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py b/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py
new file mode 100644
index 000000000..058a5b605
--- /dev/null
+++ b/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py
@@ -0,0 +1,50 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+from ansible_collections.amazon.aws.tests.unit.compat import unittest
+from ansible_collections.amazon.aws.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+
+
+def set_module_args(args):
+ if '_ansible_remote_tmp' not in args:
+ args['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in args:
+ args['_ansible_keep_remote_files'] = False
+
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+class AnsibleExitJson(Exception):
+ pass
+
+
+class AnsibleFailJson(Exception):
+ pass
+
+
+def exit_json(*args, **kwargs):
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs):
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class ModuleTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module.start()
+ self.mock_sleep = patch('time.sleep')
+ self.mock_sleep.start()
+ set_module_args({})
+ self.addCleanup(self.mock_module.stop)
+ self.addCleanup(self.mock_sleep.stop)