diff --git a/changelogs/fragments/migrate__ec2_ami_copy_acm_certificate_acm_certificate_info.yml b/changelogs/fragments/migrate__ec2_ami_copy_acm_certificate_acm_certificate_info.yml new file mode 100644 index 00000000000..465a06ad540 --- /dev/null +++ b/changelogs/fragments/migrate__ec2_ami_copy_acm_certificate_acm_certificate_info.yml @@ -0,0 +1,11 @@ +--- +breaking_changes: + - ec2_ami_copy - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be + updated to use ``amazon.aws.ec2_ami_copy``. + - acm_certificate - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be + updated to use ``amazon.aws.acm_certificate``. + - acm_certificate_info - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.acm_certificate_info``. diff --git a/meta/runtime.yml b/meta/runtime.yml index 5d05436df14..b0e0934b7e1 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -3,8 +3,6 @@ requires_ansible: '>=2.12.0' action_groups: aws: - accessanalyzer_validate_policy_info - - acm_certificate - - acm_certificate_info - api_gateway - api_gateway_domain - api_gateway_info @@ -90,7 +88,6 @@ action_groups: - dms_replication_subnet_group - dynamodb_table - dynamodb_ttl - - ec2_ami_copy - ec2_asg - ec2_asg_info - ec2_asg_instance_refresh @@ -218,10 +215,14 @@ action_groups: - wafv2_web_acl_info plugin_routing: modules: - autoscaling_group_info: - redirect: amazon.aws.autoscaling_group_info + acm_certificate: + redirect: amazon.aws.acm_certificate + acm_certificate_info: + redirect: amazon.aws.acm_certificate_info autoscaling_group: redirect: amazon.aws.autoscaling_group + autoscaling_group_info: + redirect: amazon.aws.autoscaling_group_info aws_acm: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.acm_certificate @@ -359,12 +360,14 @@ plugin_routing: aws_waf_web_acl: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.waf_web_acl - cloudtrail: - # Deprecation for this alias should not *start* prior to 2024-11-01 - redirect: amazon.aws.cloudtrail cloudfront_info: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.cloudfront_distribution_info + cloudtrail: + # Deprecation for this alias should not *start* prior to 2024-11-01 + redirect: amazon.aws.cloudtrail + cloudwatch_metric_alarm: + redirect: amazon.aws.cloudwatch_metric_alarm cloudwatchevent_rule: redirect: amazon.aws.cloudwatchevent_rule cloudwatchlogs_log_group: @@ -373,8 +376,8 @@ plugin_routing: redirect: amazon.aws.cloudwatchlogs_log_group_info cloudwatchlogs_log_group_metric_filter: redirect: amazon.aws.cloudwatchlogs_log_group_metric_filter - cloudwatch_metric_alarm: - redirect: amazon.aws.cloudwatch_metric_alarm + ec2_ami_copy: + redirect: amazon.aws.ec2_ami_copy ec2_asg: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: amazon.aws.autoscaling_group @@ -399,9 +402,6 @@ plugin_routing: redirect: amazon.aws.ec2_eip_info ec2_elb: redirect: community.aws.elb_instance - ec2_metric_alarm: - # Deprecation for this alias should not *start* prior to 2024-09-01 - redirect: amazon.aws.cloudwatch_metric_alarm ec2_instance: redirect: amazon.aws.ec2_instance ec2_instance_info: @@ -415,6 +415,9 @@ plugin_routing: ec2_lc_info: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.autoscaling_launch_config_info + ec2_metric_alarm: + # Deprecation for this alias should not *start* prior to 2024-09-01 + redirect: amazon.aws.cloudwatch_metric_alarm ec2_scaling_policy: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.autoscaling_policy @@ -436,12 +439,12 @@ plugin_routing: redirect: amazon.aws.ec2_vpc_route_table ec2_vpc_route_table_info: redirect: amazon.aws.ec2_vpc_route_table_info - elb_classic_lb: - redirect: amazon.aws.ec2_elb_lb elb_application_lb: redirect: amazon.aws.elb_application_lb elb_application_lb_info: redirect: amazon.aws.elb_application_lb_info + elb_classic_lb: + redirect: amazon.aws.ec2_elb_lb execute_lambda: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: amazon.aws.lambda_execute diff --git a/plugins/modules/acm_certificate.py b/plugins/modules/acm_certificate.py deleted file mode 100644 index 0b4f7037a71..00000000000 --- a/plugins/modules/acm_certificate.py +++ /dev/null @@ -1,556 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2019 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# Author: -# - Matthew Davis -# on behalf of Telstra Corporation Limited - -DOCUMENTATION = r""" ---- -module: acm_certificate -short_description: Upload and delete certificates in the AWS Certificate Manager service -version_added: 1.0.0 -description: - - > - Import and delete certificates in Amazon Web Service's Certificate - Manager (AWS ACM). - - > - This module does not currently interact with AWS-provided certificates. - It currently only manages certificates provided to AWS by the user. - - The ACM API allows users to upload multiple certificates for the same domain - name, and even multiple identical certificates. This module attempts to - restrict such freedoms, to be idempotent, as per the Ansible philosophy. - It does this through applying AWS resource "Name" tags to ACM certificates. - - > - When I(state=present), - if there is one certificate in ACM - with a C(Name) tag equal to the I(name_tag) parameter, - and an identical body and chain, - this task will succeed without effect. - - > - When I(state=present), - if there is one certificate in ACM - a I(Name) tag equal to the I(name_tag) parameter, - and a different body, - this task will overwrite that certificate. - - > - When I(state=present), - if there are multiple certificates in ACM - with a I(Name) tag equal to the I(name_tag) parameter, - this task will fail. - - > - When I(state=absent) and I(certificate_arn) is defined, - this module will delete the ACM resource with that ARN if it exists in this - region, and succeed without effect if it doesn't exist. - - > - When I(state=absent) and I(domain_name) is defined, this module will delete - all ACM resources in this AWS region with a corresponding domain name. - If there are none, it will succeed without effect. - - > - When I(state=absent) and I(certificate_arn) is not defined, - and I(domain_name) is not defined, this module will delete all ACM resources - in this AWS region with a corresponding I(Name) tag. - If there are none, it will succeed without effect. - - > - Note that this may not work properly with keys of size 4096 bits, due to a - limitation of the ACM API. - - Prior to release 5.0.0 this module was called C(community.aws.aws_acm). - The usage did not change. -options: - certificate: - description: - - The body of the PEM encoded public certificate. - - Required when I(state) is not C(absent) and the certificate does not exist. - - > - If your certificate is in a file, - use C(lookup('file', 'path/to/cert.pem')). - type: str - certificate_arn: - description: - - The ARN of a certificate in ACM to modify or delete. - - > - If I(state=present), the certificate with the specified ARN can be updated. - For example, this can be used to add/remove tags to an existing certificate. - - > - If I(state=absent), you must provide one of - I(certificate_arn), I(domain_name) or I(name_tag). - - > - If I(state=absent) and no resource exists with this ARN in this region, - the task will succeed with no effect. - - > - If I(state=absent) and the corresponding resource exists in a different - region, this task may report success without deleting that resource. - type: str - aliases: [arn] - certificate_chain: - description: - - The body of the PEM encoded chain for your certificate. - - > - If your certificate chain is in a file, - use C(lookup('file', 'path/to/chain.pem')). - - Ignored when I(state=absent) - type: str - domain_name: - description: - - The domain name of the certificate. - - > - If I(state=absent) and I(domain_name) is specified, - this task will delete all ACM certificates with this domain. - - > - Exactly one of I(domain_name), I(name_tag) and I(certificate_arn) - must be provided. - - > - If I(state=present) this must not be specified. - (Since the domain name is encoded within the public certificate's body.) - type: str - aliases: [domain] - name_tag: - description: - - > - The unique identifier for tagging resources using AWS tags, - with key I(Name). - - This can be any set of characters accepted by AWS for tag values. - - > - This is to ensure Ansible can treat certificates idempotently, - even though the ACM API allows duplicate certificates. - - If I(state=preset), this must be specified. - - > - If I(state=absent) and I(name_tag) is specified, - this task will delete all ACM certificates with this Name tag. - - > - If I(state=absent), you must provide exactly one of - I(certificate_arn), I(domain_name) or I(name_tag). - - > - If both I(name_tag) and the 'Name' tag in I(tags) are set, - the values must be the same. - - > - If the 'Name' tag in I(tags) is not set and I(name_tag) is set, - the I(name_tag) value is copied to I(tags). - type: str - aliases: [name] - private_key: - description: - - The body of the PEM encoded private key. - - Required when I(state=present) and the certificate does not exist. - - Ignored when I(state=absent). - - > - If your private key is in a file, - use C(lookup('file', 'path/to/key.pem')). - type: str - state: - description: - - > - If I(state=present), the specified public certificate and private key - will be uploaded, with I(Name) tag equal to I(name_tag). - - > - If I(state=absent), any certificates in this region - with a corresponding I(domain_name), I(name_tag) or I(certificate_arn) - will be deleted. - choices: [present, absent] - default: present - type: str - -notes: - - Support for I(tags) and I(purge_tags) was added in release 3.2.0 -author: - - Matthew Davis (@matt-telstra) on behalf of Telstra Corporation Limited -extends_documentation_fragment: - - amazon.aws.common.modules - - amazon.aws.region.modules - - amazon.aws.tags - - amazon.aws.boto3 -""" - -EXAMPLES = r""" - -- name: upload a self-signed certificate - community.aws.acm_certificate: - certificate: "{{ lookup('file', 'cert.pem' ) }}" - privateKey: "{{ lookup('file', 'key.pem' ) }}" - name_tag: my_cert # to be applied through an AWS tag as "Name":"my_cert" - region: ap-southeast-2 # AWS region - -- name: create/update a certificate with a chain - community.aws.acm_certificate: - certificate: "{{ lookup('file', 'cert.pem' ) }}" - private_key: "{{ lookup('file', 'key.pem' ) }}" - name_tag: my_cert - certificate_chain: "{{ lookup('file', 'chain.pem' ) }}" - state: present - region: ap-southeast-2 - register: cert_create - -- name: print ARN of cert we just created - ansible.builtin.debug: - var: cert_create.certificate.arn - -- name: delete the cert we just created - community.aws.acm_certificate: - name_tag: my_cert - state: absent - region: ap-southeast-2 - -- name: delete a certificate with a particular ARN - community.aws.acm_certificate: - certificate_arn: "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" - state: absent - region: ap-southeast-2 - -- name: delete all certificates with a particular domain name - community.aws.acm_certificate: - domain_name: acm.ansible.com - state: absent - region: ap-southeast-2 - -- name: add tags to an existing certificate with a particular ARN - community.aws.acm_certificate: - certificate_arn: "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" - tags: - Name: my_certificate - Application: search - Environment: development - purge_tags: true -""" - -RETURN = r""" -certificate: - description: Information about the certificate which was uploaded - type: complex - returned: when I(state=present) - contains: - arn: - description: The ARN of the certificate in ACM - type: str - returned: when I(state=present) and not in check mode - sample: "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" - domain_name: - description: The domain name encoded within the public certificate - type: str - returned: when I(state=present) - sample: acm.ansible.com -arns: - description: A list of the ARNs of the certificates in ACM which were deleted - type: list - elements: str - returned: when I(state=absent) - sample: - - "arn:aws:acm:ap-southeast-2:123456789012:certificate/01234567-abcd-abcd-abcd-012345678901" -""" - - -import base64 -import re # regex library -from copy import deepcopy - -try: - import botocore -except ImportError: - pass # handled by AnsibleAWSModule - -from ansible.module_utils._text import to_text - -from ansible_collections.amazon.aws.plugins.module_utils.acm import ACMServiceManager -from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list -from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags - -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule - - -def ensure_tags(client, module, resource_arn, existing_tags, tags, purge_tags): - if tags is None: - return (False, existing_tags) - - tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, tags, purge_tags) - changed = bool(tags_to_add or tags_to_remove) - if tags_to_add and not module.check_mode: - try: - client.add_tags_to_certificate( - CertificateArn=resource_arn, - Tags=ansible_dict_to_boto3_tag_list(tags_to_add), - ) - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: - module.fail_json_aws(e, f"Couldn't add tags to certificate {resource_arn}") - if tags_to_remove and not module.check_mode: - # remove_tags_from_certificate wants a list of key, value pairs, not a list of keys. - tags_list = [{"Key": key, "Value": existing_tags.get(key)} for key in tags_to_remove] - try: - client.remove_tags_from_certificate( - CertificateArn=resource_arn, - Tags=tags_list, - ) - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: - module.fail_json_aws(e, f"Couldn't remove tags from certificate {resource_arn}") - new_tags = deepcopy(existing_tags) - for key, value in tags_to_add.items(): - new_tags[key] = value - for key in tags_to_remove: - new_tags.pop(key, None) - return (changed, new_tags) - - -# Takes in two text arguments -# Each a PEM encoded certificate -# Or a chain of PEM encoded certificates -# May include some lines between each chain in the cert, e.g. "Subject: ..." -# Returns True iff the chains/certs are functionally identical (including chain order) -def chain_compare(module, a, b): - chain_a_pem = pem_chain_split(module, a) - chain_b_pem = pem_chain_split(module, b) - - if len(chain_a_pem) != len(chain_b_pem): - return False - - # Chain length is the same - for ca, cb in zip(chain_a_pem, chain_b_pem): - der_a = PEM_body_to_DER(module, ca) - der_b = PEM_body_to_DER(module, cb) - if der_a != der_b: - return False - - return True - - -# Takes in PEM encoded data with no headers -# returns equivilent DER as byte array -def PEM_body_to_DER(module, pem): - try: - der = base64.b64decode(to_text(pem)) - except (ValueError, TypeError) as e: - module.fail_json_aws(e, msg="Unable to decode certificate chain") - return der - - -# Store this globally to avoid repeated recompilation -pem_chain_split_regex = re.compile( - r"------?BEGIN [A-Z0-9. ]*CERTIFICATE------?([a-zA-Z0-9\+\/=\s]+)------?END [A-Z0-9. ]*CERTIFICATE------?" -) - - -# Use regex to split up a chain or single cert into an array of base64 encoded data -# Using "-----BEGIN CERTIFICATE-----" and "----END CERTIFICATE----" -# Noting that some chains have non-pem data in between each cert -# This function returns only what's between the headers, excluding the headers -def pem_chain_split(module, pem): - pem_arr = re.findall(pem_chain_split_regex, to_text(pem)) - - if len(pem_arr) == 0: - # This happens if the regex doesn't match at all - module.fail_json(msg="Unable to split certificate chain. Possibly zero-length chain?") - - return pem_arr - - -def update_imported_certificate(client, module, acm, old_cert, desired_tags): - """ - Update the existing certificate that was previously imported in ACM. - """ - module.debug("Existing certificate found in ACM") - if ("tags" not in old_cert) or ("Name" not in old_cert["tags"]): - # shouldn't happen - module.fail_json(msg="Internal error, unsure which certificate to update", certificate=old_cert) - if module.params.get("name_tag") is not None and (old_cert["tags"]["Name"] != module.params.get("name_tag")): - # This could happen if the user identified the certificate using 'certificate_arn' or 'domain_name', - # and the 'Name' tag in the AWS API does not match the ansible 'name_tag'. - module.fail_json(msg="Internal error, Name tag does not match", certificate=old_cert) - if "certificate" not in old_cert: - # shouldn't happen - module.fail_json(msg="Internal error, unsure what the existing cert in ACM is", certificate=old_cert) - - cert_arn = None - # Are the existing certificate in ACM and the local certificate the same? - same = True - if module.params.get("certificate") is not None: - same &= chain_compare(module, old_cert["certificate"], module.params["certificate"]) - if module.params["certificate_chain"]: - # Need to test this - # not sure if Amazon appends the cert itself to the chain when self-signed - same &= chain_compare(module, old_cert["certificate_chain"], module.params["certificate_chain"]) - else: - # When there is no chain with a cert - # it seems Amazon returns the cert itself as the chain - same &= chain_compare(module, old_cert["certificate_chain"], module.params["certificate"]) - - if same: - module.debug("Existing certificate in ACM is the same") - cert_arn = old_cert["certificate_arn"] - changed = False - else: - absent_args = ["certificate", "name_tag", "private_key"] - if sum([(module.params[a] is not None) for a in absent_args]) < 3: - module.fail_json( - msg="When importing a certificate, all of 'name_tag', 'certificate' and 'private_key' must be specified" - ) - module.debug("Existing certificate in ACM is different, overwriting") - changed = True - if module.check_mode: - cert_arn = old_cert["certificate_arn"] - # note: returned domain will be the domain of the previous cert - else: - # update cert in ACM - cert_arn = acm.import_certificate( - client, - module, - certificate=module.params["certificate"], - private_key=module.params["private_key"], - certificate_chain=module.params["certificate_chain"], - arn=old_cert["certificate_arn"], - tags=desired_tags, - ) - return (changed, cert_arn) - - -def import_certificate(client, module, acm, desired_tags): - """ - Import a certificate to ACM. - """ - # Validate argument requirements - absent_args = ["certificate", "name_tag", "private_key"] - cert_arn = None - if sum([(module.params[a] is not None) for a in absent_args]) < 3: - module.fail_json( - msg="When importing a new certificate, all of 'name_tag', 'certificate' and 'private_key' must be specified" - ) - module.debug("No certificate in ACM. Creating new one.") - changed = True - if module.check_mode: - domain = "example.com" - module.exit_json(certificate=dict(domain_name=domain), changed=True) - else: - cert_arn = acm.import_certificate( - client, - module, - certificate=module.params["certificate"], - private_key=module.params["private_key"], - certificate_chain=module.params["certificate_chain"], - tags=desired_tags, - ) - return (changed, cert_arn) - - -def ensure_certificates_present(client, module, acm, certificates, desired_tags, filter_tags): - cert_arn = None - changed = False - if len(certificates) > 1: - msg = f"More than one certificate with Name={module.params['name_tag']} exists in ACM in this region" - module.fail_json(msg=msg, certificates=certificates) - elif len(certificates) == 1: - # Update existing certificate that was previously imported to ACM. - (changed, cert_arn) = update_imported_certificate(client, module, acm, certificates[0], desired_tags) - else: # len(certificates) == 0 - # Import new certificate to ACM. - (changed, cert_arn) = import_certificate(client, module, acm, desired_tags) - - # Add/remove tags to/from certificate - try: - existing_tags = boto3_tag_list_to_ansible_dict( - client.list_tags_for_certificate(CertificateArn=cert_arn)["Tags"] - ) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Couldn't get tags for certificate") - - purge_tags = module.params.get("purge_tags") - (c, new_tags) = ensure_tags(client, module, cert_arn, existing_tags, desired_tags, purge_tags) - changed |= c - domain = acm.get_domain_of_cert(client=client, module=module, arn=cert_arn) - module.exit_json(certificate=dict(domain_name=domain, arn=cert_arn, tags=new_tags), changed=changed) - - -def ensure_certificates_absent(client, module, acm, certificates): - for cert in certificates: - if not module.check_mode: - acm.delete_certificate(client, module, cert["certificate_arn"]) - module.exit_json(arns=[cert["certificate_arn"] for cert in certificates], changed=(len(certificates) > 0)) - - -def main(): - argument_spec = dict( - certificate=dict(), - certificate_arn=dict(aliases=["arn"]), - certificate_chain=dict(), - domain_name=dict(aliases=["domain"]), - name_tag=dict(aliases=["name"]), - private_key=dict(no_log=True), - tags=dict(type="dict", aliases=["resource_tags"]), - purge_tags=dict(type="bool", default=True), - state=dict(default="present", choices=["present", "absent"]), - ) - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - acm = ACMServiceManager(module) - - # Check argument requirements - if module.params["state"] == "present": - # at least one of these should be specified. - absent_args = ["certificate_arn", "domain_name", "name_tag"] - if sum([(module.params[a] is not None) for a in absent_args]) < 1: - for a in absent_args: - module.debug(f"{a} is {module.params[a]}") - module.fail_json( - msg="If 'state' is specified as 'present' then at least one of 'name_tag', 'certificate_arn' or 'domain_name' must be specified" - ) - else: # absent - # exactly one of these should be specified - absent_args = ["certificate_arn", "domain_name", "name_tag"] - if sum([(module.params[a] is not None) for a in absent_args]) != 1: - for a in absent_args: - module.debug(f"{a} is {module.params[a]}") - module.fail_json( - msg="If 'state' is specified as 'absent' then exactly one of 'name_tag', 'certificate_arn' or 'domain_name' must be specified" - ) - - filter_tags = None - desired_tags = None - if module.params.get("tags") is not None: - desired_tags = module.params["tags"] - else: - # Because we're setting the Name tag, we need to explicitly not purge when tags isn't passed - module.params["purge_tags"] = False - if module.params.get("name_tag") is not None: - # The module was originally implemented to filter certificates based on the 'Name' tag. - # Other tags are not used to filter certificates. - # It would make sense to replace the existing name_tag, domain, certificate_arn attributes - # with a 'filter' attribute, but that would break backwards-compatibility. - filter_tags = dict(Name=module.params["name_tag"]) - if desired_tags is not None: - if "Name" in desired_tags: - if desired_tags["Name"] != module.params["name_tag"]: - module.fail_json(msg="Value of 'name_tag' conflicts with value of 'tags.Name'") - else: - desired_tags["Name"] = module.params["name_tag"] - else: - desired_tags = deepcopy(filter_tags) - - client = module.client("acm") - - # fetch the list of certificates currently in ACM - certificates = acm.get_certificates( - client=client, - module=module, - domain_name=module.params["domain_name"], - arn=module.params["certificate_arn"], - only_tags=filter_tags, - ) - - module.debug(f"Found {len(certificates)} corresponding certificates in ACM") - if module.params["state"] == "present": - ensure_certificates_present(client, module, acm, certificates, desired_tags, filter_tags) - - else: # state == absent - ensure_certificates_absent(client, module, acm, certificates) - - -if __name__ == "__main__": - # tests() - main() diff --git a/plugins/modules/acm_certificate_info.py b/plugins/modules/acm_certificate_info.py deleted file mode 100644 index 26d00e7e319..00000000000 --- a/plugins/modules/acm_certificate_info.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -DOCUMENTATION = r""" -module: acm_certificate_info -short_description: Retrieve certificate information from AWS Certificate Manager service -version_added: 1.0.0 -description: - - Retrieve information for ACM certificates. - - Note that this will not return information about uploaded keys of size 4096 bits, due to a limitation of the ACM API. - - Prior to release 5.0.0 this module was called C(community.aws.aws_acm_info). - The usage did not change. -options: - certificate_arn: - description: - - If provided, the results will be filtered to show only the certificate with this ARN. - - If no certificate with this ARN exists, this task will fail. - - If a certificate with this ARN exists in a different region, this task will fail. - aliases: - - arn - type: str - domain_name: - description: - - The domain name of an ACM certificate to limit the search to. - aliases: - - name - type: str - statuses: - description: - - Status to filter the certificate results. - choices: ['PENDING_VALIDATION', 'ISSUED', 'INACTIVE', 'EXPIRED', 'VALIDATION_TIMED_OUT', 'REVOKED', 'FAILED'] - type: list - elements: str - tags: - description: - - Filter results to show only certificates with tags that match all the tags specified here. - type: dict -author: - - Will Thames (@willthames) -extends_documentation_fragment: - - amazon.aws.common.modules - - amazon.aws.region.modules - - amazon.aws.boto3 -""" - -EXAMPLES = r""" -- name: obtain all ACM certificates - community.aws.acm_certificate_info: - -- name: obtain all information for a single ACM certificate - community.aws.acm_certificate_info: - domain_name: "*.example_com" - -- name: obtain all certificates pending validation - community.aws.acm_certificate_info: - statuses: - - PENDING_VALIDATION - -- name: obtain all certificates with tag Name=foo and myTag=bar - community.aws.acm_certificate_info: - tags: - Name: foo - myTag: bar - - -# The output is still a list of certificates, just one item long. -- name: obtain information about a certificate with a particular ARN - community.aws.acm_certificate_info: - certificate_arn: "arn:aws:acm:ap-southeast-2:123456789012:certificate/abcdeabc-abcd-1234-4321-abcdeabcde12" - -""" - -RETURN = r""" -certificates: - description: A list of certificates - returned: always - type: complex - contains: - certificate: - description: The ACM Certificate body - returned: when certificate creation is complete - sample: '-----BEGIN CERTIFICATE-----\\nMII.....-----END CERTIFICATE-----\\n' - type: str - certificate_arn: - description: Certificate ARN - returned: always - sample: arn:aws:acm:ap-southeast-2:123456789012:certificate/abcd1234-abcd-1234-abcd-123456789abc - type: str - certificate_chain: - description: Full certificate chain for the certificate - returned: when certificate creation is complete - sample: '-----BEGIN CERTIFICATE-----\\nMII...\\n-----END CERTIFICATE-----\\n-----BEGIN CERTIFICATE-----\\n...' - type: str - created_at: - description: Date certificate was created - returned: always - sample: '2017-08-15T10:31:19+10:00' - type: str - domain_name: - description: Domain name for the certificate - returned: always - sample: '*.example.com' - type: str - domain_validation_options: - description: Options used by ACM to validate the certificate - returned: when certificate type is AMAZON_ISSUED - type: complex - contains: - domain_name: - description: Fully qualified domain name of the certificate - returned: always - sample: example.com - type: str - validation_domain: - description: The domain name ACM used to send validation emails - returned: always - sample: example.com - type: str - validation_emails: - description: A list of email addresses that ACM used to send domain validation emails - returned: always - sample: - - admin@example.com - - postmaster@example.com - type: list - elements: str - validation_status: - description: Validation status of the domain - returned: always - sample: SUCCESS - type: str - failure_reason: - description: Reason certificate request failed - returned: only when certificate issuing failed - type: str - sample: NO_AVAILABLE_CONTACTS - in_use_by: - description: A list of ARNs for the AWS resources that are using the certificate. - returned: always - sample: [] - type: list - elements: str - issued_at: - description: Date certificate was issued - returned: always - sample: '2017-01-01T00:00:00+10:00' - type: str - issuer: - description: Issuer of the certificate - returned: always - sample: Amazon - type: str - key_algorithm: - description: Algorithm used to generate the certificate - returned: always - sample: RSA-2048 - type: str - not_after: - description: Date after which the certificate is not valid - returned: always - sample: '2019-01-01T00:00:00+10:00' - type: str - not_before: - description: Date before which the certificate is not valid - returned: always - sample: '2017-01-01T00:00:00+10:00' - type: str - renewal_summary: - description: Information about managed renewal process - returned: when certificate is issued by Amazon and a renewal has been started - type: complex - contains: - domain_validation_options: - description: Options used by ACM to validate the certificate - returned: when certificate type is AMAZON_ISSUED - type: complex - contains: - domain_name: - description: Fully qualified domain name of the certificate - returned: always - sample: example.com - type: str - validation_domain: - description: The domain name ACM used to send validation emails - returned: always - sample: example.com - type: str - validation_emails: - description: A list of email addresses that ACM used to send domain validation emails - returned: always - sample: - - admin@example.com - - postmaster@example.com - type: list - elements: str - validation_status: - description: Validation status of the domain - returned: always - sample: SUCCESS - type: str - renewal_status: - description: Status of the domain renewal - returned: always - sample: PENDING_AUTO_RENEWAL - type: str - revocation_reason: - description: Reason for certificate revocation - returned: when the certificate has been revoked - sample: SUPERCEDED - type: str - revoked_at: - description: Date certificate was revoked - returned: when the certificate has been revoked - sample: '2017-09-01T10:00:00+10:00' - type: str - serial: - description: The serial number of the certificate - returned: always - sample: 00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f - type: str - signature_algorithm: - description: Algorithm used to sign the certificate - returned: always - sample: SHA256WITHRSA - type: str - status: - description: Status of the certificate in ACM - returned: always - sample: ISSUED - type: str - subject: - description: The name of the entity that is associated with the public key contained in the certificate - returned: always - sample: CN=*.example.com - type: str - subject_alternative_names: - description: Subject Alternative Names for the certificate - returned: always - sample: - - '*.example.com' - type: list - elements: str - tags: - description: Tags associated with the certificate - returned: always - type: dict - sample: - Application: helloworld - Environment: test - type: - description: The source of the certificate - returned: always - sample: AMAZON_ISSUED - type: str -""" - -from ansible_collections.amazon.aws.plugins.module_utils.acm import ACMServiceManager - -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule - - -def main(): - argument_spec = dict( - certificate_arn=dict(aliases=["arn"]), - domain_name=dict(aliases=["name"]), - statuses=dict( - type="list", - elements="str", - choices=[ - "PENDING_VALIDATION", - "ISSUED", - "INACTIVE", - "EXPIRED", - "VALIDATION_TIMED_OUT", - "REVOKED", - "FAILED", - ], - ), - tags=dict(type="dict"), - ) - module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) - acm_info = ACMServiceManager(module) - - client = module.client("acm") - - certificates = acm_info.get_certificates( - client, - module, - domain_name=module.params["domain_name"], - statuses=module.params["statuses"], - arn=module.params["certificate_arn"], - only_tags=module.params["tags"], - ) - - if module.params["certificate_arn"] and len(certificates) != 1: - module.fail_json(msg=f"No certificate exists in this region with ARN {module.params['certificate_arn']}") - - module.exit_json(certificates=certificates) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/ec2_ami_copy.py b/plugins/modules/ec2_ami_copy.py deleted file mode 100644 index 170a564e15d..00000000000 --- a/plugins/modules/ec2_ami_copy.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# This file is part of Ansible -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -DOCUMENTATION = r""" ---- -module: ec2_ami_copy -version_added: 1.0.0 -short_description: copies AMI between AWS regions, return new image id -description: - - Copies AMI from a source region to a destination region. B(Since version 2.3 this module depends on boto3.) -options: - source_region: - description: - - The source region the AMI should be copied from. - required: true - type: str - source_image_id: - description: - - The ID of the AMI in source region that should be copied. - required: true - type: str - name: - description: - - The name of the new AMI to copy. (As of 2.3 the default is C(default), in prior versions it was C(null).) - default: "default" - type: str - description: - description: - - An optional human-readable string describing the contents and purpose of the new AMI. - type: str - default: '' - encrypted: - description: - - Whether or not the destination snapshots of the copied AMI should be encrypted. - type: bool - default: false - kms_key_id: - description: - - KMS key id used to encrypt the image. If not specified, uses default EBS Customer Master Key (CMK) for your account. - type: str - wait: - description: - - Wait for the copied AMI to be in state C(available) before returning. - type: bool - default: false - wait_timeout: - description: - - How long before wait gives up, in seconds. - - Prior to 2.3 the default was C(1200). - - From 2.3-2.5 this option was deprecated in favor of boto3 waiter defaults. - - This was reenabled in 2.6 to allow timeouts greater than 10 minutes. - default: 600 - type: int - tags: - description: - - 'A hash/dictionary of tags to add to the new copied AMI: C({"key":"value"}) and C({"key":"value","key":"value"})' - type: dict - aliases: ['resource_tags'] - tag_equality: - description: - - Whether to use tags if the source AMI already exists in the target region. If this is set, and all tags match - in an existing AMI, the AMI will not be copied again. - default: false - type: bool -author: - - Amir Moulavi (@amir343) - - Tim C (@defunctio) -extends_documentation_fragment: - - amazon.aws.common.modules - - amazon.aws.region.modules - - amazon.aws.boto3 -""" - -EXAMPLES = r""" -- name: Basic AMI Copy - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - -- name: AMI copy wait until available - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - wait: true - wait_timeout: 1200 # Default timeout is 600 - register: image_id - -- name: Named AMI copy - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - name: My-Awesome-AMI - description: latest patch - -- name: Tagged AMI copy (will not copy the same AMI twice) - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - tags: - Name: My-Super-AMI - Patch: 1.2.3 - tag_equality: true - -- name: Encrypted AMI copy - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - encrypted: true - -- name: Encrypted AMI copy with specified key - community.aws.ec2_ami_copy: - source_region: us-east-1 - region: eu-west-1 - source_image_id: ami-xxxxxxx - encrypted: true - kms_key_id: arn:aws:kms:us-east-1:XXXXXXXXXXXX:key/746de6ea-50a4-4bcb-8fbc-e3b29f2d367b -""" - -RETURN = r""" -image_id: - description: AMI ID of the copied AMI - returned: always - type: str - sample: ami-e689729e -""" - -try: - from botocore.exceptions import BotoCoreError - from botocore.exceptions import ClientError - from botocore.exceptions import WaiterError -except ImportError: - pass # caught by AnsibleAWSModule - -from ansible.module_utils._text import to_native -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict - -from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list - -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule - - -def copy_image(module, ec2): - """ - Copies an AMI - - module : AnsibleAWSModule object - ec2: ec2 connection object - """ - - image = None - changed = False - tags = module.params.get("tags") - - params = { - "SourceRegion": module.params.get("source_region"), - "SourceImageId": module.params.get("source_image_id"), - "Name": module.params.get("name"), - "Description": module.params.get("description"), - "Encrypted": module.params.get("encrypted"), - } - if module.params.get("kms_key_id"): - params["KmsKeyId"] = module.params.get("kms_key_id") - - try: - if module.params.get("tag_equality"): - filters = [{"Name": f"tag:{k}", "Values": [v]} for (k, v) in module.params.get("tags").items()] - filters.append(dict(Name="state", Values=["available", "pending"])) - images = ec2.describe_images(Filters=filters) - if len(images["Images"]) > 0: - image = images["Images"][0] - if not image: - image = ec2.copy_image(**params) - image_id = image["ImageId"] - if tags: - ec2.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags)) - changed = True - - if module.params.get("wait"): - delay = 15 - max_attempts = module.params.get("wait_timeout") // delay - image_id = image.get("ImageId") - ec2.get_waiter("image_available").wait( - ImageIds=[image_id], WaiterConfig={"Delay": delay, "MaxAttempts": max_attempts} - ) - - module.exit_json(changed=changed, **camel_dict_to_snake_dict(image)) - except WaiterError as e: - module.fail_json_aws(e, msg="An error occurred waiting for the image to become available") - except (ClientError, BotoCoreError) as e: - module.fail_json_aws(e, msg="Could not copy AMI") - except Exception as e: - module.fail_json(msg=f"Unhandled exception. ({to_native(e)})") - - -def main(): - argument_spec = dict( - source_region=dict(required=True), - source_image_id=dict(required=True), - name=dict(default="default"), - description=dict(default=""), - encrypted=dict(type="bool", default=False, required=False), - kms_key_id=dict(type="str", required=False), - wait=dict(type="bool", default=False), - wait_timeout=dict(type="int", default=600), - tags=dict(type="dict", aliases=["resource_tags"]), - tag_equality=dict(type="bool", default=False), - ) - - module = AnsibleAWSModule(argument_spec=argument_spec) - ec2 = module.client("ec2") - copy_image(module, ec2) - - -if __name__ == "__main__": - main() diff --git a/tests/integration/targets/acm_certificate/aliases b/tests/integration/targets/acm_certificate/aliases deleted file mode 100644 index 55246697e84..00000000000 --- a/tests/integration/targets/acm_certificate/aliases +++ /dev/null @@ -1,4 +0,0 @@ -time=15m -cloud/aws - -acm_certificate_info diff --git a/tests/integration/targets/acm_certificate/defaults/main.yml b/tests/integration/targets/acm_certificate/defaults/main.yml deleted file mode 100644 index 5d3648f8e60..00000000000 --- a/tests/integration/targets/acm_certificate/defaults/main.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -# we'll generate 3 certificates locally for the test -# Upload the first -# overwrite it with the second -# and the third is unrelated, to check we only get info about the first when we want -local_certs: - - priv_key: "{{ remote_tmp_dir }}/private-1.pem" - cert: "{{ remote_tmp_dir }}/public-1.pem" - csr: "{{ remote_tmp_dir }}/csr-1.csr" - domain: "acm1.{{ aws_acm_test_uuid }}.ansible.com" - name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_1" - - - priv_key: "{{ remote_tmp_dir }}/private-2.pem" - cert: "{{ remote_tmp_dir }}/public-2.pem" - csr: "{{ remote_tmp_dir }}/csr-2.csr" - domain: "acm2.{{ aws_acm_test_uuid }}.ansible.com" - name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_2" - - - priv_key: "{{ remote_tmp_dir }}/private-3.pem" - cert: "{{ remote_tmp_dir }}/public-3.pem" - csr: "{{ remote_tmp_dir }}/csr-3.csr" - domain: "acm3.{{ aws_acm_test_uuid }}.ansible.com" - name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_3" - -# we'll have one private key -# make 2 chains using it -# so we can test what happens when you change just the chain -# not the domain or key -chained_cert: - priv_key: "{{ remote_tmp_dir }}/private-ch-0.pem" - domain: "acm-ch.{{ aws_acm_test_uuid }}.ansible.com" - name: "{{ resource_prefix }}_{{ aws_acm_test_uuid }}_4" - chains: - - cert: "{{ remote_tmp_dir }}/public-ch-0.pem" - csr: "{{ remote_tmp_dir }}/csr-ch-0.csr" - ca: 0 # index into local_certs - - cert: "{{ remote_tmp_dir }}/public-ch-1.pem" - csr: "{{ remote_tmp_dir }}/csr-ch-1.csr" - ca: 1 # index into local_certs - \ No newline at end of file diff --git a/tests/integration/targets/acm_certificate/meta/main.yml b/tests/integration/targets/acm_certificate/meta/main.yml deleted file mode 100644 index 1810d4bec98..00000000000 --- a/tests/integration/targets/acm_certificate/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - setup_remote_tmp_dir diff --git a/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml b/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml deleted file mode 100644 index 4c45db05e6d..00000000000 --- a/tests/integration/targets/acm_certificate/tasks/full_acm_test.yml +++ /dev/null @@ -1,508 +0,0 @@ -- name: AWS ACM integration test - module_defaults: - group/aws: - aws_region: '{{ aws_region }}' - access_key: '{{ aws_access_key }}' - secret_key: '{{ aws_secret_key }}' - session_token: '{{ security_token | default(omit) }}' - block: - - name: list certs - acm_certificate_info: null - register: list_all - - name: list certs with check mode - acm_certificate_info: null - register: list_all_check - check_mode: yes # read-only task, should work the same as with no - - name: check certificate listing worked - assert: - that: - - list_all.certificates is defined - - list_all_check.certificates is defined - - list_all.certificates == list_all_check.certificates - - name: ensure absent cert which doesn't exist - first time - acm_certificate: - name_tag: '{{ item.name }}' - state: absent - with_items: '{{ local_certs }}' - - name: ensure absent cert which doesn't exist - second time - acm_certificate: - name_tag: '{{ item[0].name }}' - state: absent - check_mode: '{{ item[1] }}' - with_nested: - - '{{ local_certs }}' - - [true, false] - register: absent_start_two - - name: check no change when ensuring absent cert is absent - assert: - that: - - not item.changed - with_items: "{{ absent_start_two.results }}" - - name: list cert which shouldn't exist - acm_certificate_info: - tags: - Name: '{{ item.name }}' - register: list_tag - with_items: '{{ local_certs }}' - - name: check listing of missing cert returns no result - with_items: '{{ list_tag.results }}' - assert: - that: - - (item.certificates | length) == 0 - - not list_tag.changed - - name: check directory was made - assert: - that: - - remote_tmp_dir is defined - - name: Generate private key for local certs - with_items: '{{ local_certs }}' - community.crypto.openssl_privatekey: - path: '{{ item.priv_key }}' - type: RSA - size: 2048 - - name: Generate an OpenSSL Certificate Signing Request for own certs - with_items: '{{ local_certs }}' - community.crypto.openssl_csr: - path: '{{ item.csr }}' - privatekey_path: '{{ item.priv_key }}' - common_name: '{{ item.domain }}' - - name: Generate a Self Signed OpenSSL certificate for own certs - with_items: '{{ local_certs }}' - community.crypto.x509_certificate: - provider: selfsigned - path: '{{ item.cert }}' - csr_path: '{{ item.csr }}' - privatekey_path: '{{ item.priv_key }}' - selfsigned_digest: sha256 - - name: upload certificate with check mode - acm_certificate: - name_tag: '{{ item.name }}' - certificate: '{{ lookup(''file'', item.cert ) }}' - private_key: '{{ lookup(''file'', item.priv_key ) }}' - state: present - check_mode: yes - register: upload_check - with_items: '{{ local_certs }}' - - name: check whether cert was uploaded in check mode - acm_certificate_info: - tags: - Name: '{{ item.name }}' - register: list_after_check_mode_upload - with_items: '{{ local_certs }}' - - name: check cert was not really uploaded in check mode - with_items: "{{ list_after_check_mode_upload.results }}" - assert: - that: - - upload_check.changed - - (item.certificates | length) == 0 - - name: upload certificates first time - acm_certificate: - name_tag: '{{ item.name }}' - certificate: '{{ lookup(''file'', item.cert ) }}' - private_key: '{{ lookup(''file'', item.priv_key ) }}' - state: present - register: upload - check_mode: no - with_items: '{{ local_certs }}' - until: upload is succeeded - retries: 5 - delay: 10 - - assert: - that: - - prev_task.certificate.arn is defined - - ('arn:aws:acm:123' | regex_search( 'arn:aws:acm:' )) is defined - - (prev_task.certificate.arn | regex_search( 'arn:aws:acm:' )) is defined - - prev_task.certificate.domain_name == original_cert.domain - - prev_task.changed - with_items: '{{ upload.results }}' - vars: - original_cert: '{{ item.item }}' - prev_task: '{{ item }}' - - name: fetch data about cert just uploaded, by ARN - acm_certificate_info: - certificate_arn: '{{ item.certificate.arn }}' - register: fetch_after_up - with_items: '{{ upload.results }}' - - name: check output of prior task (fetch data about cert just uploaded, by ARN) - assert: - that: - - fetch_after_up_result.certificates | length == 1 - - fetch_after_up_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup( 'file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '' )) - - '''Name'' in fetch_after_up_result.certificates[0].tags' - - fetch_after_up_result.certificates[0].tags['Name'] == original_cert.name - with_items: '{{ fetch_after_up.results }}' - vars: - fetch_after_up_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: fetch data about cert just uploaded, by name - acm_certificate_info: - tags: - Name: '{{ original_cert.name }}' - register: fetch_after_up_name - with_items: '{{ upload.results }}' - vars: - upload_result: '{{ item }}' - original_cert: '{{ item.item }}' - - name: check fetched data of cert we just uploaded - assert: - that: - - fetch_after_up_name_result.certificates | length == 1 - - fetch_after_up_name_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_name_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_name_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_up_name_result.certificates[0].tags' - - fetch_after_up_name_result.certificates[0].tags['Name'] == original_cert.name - with_items: '{{ fetch_after_up_name.results }}' - vars: - fetch_after_up_name_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: fetch data about cert just uploaded, by domain name - acm_certificate_info: - domain_name: '{{ original_cert.domain }}' - register: fetch_after_up_domain - with_items: '{{ upload.results }}' - vars: - original_cert: '{{ item.item }}' - - name: compare fetched data of cert just uploaded to upload task - assert: - that: - - fetch_after_up_domain_result.certificates | length == 1 - - fetch_after_up_domain_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_domain_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_domain_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_up_domain_result.certificates[0].tags' - - fetch_after_up_domain_result.certificates[0].tags['Name'] == original_cert.name - with_items: '{{ fetch_after_up_domain.results }}' - vars: - fetch_after_up_domain_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: upload certificates again, check not changed - acm_certificate: - name_tag: '{{ item.name }}' - certificate: '{{ lookup(''file'', item.cert ) }}' - private_key: '{{ lookup(''file'', item.priv_key ) }}' - state: present - register: upload2 - with_items: '{{ local_certs }}' - failed_when: upload2.changed - - name: update first cert with body of the second, first time, check mode - acm_certificate: - state: present - name_tag: '{{ local_certs[0].name }}' - certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' - private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' - check_mode: yes - register: overwrite_check - - name: check update in check mode detected required update - assert: - that: - - overwrite_check.changed - - name: check previous tasks did not change real cert - acm_certificate_info: - tags: - Name: '{{ local_certs[0].name }}' - register: fetch_after_overwrite_check - - name: check update with check mode did not change real cert - assert: - that: - - fetch_after_overwrite_check.certificates | length == 1 - - fetch_after_overwrite_check.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn - - fetch_after_overwrite_check.certificates[0].domain_name == local_certs[0].domain - - (fetch_after_overwrite_check.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[0].cert )| replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_overwrite_check.certificates[0].tags' - - fetch_after_overwrite_check.certificates[0].tags['Name'] == local_certs[0].name - - name: update first cert with body of the second, first real time - acm_certificate: - state: present - name_tag: '{{ local_certs[0].name }}' - certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' - private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' - register: overwrite - - name: check output of previous task (update first cert with body of the second, first time) - assert: - that: - - overwrite.certificate.arn is defined - - overwrite.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined - - overwrite.certificate.arn == upload.results[0].certificate.arn - - overwrite.certificate.domain_name == local_certs[1].domain - - overwrite.changed - - name: check update was sucessfull - acm_certificate_info: - tags: - Name: '{{ local_certs[0].name }}' - register: fetch_after_overwrite - - name: check output of update fetch - assert: - that: - - fetch_after_overwrite.certificates | length == 1 - - fetch_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn - - fetch_after_overwrite.certificates[0].domain_name == local_certs[1].domain - - (fetch_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert )| replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_overwrite.certificates[0].tags' - - fetch_after_overwrite.certificates[0].tags['Name'] == local_certs[0].name - - name: fetch other cert - acm_certificate_info: - tags: - Name: '{{ local_certs[1].name }}' - register: check_after_overwrite - - name: check other cert unaffected - assert: - that: - - check_after_overwrite.certificates | length == 1 - - check_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[1].certificates[0].certificate_arn - - check_after_overwrite.certificates[0].domain_name == local_certs[1].domain - - (check_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in check_after_overwrite.certificates[0].tags' - - check_after_overwrite.certificates[0].tags['Name'] == local_certs[1].name - - name: update first cert with body of the second again - acm_certificate: - state: present - name_tag: '{{ local_certs[0].name }}' - certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' - private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' - register: overwrite2 - - name: check output of previous task (update first cert with body of the second again) - assert: - that: - - overwrite2.certificate.arn is defined - - overwrite2.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined - - overwrite2.certificate.arn == upload.results[0].certificate.arn - - overwrite2.certificate.domain_name == local_certs[1].domain - - not overwrite2.changed - - name: delete certs 1 and 2 in check mode - acm_certificate: - state: absent - domain_name: '{{ local_certs[1].domain }}' - check_mode: yes - register: delete_both_check - - name: test deletion with check mode detected change - assert: - that: - - delete_both_check.changed - - name: fetch info for certs 1 and 2 - acm_certificate_info: - tags: - Name: '{{ local_certs[item].name }}' - register: check_del_one_check - with_items: - - 0 - - 1 - - name: test deletion with check mode detected change - with_items: '{{ check_del_one_check.results }}' - assert: - that: - - (item.certificates | length) == 1 - - name: delete certs 1 and 2 real - acm_certificate: - state: absent - domain_name: '{{ local_certs[1].domain }}' - register: delete_both - - name: test prev task - assert: - that: - - delete_both.arns is defined - - check_after_overwrite.certificates[0].certificate_arn in delete_both.arns - - upload.results[0].certificate.arn in delete_both.arns - - delete_both.changed - - name: fetch info for certs 1 and 2 - acm_certificate_info: - tags: - Name: '{{ local_certs[item].name }}' - register: check_del_one - with_items: - - 0 - - 1 - retries: 2 - until: - - check_del_one is not failed - - check_del_one.certificates | length == 0 - delay: 10 - - name: check certs 1 and 2 were already deleted - with_items: '{{ check_del_one.results }}' - assert: - that: (item.certificates | length) == 0 - - name: check cert 3 - acm_certificate_info: - tags: - Name: '{{ local_certs[2].name }}' - register: check_del_one_remain - - name: check cert 3 not deleted - assert: - that: - - (check_del_one_remain.certificates | length) == 1 - - name: delete cert 3 - acm_certificate: - state: absent - domain_name: '{{ local_certs[2].domain }}' - register: delete_third - - name: check cert 3 deletion went as expected - assert: - that: - - delete_third.arns is defined - - delete_third.arns | length == 1 - - delete_third.arns[0] == upload.results[2].certificate.arn - - delete_third.changed - - name: check cert 3 was deleted - acm_certificate_info: - tags: - Name: '{{ local_certs[2].name }}' - register: check_del_three - failed_when: check_del_three.certificates | length != 0 - - name: delete cert 3 again - acm_certificate: - state: absent - domain_name: '{{ local_certs[2].domain }}' - register: delete_third - - name: check deletion of cert 3 not changed, because already deleted - assert: - that: - - delete_third.arns is defined - - delete_third.arns | length == 0 - - not delete_third.changed - - name: delete cert 3 again, check mode - acm_certificate: - state: absent - domain_name: '{{ local_certs[2].domain }}' - check_mode: yes - register: delete_third_check - - name: test deletion in check mode detected required change - assert: - that: - - not delete_third_check.changed - - name: check directory was made - assert: - that: - - remote_tmp_dir is defined - - name: Generate private key for cert to be chained - community.crypto.openssl_privatekey: - path: '{{ chained_cert.priv_key }}' - type: RSA - size: 2048 - - name: Generate two OpenSSL Certificate Signing Requests for cert to be chained - with_items: '{{ chained_cert.chains }}' - community.crypto.openssl_csr: - path: '{{ item.csr }}' - privatekey_path: '{{ chained_cert.priv_key }}' - common_name: '{{ chained_cert.domain }}' - - name: Sign new certs with cert 0 and 1 - with_items: '{{ chained_cert.chains }}' - community.crypto.x509_certificate: - provider: ownca - path: '{{ item.cert }}' - csr_path: '{{ item.csr }}' - ownca_path: '{{ local_certs[item.ca].cert }}' - ownca_privatekey_path: '{{ local_certs[item.ca].priv_key }}' - selfsigned_digest: sha256 - - name: check files exist (for next task) - file: - path: '{{ item }}' - state: file - with_items: - - '{{ local_certs[chained_cert.chains[0].ca].cert }}' - - '{{ local_certs[chained_cert.chains[1].ca].cert }}' - - '{{ chained_cert.chains[0].cert }}' - - '{{ chained_cert.chains[1].cert }}' - - name: Find chains - with_items: '{{ chained_cert.chains }}' - register: chains - community.crypto.certificate_complete_chain: - input_chain: '{{ lookup(''file'', item.cert ) }}' - root_certificates: - - '{{ local_certs[item.ca].cert }}' - - name: upload chained cert, first chain, first time - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' - certificate_chain: '{{ chains.results[0].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain - failed_when: not upload_chain.changed - - name: fetch chain of cert we just uploaded - acm_certificate_info: - tags: - Name: '{{ chained_cert.name }}' - register: check_chain - until: - - check_chain.certificates | length >= 1 - retries: 5 - delay: 2 - - name: check chain of cert we just uploaded - assert: - that: - - (check_chain.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[0].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) - - (check_chain.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[0].cert ) | replace( ' ', '' ) | replace( '\n', '') ) - - name: upload chained cert again, check not changed - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' - certificate_chain: '{{ chains.results[0].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain_2 - - name: check previous task not changed - assert: - that: - - upload_chain_2.certificate.arn == upload_chain.certificate.arn - - not upload_chain_2.changed - - name: upload chained cert, different chain - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[1].cert ) }}' - certificate_chain: '{{ chains.results[1].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain_3 - - name: check uploading with different chain is changed - assert: - that: - - upload_chain_3.changed - - upload_chain_3.certificate.arn == upload_chain.certificate.arn - - name: fetch info about chain of cert we just updated - acm_certificate_info: - tags: - Name: '{{ chained_cert.name }}' - register: check_chain_2 - - name: check chain of cert we just uploaded - assert: - that: - - (check_chain_2.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[1].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) - - (check_chain_2.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[1].cert ) | replace( ' ', '' ) | replace( '\n', '') ) - - name: delete chained cert - acm_certificate: - name_tag: '{{ chained_cert.name }}' - state: absent - register: delete_chain_3 - - name: check deletion of chained cert 3 is changed - assert: - that: - - delete_chain_3.changed - - upload_chain.certificate.arn in delete_chain_3.arns - always: - - name: delete first bunch of certificates - acm_certificate: - name_tag: '{{ item.name }}' - state: absent - with_items: '{{ local_certs }}' - ignore_errors: true - - name: delete chained cert - acm_certificate: - state: absent - name_tag: '{{ chained_cert.name }}' - ignore_errors: true - - name: deleting local directory with test artefacts - file: - path: '{{ remote_tmp_dir }}' - state: directory - ignore_errors: true diff --git a/tests/integration/targets/acm_certificate/tasks/main.yml b/tests/integration/targets/acm_certificate/tasks/main.yml deleted file mode 100644 index 5cc6d31a096..00000000000 --- a/tests/integration/targets/acm_certificate/tasks/main.yml +++ /dev/null @@ -1,583 +0,0 @@ -- name: AWS ACM integration test - module_defaults: - group/aws: - aws_region: '{{ aws_region }}' - access_key: '{{ aws_access_key }}' - secret_key: '{{ aws_secret_key }}' - session_token: '{{ security_token | default(omit) }}' - block: - # The CI runs many of these tests in parallel - # Use this random ID to differentiate which resources - # are from which test - - set_fact: - aws_acm_test_uuid: "{{ (10**9) | random }}" - - name: attempt to delete cert without specifying required parameter - acm_certificate: - state: absent - register: result - ignore_errors: true - - name: assert failure when name_tag conflicts with tags.Name - assert: - that: - - 'result.failed' - - '"If ''state'' is specified as ''absent'' then exactly one of ''name_tag''" in result.msg' - - name: list certs - acm_certificate_info: null - register: list_all - failed_when: list_all.certificates is not defined - - name: ensure absent cert which doesn't exist - first time - acm_certificate: - name_tag: '{{ item.name }}' - state: absent - with_items: '{{ local_certs }}' - - name: ensure absent cert which doesn't exist - second time - acm_certificate: - name_tag: '{{ item.name }}' - state: absent - with_items: '{{ local_certs }}' - register: absent_start_two - failed_when: absent_start_two.changed - - name: list cert which shouldn't exist - acm_certificate_info: - tags: - Name: '{{ item.name }}' - register: list_tag - with_items: '{{ local_certs }}' - failed_when: list_tag.certificates | length > 0 - - name: check directory was made - assert: - that: - - remote_tmp_dir is defined - - name: Generate private key for local certs - with_items: '{{ local_certs }}' - community.crypto.openssl_privatekey: - path: '{{ item.priv_key }}' - type: RSA - size: 2048 - - name: Generate an OpenSSL Certificate Signing Request for own certs - with_items: '{{ local_certs }}' - community.crypto.openssl_csr: - path: '{{ item.csr }}' - privatekey_path: '{{ item.priv_key }}' - common_name: '{{ item.domain }}' - - name: Generate a Self Signed OpenSSL certificate for own certs - with_items: '{{ local_certs }}' - community.crypto.x509_certificate: - provider: selfsigned - path: '{{ item.cert }}' - csr_path: '{{ item.csr }}' - privatekey_path: '{{ item.priv_key }}' - selfsigned_digest: sha256 - - name: try to upload certificate, but name_tag conflicts with tags.Name - vars: - local_cert: '{{ local_certs[0] }}' - acm_certificate: - name_tag: '{{ local_cert.name }}' - certificate: '{{ lookup(''file'', local_cert.cert ) }}' - private_key: '{{ lookup(''file'', local_cert.priv_key ) }}' - state: present - tags: - Name: '{{ local_cert.name }}-other' - Application: search - Environment: development - register: result - ignore_errors: true - - name: assert failure when name_tag conflicts with tags.Name - assert: - that: - - 'result.failed' - - '"conflicts with value of" in result.msg' - - name: upload certificates first time - acm_certificate: - name_tag: '{{ item.name }}' - certificate: '{{ lookup(''file'', item.cert ) }}' - private_key: '{{ lookup(''file'', item.priv_key ) }}' - state: present - # Add tags - tags: - Application: search - Environment: development - purge_tags: false - register: upload - with_items: '{{ local_certs }}' - until: upload is succeeded - retries: 5 - delay: 10 - - assert: - that: - - prev_task.certificate.arn is defined - - ('arn:aws:acm:123' | regex_search( 'arn:aws:acm:' )) is defined - - (prev_task.certificate.arn | regex_search( 'arn:aws:acm:' )) is defined - - prev_task.certificate.domain_name == original_cert.domain - - prev_task.changed - with_items: '{{ upload.results }}' - vars: - original_cert: '{{ item.item }}' - prev_task: '{{ item }}' - - name: fetch data about cert just uploaded, by ARN - acm_certificate_info: - certificate_arn: '{{ item.certificate.arn }}' - register: fetch_after_up - with_items: '{{ upload.results }}' - - name: check output of prior task (fetch data about cert just uploaded, by ARN) - assert: - that: - - fetch_after_up_result.certificates | length == 1 - - fetch_after_up_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup( 'file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '' )) - - '''Name'' in fetch_after_up_result.certificates[0].tags' - - '''Application'' in fetch_after_up_result.certificates[0].tags' - - '''Environment'' in fetch_after_up_result.certificates[0].tags' - - fetch_after_up_result.certificates[0].tags['Name'] == original_cert.name - - fetch_after_up_result.certificates[0].tags['Application'] == 'search' - - fetch_after_up_result.certificates[0].tags['Environment'] == 'development' - with_items: '{{ fetch_after_up.results }}' - vars: - fetch_after_up_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: fetch data about cert just uploaded, by name - acm_certificate_info: - tags: - Name: '{{ original_cert.name }}' - register: fetch_after_up_name - with_items: '{{ upload.results }}' - vars: - upload_result: '{{ item }}' - original_cert: '{{ item.item }}' - - name: check fetched data of cert we just uploaded - assert: - that: - - fetch_after_up_name_result.certificates | length == 1 - - fetch_after_up_name_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_name_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_name_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_up_name_result.certificates[0].tags' - - fetch_after_up_name_result.certificates[0].tags['Name'] == original_cert.name - with_items: '{{ fetch_after_up_name.results }}' - vars: - fetch_after_up_name_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: fetch data about cert just uploaded, by domain name - acm_certificate_info: - domain_name: '{{ original_cert.domain }}' - register: fetch_after_up_domain - with_items: '{{ upload.results }}' - vars: - original_cert: '{{ item.item }}' - - name: compare fetched data of cert just uploaded to upload task - assert: - that: - - fetch_after_up_domain_result.certificates | length == 1 - - fetch_after_up_domain_result.certificates[0].certificate_arn == upload_result.certificate.arn - - fetch_after_up_domain_result.certificates[0].domain_name == original_cert.domain - - (fetch_after_up_domain_result.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', original_cert.cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_up_domain_result.certificates[0].tags' - - fetch_after_up_domain_result.certificates[0].tags['Name'] == original_cert.name - with_items: '{{ fetch_after_up_domain.results }}' - vars: - fetch_after_up_domain_result: '{{ item }}' - upload_result: '{{ item.item }}' - original_cert: '{{ item.item.item }}' - - name: upload certificates again, check not changed - acm_certificate: - name_tag: '{{ item.name }}' - certificate: '{{ lookup(''file'', item.cert ) }}' - private_key: '{{ lookup(''file'', item.priv_key ) }}' - state: present - register: upload2 - with_items: '{{ local_certs }}' - failed_when: upload2.changed - - name: change tags of existing certificate, check mode - acm_certificate: - certificate_arn: '{{ certificate_arn }}' - tags: - Name: '{{ name_tag }}' - Application: search - Environment: staging - Owner: Bob - register: certificate_with_tags - check_mode: true - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - domain_name: '{{ upload2.results[0].certificate.domain_name }}' - - assert: - that: - - certificate_with_tags.changed - - name: change tags of existing certificate, changes expected - acm_certificate: - # When applying tags to an existing certificate, it is sufficient to specify the 'certificate_arn'. - # Previously, the 'aws_acm' module was requiring the 'certificate', 'name_tag' and 'domain_name' - # attributes. - certificate_arn: '{{ certificate_arn }}' - tags: - Name: '{{ name_tag }}' - Application: search - Environment: staging - Owner: Bob - register: certificate_with_tags - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - domain_name: '{{ upload2.results[0].certificate.domain_name }}' - - name: assert certificate tags - assert: - that: - - certificate_with_tags.certificate.tags | length == 4 - - '''Name'' in certificate_with_tags.certificate.tags' - - '''Application'' in certificate_with_tags.certificate.tags' - - '''Environment'' in certificate_with_tags.certificate.tags' - - '''Owner'' in certificate_with_tags.certificate.tags' - - certificate_with_tags.certificate.tags['Name'] == name_tag - - certificate_with_tags.certificate.tags['Application'] == 'search' - - certificate_with_tags.certificate.tags['Environment'] == 'staging' - - certificate_with_tags.certificate.tags['Owner'] == 'Bob' - - certificate_with_tags.changed - vars: - name_tag: '{{ upload2.results[0].item.name }}' - - name: change tags of existing certificate, check mode again - acm_certificate: - certificate_arn: '{{ certificate_arn }}' - tags: - Name: '{{ name_tag }}' - Application: search - Environment: staging - Owner: Bob - register: certificate_with_tags - check_mode: true - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - - assert: - that: - - not certificate_with_tags.changed - - name: change tags of existing certificate, no change expected - acm_certificate: - certificate_arn: '{{ certificate_arn }}' - tags: - Name: '{{ name_tag }}' - Application: search - Environment: staging - Owner: Bob - register: certificate_with_tags - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - - name: assert certificate tags - assert: - that: - - certificate_with_tags.certificate.tags | length == 4 - - '''Name'' in certificate_with_tags.certificate.tags' - - '''Application'' in certificate_with_tags.certificate.tags' - - '''Environment'' in certificate_with_tags.certificate.tags' - - '''Owner'' in certificate_with_tags.certificate.tags' - - certificate_with_tags.certificate.tags['Name'] == name_tag - - certificate_with_tags.certificate.tags['Application'] == 'search' - - certificate_with_tags.certificate.tags['Environment'] == 'staging' - - certificate_with_tags.certificate.tags['Owner'] == 'Bob' - - not certificate_with_tags.changed - vars: - name_tag: '{{ upload2.results[0].item.name }}' - - name: check fetched data of cert we just uploaded - vars: - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - domain_name: '{{ upload2.results[0].certificate.domain_name }}' - name_tag: '{{ upload2.results[0].item.name }}' - assert: - that: - - certificate_with_tags.certificate.arn == certificate_arn - - certificate_with_tags.certificate.tags | length == 4 - - '''Name'' in certificate_with_tags.certificate.tags' - - '''Application'' in certificate_with_tags.certificate.tags' - - '''Environment'' in certificate_with_tags.certificate.tags' - - '''Owner'' in certificate_with_tags.certificate.tags' - - certificate_with_tags.certificate.tags['Name'] == name_tag - - certificate_with_tags.certificate.tags['Application'] == 'search' - - certificate_with_tags.certificate.tags['Environment'] == 'staging' - - certificate_with_tags.certificate.tags['Owner'] == 'Bob' - - name: change tags of existing certificate, purge tags - acm_certificate: - certificate_arn: '{{ certificate_arn }}' - tags: - Name: '{{ name_tag }}' - Application: search - Environment: staging - # 'Owner' tag should be removed because 'purge_tags: true' - purge_tags: true - register: certificate_with_tags - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - domain_name: '{{ upload2.results[0].certificate.domain_name }}' - - name: check fetched data of cert we just uploaded - vars: - name_tag: '{{ upload2.results[0].item.name }}' - certificate_arn: '{{ upload2.results[0].certificate.arn }}' - domain_name: '{{ upload2.results[0].certificate.domain_name }}' - assert: - that: - - certificate_with_tags.certificate.arn == certificate_arn - - certificate_with_tags.certificate.tags | length == 3 - - '''Name'' in certificate_with_tags.certificate.tags' - - '''Application'' in certificate_with_tags.certificate.tags' - - '''Environment'' in certificate_with_tags.certificate.tags' - - certificate_with_tags.certificate.tags['Name'] == name_tag - - certificate_with_tags.certificate.tags['Application'] == 'search' - - certificate_with_tags.certificate.tags['Environment'] == 'staging' - - name: update first cert with body of the second, first time - acm_certificate: - state: present - name_tag: '{{ local_certs[0].name }}' - certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' - private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' - register: overwrite - - name: check output of previous task (update first cert with body of the second, first time) - assert: - that: - - overwrite.certificate.arn is defined - - overwrite.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined - - overwrite.certificate.arn == upload.results[0].certificate.arn - - overwrite.certificate.domain_name == local_certs[1].domain - - overwrite.changed - - name: check update was sucessfull - acm_certificate_info: - tags: - Name: '{{ local_certs[0].name }}' - register: fetch_after_overwrite - - name: check output of update fetch - assert: - that: - - fetch_after_overwrite.certificates | length == 1 - - fetch_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[0].certificates[0].certificate_arn - - fetch_after_overwrite.certificates[0].domain_name == local_certs[1].domain - - (fetch_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert )| replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in fetch_after_overwrite.certificates[0].tags' - - fetch_after_overwrite.certificates[0].tags['Name'] == local_certs[0].name - - name: fetch other cert - acm_certificate_info: - tags: - Name: '{{ local_certs[1].name }}' - register: check_after_overwrite - - name: check other cert unaffected - assert: - that: - - check_after_overwrite.certificates | length == 1 - - check_after_overwrite.certificates[0].certificate_arn == fetch_after_up.results[1].certificates[0].certificate_arn - - check_after_overwrite.certificates[0].domain_name == local_certs[1].domain - - (check_after_overwrite.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == (lookup('file', local_certs[1].cert ) | replace( ' ', '' ) | replace( '\n', '')) - - '''Name'' in check_after_overwrite.certificates[0].tags' - - check_after_overwrite.certificates[0].tags['Name'] == local_certs[1].name - - name: update first cert with body of the second again - acm_certificate: - state: present - name_tag: '{{ local_certs[0].name }}' - certificate: '{{ lookup(''file'', local_certs[1].cert ) }}' - private_key: '{{ lookup(''file'', local_certs[1].priv_key ) }}' - register: overwrite2 - - name: check output of previous task (update first cert with body of the second again) - assert: - that: - - overwrite2.certificate.arn is defined - - overwrite2.certificate.arn | regex_search( 'arn:aws:acm:' ) is defined - - overwrite2.certificate.arn == upload.results[0].certificate.arn - - overwrite2.certificate.domain_name == local_certs[1].domain - - not overwrite2.changed - - name: delete certs 1 and 2 - acm_certificate: - state: absent - domain_name: '{{ local_certs[1].domain }}' - register: delete_both - - name: test prev task - assert: - that: - - delete_both.arns is defined - - check_after_overwrite.certificates[0].certificate_arn in delete_both.arns - - upload.results[0].certificate.arn in delete_both.arns - - delete_both.changed - - name: fetch info for certs 1 and 2 - acm_certificate_info: - tags: - Name: '{{ local_certs[item].name }}' - register: check_del_one - with_items: - - 0 - - 1 - retries: 2 - until: - - check_del_one is not failed - - check_del_one.certificates | length == 0 - delay: 10 - - name: check certs 1 and 2 were already deleted - with_items: '{{ check_del_one.results }}' - assert: - that: item.certificates | length == 0 - - name: check cert 3 not deleted - acm_certificate_info: - tags: - Name: '{{ local_certs[2].name }}' - register: check_del_one_remain - failed_when: check_del_one_remain.certificates | length != 1 - - name: delete cert 3 - acm_certificate: - state: absent - domain_name: '{{ local_certs[2].domain }}' - register: delete_third - - name: check cert 3 deletion went as expected - assert: - that: - - delete_third.arns is defined - - delete_third.arns | length == 1 - - delete_third.arns[0] == upload.results[2].certificate.arn - - delete_third.changed - - name: check cert 3 was deleted - acm_certificate_info: - tags: - Name: '{{ local_certs[2].name }}' - register: check_del_three - failed_when: check_del_three.certificates | length != 0 - - name: delete cert 3 again - acm_certificate: - state: absent - domain_name: '{{ local_certs[2].domain }}' - register: delete_third - - name: check deletion of cert 3 not changed, because already deleted - assert: - that: - - delete_third.arns is defined - - delete_third.arns | length == 0 - - not delete_third.changed - - name: check directory was made - assert: - that: - - remote_tmp_dir is defined - - name: Generate private key for cert to be chained - community.crypto.openssl_privatekey: - path: '{{ chained_cert.priv_key }}' - type: RSA - size: 2048 - - name: Generate two OpenSSL Certificate Signing Requests for cert to be chained - with_items: '{{ chained_cert.chains }}' - community.crypto.openssl_csr: - path: '{{ item.csr }}' - privatekey_path: '{{ chained_cert.priv_key }}' - common_name: '{{ chained_cert.domain }}' - - name: Sign new certs with cert 0 and 1 - with_items: '{{ chained_cert.chains }}' - community.crypto.x509_certificate: - provider: ownca - path: '{{ item.cert }}' - csr_path: '{{ item.csr }}' - ownca_path: '{{ local_certs[item.ca].cert }}' - ownca_privatekey_path: '{{ local_certs[item.ca].priv_key }}' - selfsigned_digest: sha256 - - name: check files exist (for next task) - file: - path: '{{ item }}' - state: file - with_items: - - '{{ local_certs[chained_cert.chains[0].ca].cert }}' - - '{{ local_certs[chained_cert.chains[1].ca].cert }}' - - '{{ chained_cert.chains[0].cert }}' - - '{{ chained_cert.chains[1].cert }}' - - name: Find chains - with_items: '{{ chained_cert.chains }}' - register: chains - community.crypto.certificate_complete_chain: - input_chain: '{{ lookup(''file'', item.cert ) }}' - root_certificates: - - '{{ local_certs[item.ca].cert }}' - - name: upload chained cert, first chain, first time - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' - certificate_chain: '{{ chains.results[0].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain - failed_when: not upload_chain.changed - - name: fetch chain of cert we just uploaded - acm_certificate_info: - tags: - Name: '{{ chained_cert.name }}' - register: check_chain - until: check_chain.certificates|length > 0 - retries: 3 - - name: check chain of cert we just uploaded - assert: - that: - - (check_chain.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[0].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) - - (check_chain.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[0].cert ) | replace( ' ', '' ) | replace( '\n', '') ) - - name: upload chained cert again, check not changed - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[0].cert ) }}' - certificate_chain: '{{ chains.results[0].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain_2 - - name: check previous task not changed - assert: - that: - - upload_chain_2.certificate.arn == upload_chain.certificate.arn - - not upload_chain_2.changed - - name: upload chained cert, different chain - acm_certificate: - name_tag: '{{ chained_cert.name }}' - certificate: '{{ lookup(''file'', chained_cert.chains[1].cert ) }}' - certificate_chain: '{{ chains.results[1].complete_chain | join('' - - '') }}' - private_key: '{{ lookup(''file'', chained_cert.priv_key ) }}' - state: present - register: upload_chain_3 - - name: check uploading with different chain is changed - assert: - that: - - upload_chain_3.changed - - upload_chain_3.certificate.arn == upload_chain.certificate.arn - - name: fetch info about chain of cert we just updated - acm_certificate_info: - tags: - Name: '{{ chained_cert.name }}' - register: check_chain_2 - until: check_chain_2.certificates|length > 0 - retries: 3 - - name: check chain of cert we just uploaded - assert: - that: - - (check_chain_2.certificates[0].certificate_chain | replace( ' ', '' ) | replace( '\n', '')) == ( chains.results[1].complete_chain | join( '\n' ) | replace( ' ', '' ) | replace( '\n', '') ) - - (check_chain_2.certificates[0].certificate | replace( ' ', '' ) | replace( '\n', '')) == ( lookup('file', chained_cert.chains[1].cert ) | replace( ' ', '' ) | replace( '\n', '') ) - - name: delete chained cert - acm_certificate: - name_tag: '{{ chained_cert.name }}' - state: absent - register: delete_chain_3 - - name: check deletion of chained cert 3 is changed - assert: - that: - - delete_chain_3.changed - - upload_chain.certificate.arn in delete_chain_3.arns - always: - - name: delete first bunch of certificates - acm_certificate: - name_tag: '{{ item.name }}' - state: absent - with_items: '{{ local_certs }}' - ignore_errors: true - - name: delete chained cert - acm_certificate: - state: absent - name_tag: '{{ chained_cert.name }}' - ignore_errors: true - - name: deleting local directory with test artefacts - file: - path: '{{ remote_tmp_dir }}' - state: directory - ignore_errors: true