|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +# Copyright: Contributors to the Ansible project |
| 4 | +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) |
| 5 | + |
| 6 | +from typing import Any |
| 7 | +from typing import Dict |
| 8 | +from typing import List |
| 9 | + |
| 10 | +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code |
| 11 | +from ansible_collections.amazon.aws.plugins.module_utils.errors import AWSErrorHandler |
| 12 | +from ansible_collections.amazon.aws.plugins.module_utils.exceptions import AnsibleAWSError |
| 13 | +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule |
| 14 | +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry |
| 15 | +from ansible_collections.amazon.aws.plugins.module_utils.tagging import ansible_dict_to_boto3_tag_list |
| 16 | +from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags |
| 17 | + |
| 18 | + |
| 19 | +class AnsibleSSMError(AnsibleAWSError): |
| 20 | + pass |
| 21 | + |
| 22 | + |
| 23 | +# SSM Documents |
| 24 | +class SSMDocumentErrorHandler(AWSErrorHandler): |
| 25 | + _CUSTOM_EXCEPTION = AnsibleSSMError |
| 26 | + |
| 27 | + @classmethod |
| 28 | + def _is_missing(cls): |
| 29 | + return is_boto3_error_code("InvalidDocument") |
| 30 | + |
| 31 | + |
| 32 | +@SSMDocumentErrorHandler.deletion_error_handler("delete document") |
| 33 | +@AWSRetry.jittered_backoff() |
| 34 | +def delete_document(client, name: str, **kwargs: Dict[str, Any]) -> bool: |
| 35 | + client.delete_document(Name=name, **kwargs) |
| 36 | + return True |
| 37 | + |
| 38 | + |
| 39 | +@SSMDocumentErrorHandler.list_error_handler("describe document", {}) |
| 40 | +@AWSRetry.jittered_backoff() |
| 41 | +def describe_document(client, name: str, **params: Dict[str, str]) -> Dict[str, Any]: |
| 42 | + return client.describe_document(Name=name, **params)["Document"] |
| 43 | + |
| 44 | + |
| 45 | +@SSMDocumentErrorHandler.common_error_handler("create document") |
| 46 | +@AWSRetry.jittered_backoff() |
| 47 | +def create_document(client, name: str, content: str, **params: Dict[str, Any]) -> Dict[str, Any]: |
| 48 | + return client.create_document(Name=name, Content=content, **params)["DocumentDescription"] |
| 49 | + |
| 50 | + |
| 51 | +@SSMDocumentErrorHandler.common_error_handler("update document") |
| 52 | +@AWSRetry.jittered_backoff() |
| 53 | +def update_document(client, name: str, content: str, **params: Dict[str, Any]) -> Dict[str, Any]: |
| 54 | + return client.update_document(Name=name, Content=content, **params)["DocumentDescription"] |
| 55 | + |
| 56 | + |
| 57 | +@SSMDocumentErrorHandler.common_error_handler("update document default version") |
| 58 | +@AWSRetry.jittered_backoff() |
| 59 | +def update_document_default_version(client, name: str, default_version: str) -> Dict[str, Any]: |
| 60 | + return client.update_document_default_version(Name=name, DocumentVersion=default_version) |
| 61 | + |
| 62 | + |
| 63 | +@SSMDocumentErrorHandler.list_error_handler("list documents", {}) |
| 64 | +@AWSRetry.jittered_backoff() |
| 65 | +def list_documents(client, **kwargs: Dict[str, Any]) -> List[Dict[str, Any]]: |
| 66 | + paginator = client.get_paginator("list_documents") |
| 67 | + return paginator.paginate(**kwargs).build_full_result()["DocumentIdentifiers"] |
| 68 | + |
| 69 | + |
| 70 | +@SSMDocumentErrorHandler.list_error_handler("list document versions", {}) |
| 71 | +@AWSRetry.jittered_backoff() |
| 72 | +def list_document_versions(ssm: Any, name: str) -> List[Dict[str, Any]]: |
| 73 | + paginator = ssm.get_paginator("list_document_versions") |
| 74 | + return paginator.paginate(Name=name).build_full_result()["DocumentVersions"] |
| 75 | + |
| 76 | + |
| 77 | +# Tags |
| 78 | +def add_tags_to_resource(client, resource_type: str, resource_id: str, tags: List[Dict[str, Any]]) -> None: |
| 79 | + client.add_tags_to_resource(ResourceType=resource_type, ResourceId=resource_id, Tags=tags) |
| 80 | + |
| 81 | + |
| 82 | +def remove_tags_from_resource(client, resource_type: str, resource_id: str, tag_keys: List[str]) -> None: |
| 83 | + client.remove_tags_from_resource(ResourceType=resource_type, ResourceId=resource_id, TagKeys=tag_keys) |
| 84 | + |
| 85 | + |
| 86 | +def ensure_ssm_resource_tags( |
| 87 | + client, module: AnsibleAWSModule, current_tags: Dict[str, str], resource_id: str, resource_type: str |
| 88 | +) -> bool: |
| 89 | + """Update resources tags""" |
| 90 | + tags = module.params.get("tags") |
| 91 | + purge_tags = module.params.get("purge_tags") |
| 92 | + tags_to_set, tags_to_unset = compare_aws_tags(current_tags, tags, purge_tags) |
| 93 | + |
| 94 | + if purge_tags and not tags: |
| 95 | + tags_to_unset = current_tags |
| 96 | + |
| 97 | + changed = False |
| 98 | + if tags_to_set: |
| 99 | + changed = True |
| 100 | + if not module.check_mode: |
| 101 | + add_tags_to_resource( |
| 102 | + client, |
| 103 | + resource_type=resource_type, |
| 104 | + resource_id=resource_id, |
| 105 | + tags=ansible_dict_to_boto3_tag_list(tags_to_set), |
| 106 | + ) |
| 107 | + if tags_to_unset: |
| 108 | + changed = True |
| 109 | + if not module.check_mode: |
| 110 | + remove_tags_from_resource( |
| 111 | + client, resource_type=resource_type, resource_id=resource_id, tag_keys=tags_to_unset |
| 112 | + ) |
| 113 | + return changed |
0 commit comments