From 8a3ef4878aa0cdce7618a774b032e0f3691f2735 Mon Sep 17 00:00:00 2001 From: GomathiselviS Date: Fri, 23 Sep 2022 03:44:14 -0400 Subject: [PATCH] Promote kms_key* modules and tests (#1520) Promote kms_key* modules and tests SUMMARY Remove kms_key* modules and tests These modules have been migrated to amazon.aws Update runtime.yml with redirects to that collection Update ignore files ISSUE TYPE Bugfix Pull Request Docs Pull Request Feature Pull Request New Module Pull Request COMPONENT NAME ADDITIONAL INFORMATION Reviewed-by: Mark Chappell --- kms_key.py | 993 ------------------------------------------------ kms_key_info.py | 525 ------------------------- 2 files changed, 1518 deletions(-) delete mode 100644 kms_key.py delete mode 100644 kms_key_info.py diff --git a/kms_key.py b/kms_key.py deleted file mode 100644 index 2f664bbafff..00000000000 --- a/kms_key.py +++ /dev/null @@ -1,993 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -* -# 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 - - -DOCUMENTATION = r''' ---- -module: kms_key -version_added: 1.0.0 -short_description: Perform various KMS key management tasks -description: - - Manage role/user access to a KMS key. - - Not designed for encrypting/decrypting. - - Prior to release 5.0.0 this module was called C(community.aws.aws_kms). - The usage did not change. -options: - alias: - description: - - An alias for a key. - - For safety, even though KMS does not require keys to have an alias, this module expects all - new keys to be given an alias to make them easier to manage. Existing keys without an alias - may be referred to by I(key_id). Use M(community.aws.kms_key_info) to find key ids. - - Note that passing a I(key_id) and I(alias) will only cause a new alias to be added, an alias will never be renamed. - - The C(alias/) prefix is optional. - - Required if I(key_id) is not given. - required: false - aliases: - - key_alias - type: str - key_id: - description: - - Key ID or ARN of the key. - - One of I(alias) or I(key_id) are required. - required: false - aliases: - - key_arn - type: str - enable_key_rotation: - description: - - Whether the key should be automatically rotated every year. - required: false - type: bool - state: - description: - - Whether a key should be present or absent. - - Note that making an existing key C(absent) only schedules a key for deletion. - - Passing a key that is scheduled for deletion with I(state=present) will cancel key deletion. - required: False - choices: - - present - - absent - default: present - type: str - enabled: - description: Whether or not a key is enabled. - default: True - type: bool - description: - description: - - A description of the CMK. - - Use a description that helps you decide whether the CMK is appropriate for a task. - type: str - pending_window: - description: - - The number of days between requesting deletion of the CMK and when it will actually be deleted. - - Only used when I(state=absent) and the CMK has not yet been deleted. - - Valid values are between 7 and 30 (inclusive). - - 'See also: U(https://docs.aws.amazon.com/kms/latest/APIReference/API_ScheduleKeyDeletion.html#KMS-ScheduleKeyDeletion-request-PendingWindowInDays)' - type: int - aliases: ['deletion_delay'] - version_added: 1.4.0 - purge_grants: - description: - - Whether the I(grants) argument should cause grants not in the list to be removed. - default: False - type: bool - grants: - description: - - A list of grants to apply to the key. Each item must contain I(grantee_principal). - Each item can optionally contain I(retiring_principal), I(operations), I(constraints), - I(name). - - I(grantee_principal) and I(retiring_principal) must be ARNs - - 'For full documentation of suboptions see the boto3 documentation:' - - 'U(https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html#KMS.Client.create_grant)' - type: list - elements: dict - suboptions: - grantee_principal: - description: The full ARN of the principal being granted permissions. - required: true - type: str - retiring_principal: - description: The full ARN of the principal permitted to revoke/retire the grant. - type: str - operations: - type: list - elements: str - description: - - A list of operations that the grantee may perform using the CMK. - choices: ['Decrypt', 'Encrypt', 'GenerateDataKey', 'GenerateDataKeyWithoutPlaintext', 'ReEncryptFrom', 'ReEncryptTo', - 'CreateGrant', 'RetireGrant', 'DescribeKey', 'Verify', 'Sign'] - constraints: - description: - - Constraints is a dict containing C(encryption_context_subset) or C(encryption_context_equals), - either or both being a dict specifying an encryption context match. - See U(https://docs.aws.amazon.com/kms/latest/APIReference/API_GrantConstraints.html) or - U(https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html#KMS.Client.create_grant) - type: dict - policy: - description: - - policy to apply to the KMS key. - - See U(https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html) - type: json - key_spec: - aliases: - - customer_master_key_spec - description: - - Specifies the type of KMS key to create. - - The specification is not changeable once the key is created. - type: str - default: SYMMETRIC_DEFAULT - choices: ['SYMMETRIC_DEFAULT', 'RSA_2048', 'RSA_3072', 'RSA_4096', 'ECC_NIST_P256', 'ECC_NIST_P384', 'ECC_NIST_P521', 'ECC_SECG_P256K1'] - version_added: 2.1.0 - key_usage: - description: - - Determines the cryptographic operations for which you can use the KMS key. - - The usage is not changeable once the key is created. - type: str - default: ENCRYPT_DECRYPT - choices: ['ENCRYPT_DECRYPT', 'SIGN_VERIFY'] - version_added: 2.1.0 -author: - - Ted Timmons (@tedder) - - Will Thames (@willthames) - - Mark Chappell (@tremble) -extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 - - amazon.aws.tags - -notes: - - There are known inconsistencies in the amount of time required for updates of KMS keys to be fully reflected on AWS. - This can cause issues when running duplicate tasks in succession or using the M(community.aws.kms_key_info) module to fetch key metadata - shortly after modifying keys. - For this reason, it is recommended to use the return data from this module (M(community.aws.kms_key)) to fetch a key's metadata. -''' - -EXAMPLES = r''' -# Create a new KMS key -- community.aws.kms_key: - alias: mykey - tags: - Name: myKey - Purpose: protect_stuff - -# Update previous key with more tags -- community.aws.kms_key: - alias: mykey - tags: - Name: myKey - Purpose: protect_stuff - Owner: security_team - -# Update a known key with grants allowing an instance with the billing-prod IAM profile -# to decrypt data encrypted with the environment: production, application: billing -# encryption context -- community.aws.kms_key: - key_id: abcd1234-abcd-1234-5678-ef1234567890 - grants: - - name: billing_prod - grantee_principal: arn:aws:iam::1234567890123:role/billing_prod - constraints: - encryption_context_equals: - environment: production - application: billing - operations: - - Decrypt - - RetireGrant - -- name: Update IAM policy on an existing KMS key - community.aws.kms_key: - alias: my-kms-key - policy: '{"Version": "2012-10-17", "Id": "my-kms-key-permissions", "Statement": [ { } ]}' - state: present - -- name: Example using lookup for policy json - community.aws.kms_key: - alias: my-kms-key - policy: "{{ lookup('template', 'kms_iam_policy_template.json.j2') }}" - state: present -''' - -RETURN = r''' -key_id: - description: ID of key. - type: str - returned: always - sample: abcd1234-abcd-1234-5678-ef1234567890 -key_arn: - description: ARN of key. - type: str - returned: always - sample: arn:aws:kms:ap-southeast-2:123456789012:key/abcd1234-abcd-1234-5678-ef1234567890 -key_state: - description: - - The state of the key. - - Will be one of C('Creating'), C('Enabled'), C('Disabled'), C('PendingDeletion'), C('PendingImport'), - C('PendingReplicaDeletion'), C('Unavailable'), or C('Updating'). - type: str - returned: always - sample: PendingDeletion -key_usage: - description: The cryptographic operations for which you can use the key. - type: str - returned: always - sample: ENCRYPT_DECRYPT -origin: - description: The source of the key's key material. When this value is C(AWS_KMS), - AWS KMS created the key material. When this value is C(EXTERNAL), the - key material was imported or the CMK lacks key material. - type: str - returned: always - sample: AWS_KMS -aws_account_id: - description: The AWS Account ID that the key belongs to. - type: str - returned: always - sample: 1234567890123 -creation_date: - description: Date and time of creation of the key. - type: str - returned: always - sample: "2017-04-18T15:12:08.551000+10:00" -deletion_date: - description: Date and time after which KMS deletes this KMS key. - type: str - returned: when key_state is PendingDeletion - sample: "2017-04-18T15:12:08.551000+10:00" - version_added: 3.3.0 -description: - description: Description of the key. - type: str - returned: always - sample: "My Key for Protecting important stuff" -enabled: - description: Whether the key is enabled. True if I(key_state) is C(Enabled). - type: bool - returned: always - sample: false -enable_key_rotation: - description: Whether the automatic annual key rotation is enabled. Returns None if key rotation status can't be determined. - type: bool - returned: always - sample: false -aliases: - description: List of aliases associated with the key. - type: list - returned: always - sample: - - aws/acm - - aws/ebs -policies: - description: List of policy documents for the key. Empty when access is denied even if there are policies. - type: list - returned: always - elements: str - sample: - Version: "2012-10-17" - Id: "auto-ebs-2" - Statement: - - Sid: "Allow access through EBS for all principals in the account that are authorized to use EBS" - Effect: "Allow" - Principal: - AWS: "*" - Action: - - "kms:Encrypt" - - "kms:Decrypt" - - "kms:ReEncrypt*" - - "kms:GenerateDataKey*" - - "kms:CreateGrant" - - "kms:DescribeKey" - Resource: "*" - Condition: - StringEquals: - kms:CallerAccount: "111111111111" - kms:ViaService: "ec2.ap-southeast-2.amazonaws.com" - - Sid: "Allow direct access to key metadata to the account" - Effect: "Allow" - Principal: - AWS: "arn:aws:iam::111111111111:root" - Action: - - "kms:Describe*" - - "kms:Get*" - - "kms:List*" - - "kms:RevokeGrant" - Resource: "*" -key_policies: - description: List of policy documents for the key. Empty when access is denied even if there are policies. - type: list - returned: always - elements: dict - sample: - Version: "2012-10-17" - Id: "auto-ebs-2" - Statement: - - Sid: "Allow access through EBS for all principals in the account that are authorized to use EBS" - Effect: "Allow" - Principal: - AWS: "*" - Action: - - "kms:Encrypt" - - "kms:Decrypt" - - "kms:ReEncrypt*" - - "kms:GenerateDataKey*" - - "kms:CreateGrant" - - "kms:DescribeKey" - Resource: "*" - Condition: - StringEquals: - kms:CallerAccount: "111111111111" - kms:ViaService: "ec2.ap-southeast-2.amazonaws.com" - - Sid: "Allow direct access to key metadata to the account" - Effect: "Allow" - Principal: - AWS: "arn:aws:iam::111111111111:root" - Action: - - "kms:Describe*" - - "kms:Get*" - - "kms:List*" - - "kms:RevokeGrant" - Resource: "*" - version_added: 3.3.0 -tags: - description: Dictionary of tags applied to the key. Empty when access is denied even if there are tags. - type: dict - returned: always - sample: - Name: myKey - Purpose: protecting_stuff -grants: - description: List of grants associated with a key. - type: list - elements: dict - returned: always - contains: - constraints: - description: Constraints on the encryption context that the grant allows. - See U(https://docs.aws.amazon.com/kms/latest/APIReference/API_GrantConstraints.html) for further details - type: dict - returned: always - sample: - encryption_context_equals: - "aws:lambda:_function_arn": "arn:aws:lambda:ap-southeast-2:012345678912:function:xyz" - creation_date: - description: Date of creation of the grant. - type: str - returned: always - sample: "2017-04-18T15:12:08+10:00" - grant_id: - description: The unique ID for the grant. - type: str - returned: always - sample: abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234 - grantee_principal: - description: The principal that receives the grant's permissions. - type: str - returned: always - sample: arn:aws:sts::0123456789012:assumed-role/lambda_xyz/xyz - issuing_account: - description: The AWS account under which the grant was issued. - type: str - returned: always - sample: arn:aws:iam::01234567890:root - key_id: - description: The key ARN to which the grant applies. - type: str - returned: always - sample: arn:aws:kms:ap-southeast-2:123456789012:key/abcd1234-abcd-1234-5678-ef1234567890 - name: - description: The friendly name that identifies the grant. - type: str - returned: always - sample: xyz - operations: - description: The list of operations permitted by the grant. - type: list - returned: always - sample: - - Decrypt - - RetireGrant - retiring_principal: - description: The principal that can retire the grant. - type: str - returned: always - sample: arn:aws:sts::0123456789012:assumed-role/lambda_xyz/xyz -changes_needed: - description: Grant types that would be changed/were changed. - type: dict - returned: always - sample: { "role": "add", "role grant": "add" } -had_invalid_entries: - description: Whether there are invalid (non-ARN) entries in the KMS entry. These don't count as a change, but will be removed if any changes are being made. - type: bool - returned: always -''' - -# these mappings are used to go from simple labels to the actual 'Sid' values returned -# by get_policy. They seem to be magic values. -statement_label = { - 'role': 'Allow use of the key', - 'role grant': 'Allow attachment of persistent resources', - 'admin': 'Allow access for Key Administrators' -} - -import json - -try: - import botocore -except ImportError: - pass # caught by AnsibleAWSModule - -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_iam_roles_with_backoff(connection): - paginator = connection.get_paginator('list_roles') - return paginator.paginate().build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_keys_with_backoff(connection): - paginator = connection.get_paginator('list_keys') - return paginator.paginate().build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_aliases_with_backoff(connection): - paginator = connection.get_paginator('list_aliases') - return paginator.paginate().build_full_result() - - -def get_kms_aliases_lookup(connection): - _aliases = dict() - for alias in get_kms_aliases_with_backoff(connection)['Aliases']: - # Not all aliases are actually associated with a key - if 'TargetKeyId' in alias: - # strip off leading 'alias/' and add it to key's aliases - if alias['TargetKeyId'] in _aliases: - _aliases[alias['TargetKeyId']].append(alias['AliasName'][6:]) - else: - _aliases[alias['TargetKeyId']] = [alias['AliasName'][6:]] - return _aliases - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_tags_with_backoff(connection, key_id, **kwargs): - return connection.list_resource_tags(KeyId=key_id, **kwargs) - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_grants_with_backoff(connection, key_id): - params = dict(KeyId=key_id) - paginator = connection.get_paginator('list_grants') - return paginator.paginate(**params).build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_metadata_with_backoff(connection, key_id): - return connection.describe_key(KeyId=key_id) - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def list_key_policies_with_backoff(connection, key_id): - paginator = connection.get_paginator('list_key_policies') - return paginator.paginate(KeyId=key_id).build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_key_policy_with_backoff(connection, key_id, policy_name): - return connection.get_key_policy(KeyId=key_id, PolicyName=policy_name) - - -def get_kms_tags(connection, module, key_id): - # Handle pagination here as list_resource_tags does not have - # a paginator - kwargs = {} - tags = [] - more = True - while more: - try: - tag_response = get_kms_tags_with_backoff(connection, key_id, **kwargs) - tags.extend(tag_response['Tags']) - except is_boto3_error_code('AccessDeniedException'): - tag_response = {} - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key tags") - if tag_response.get('NextMarker'): - kwargs['Marker'] = tag_response['NextMarker'] - else: - more = False - return tags - - -def get_kms_policies(connection, module, key_id): - try: - policies = list_key_policies_with_backoff(connection, key_id)['PolicyNames'] - return [get_key_policy_with_backoff(connection, key_id, policy)['Policy'] for - policy in policies] - except is_boto3_error_code('AccessDeniedException'): - return [] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key policies") - - -def camel_to_snake_grant(grant): - ''' camel_to_snake_grant snakifies everything except the encryption context ''' - constraints = grant.get('Constraints', {}) - result = camel_dict_to_snake_dict(grant) - if 'EncryptionContextEquals' in constraints: - result['constraints']['encryption_context_equals'] = constraints['EncryptionContextEquals'] - if 'EncryptionContextSubset' in constraints: - result['constraints']['encryption_context_subset'] = constraints['EncryptionContextSubset'] - return result - - -def get_key_details(connection, module, key_id): - try: - result = get_kms_metadata_with_backoff(connection, key_id)['KeyMetadata'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to obtain key metadata") - result['KeyArn'] = result.pop('Arn') - - try: - aliases = get_kms_aliases_lookup(connection) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to obtain aliases") - - try: - current_rotation_status = connection.get_key_rotation_status(KeyId=key_id) - result['enable_key_rotation'] = current_rotation_status.get('KeyRotationEnabled') - except is_boto3_error_code(['AccessDeniedException', 'UnsupportedOperationException']) as e: - result['enable_key_rotation'] = None - result['aliases'] = aliases.get(result['KeyId'], []) - - result = camel_dict_to_snake_dict(result) - - # grants and tags get snakified differently - try: - result['grants'] = [camel_to_snake_grant(grant) for grant in - get_kms_grants_with_backoff(connection, key_id)['Grants']] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to obtain key grants") - tags = get_kms_tags(connection, module, key_id) - result['tags'] = boto3_tag_list_to_ansible_dict(tags, 'TagKey', 'TagValue') - result['policies'] = get_kms_policies(connection, module, key_id) - result['key_policies'] = [json.loads(policy) for policy in result['policies']] - return result - - -def get_kms_facts(connection, module): - try: - keys = get_kms_keys_with_backoff(connection)['Keys'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to obtain keys") - - return [get_key_details(connection, module, key['KeyId']) for key in keys] - - -def convert_grant_params(grant, key): - grant_params = dict(KeyId=key['key_arn'], - GranteePrincipal=grant['grantee_principal']) - if grant.get('operations'): - grant_params['Operations'] = grant['operations'] - if grant.get('retiring_principal'): - grant_params['RetiringPrincipal'] = grant['retiring_principal'] - if grant.get('name'): - grant_params['Name'] = grant['name'] - if grant.get('constraints'): - grant_params['Constraints'] = dict() - if grant['constraints'].get('encryption_context_subset'): - grant_params['Constraints']['EncryptionContextSubset'] = grant['constraints']['encryption_context_subset'] - if grant['constraints'].get('encryption_context_equals'): - grant_params['Constraints']['EncryptionContextEquals'] = grant['constraints']['encryption_context_equals'] - return grant_params - - -def different_grant(existing_grant, desired_grant): - if existing_grant.get('grantee_principal') != desired_grant.get('grantee_principal'): - return True - if existing_grant.get('retiring_principal') != desired_grant.get('retiring_principal'): - return True - if set(existing_grant.get('operations', [])) != set(desired_grant.get('operations')): - return True - if existing_grant.get('constraints') != desired_grant.get('constraints'): - return True - return False - - -def compare_grants(existing_grants, desired_grants, purge_grants=False): - existing_dict = dict((eg['name'], eg) for eg in existing_grants) - desired_dict = dict((dg['name'], dg) for dg in desired_grants) - to_add_keys = set(desired_dict.keys()) - set(existing_dict.keys()) - if purge_grants: - to_remove_keys = set(existing_dict.keys()) - set(desired_dict.keys()) - else: - to_remove_keys = set() - to_change_candidates = set(existing_dict.keys()) & set(desired_dict.keys()) - for candidate in to_change_candidates: - if different_grant(existing_dict[candidate], desired_dict[candidate]): - to_add_keys.add(candidate) - to_remove_keys.add(candidate) - - to_add = [] - to_remove = [] - for key in to_add_keys: - grant = desired_dict[key] - to_add.append(grant) - for key in to_remove_keys: - grant = existing_dict[key] - to_remove.append(grant) - return to_add, to_remove - - -def start_key_deletion(connection, module, key_metadata): - if key_metadata['KeyState'] == 'PendingDeletion': - return False - - if module.check_mode: - return True - - deletion_params = {'KeyId': key_metadata['Arn']} - if module.params.get('pending_window'): - deletion_params['PendingWindowInDays'] = module.params.get('pending_window') - - try: - connection.schedule_key_deletion(**deletion_params) - return True - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to schedule key for deletion") - - -def cancel_key_deletion(connection, module, key): - key_id = key['key_arn'] - if key['key_state'] != 'PendingDeletion': - return False - - if module.check_mode: - return True - - try: - connection.cancel_key_deletion(KeyId=key_id) - # key is disabled after deletion cancellation - # set this so that ensure_enabled_disabled works correctly - key['key_state'] = 'Disabled' - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to cancel key deletion") - - return True - - -def ensure_enabled_disabled(connection, module, key, enabled): - desired_state = 'Enabled' - if not enabled: - desired_state = 'Disabled' - - if key['key_state'] == desired_state: - return False - - key_id = key['key_arn'] - if not module.check_mode: - if enabled: - try: - connection.enable_key(KeyId=key_id) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to enable key") - else: - try: - connection.disable_key(KeyId=key_id) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to disable key") - - return True - - -def update_alias(connection, module, key, alias): - alias = canonicalize_alias_name(alias) - - if alias is None: - return False - - key_id = key['key_arn'] - aliases = get_kms_aliases_with_backoff(connection)['Aliases'] - # We will only add new aliases, not rename existing ones - if alias in [_alias['AliasName'] for _alias in aliases]: - return False - - if not module.check_mode: - try: - connection.create_alias(TargetKeyId=key_id, AliasName=alias) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed create key alias") - - return True - - -def update_description(connection, module, key, description): - if description is None: - return False - if key['description'] == description: - return False - - key_id = key['key_arn'] - if not module.check_mode: - try: - connection.update_key_description(KeyId=key_id, Description=description) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update key description") - - return True - - -def update_tags(connection, module, key, desired_tags, purge_tags): - if desired_tags is None: - return False - - to_add, to_remove = compare_aws_tags(key['tags'], desired_tags, purge_tags) - if not (bool(to_add) or bool(to_remove)): - return False - - key_id = key['key_arn'] - if not module.check_mode: - if to_remove: - try: - connection.untag_resource(KeyId=key_id, TagKeys=to_remove) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to remove tag") - if to_add: - try: - tags = ansible_dict_to_boto3_tag_list(module.params['tags'], tag_name_key_name='TagKey', tag_value_key_name='TagValue') - connection.tag_resource(KeyId=key_id, Tags=tags) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to add tag to key") - - return True - - -def update_policy(connection, module, key, policy): - if policy is None: - return False - try: - new_policy = json.loads(policy) - except ValueError as e: - module.fail_json_aws(e, msg="Unable to parse new policy as JSON") - - key_id = key['key_arn'] - try: - keyret = connection.get_key_policy(KeyId=key_id, PolicyName='default') - original_policy = json.loads(keyret['Policy']) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): - # If we can't fetch the current policy assume we're making a change - # Could occur if we have PutKeyPolicy without GetKeyPolicy - original_policy = {} - - if not compare_policies(original_policy, new_policy): - return False - - if not module.check_mode: - try: - connection.put_key_policy(KeyId=key_id, PolicyName='default', Policy=policy) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to update key policy") - - return True - - -def update_key_rotation(connection, module, key, enable_key_rotation): - if enable_key_rotation is None: - return False - key_id = key['key_arn'] - - try: - current_rotation_status = connection.get_key_rotation_status(KeyId=key_id) - if current_rotation_status.get('KeyRotationEnabled') == enable_key_rotation: - return False - except is_boto3_error_code('AccessDeniedException'): - pass - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Unable to get current key rotation status") - - if not module.check_mode: - try: - if enable_key_rotation: - connection.enable_key_rotation(KeyId=key_id) - else: - connection.disable_key_rotation(KeyId=key_id) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to enable/disable key rotation") - - return True - - -def update_grants(connection, module, key, desired_grants, purge_grants): - existing_grants = key['grants'] - - to_add, to_remove = compare_grants(existing_grants, desired_grants, purge_grants) - if not (bool(to_add) or bool(to_remove)): - return False - - key_id = key['key_arn'] - if not module.check_mode: - for grant in to_remove: - try: - connection.retire_grant(KeyId=key_id, GrantId=grant['grant_id']) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to retire grant") - for grant in to_add: - grant_params = convert_grant_params(grant, key) - try: - connection.create_grant(**grant_params) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Unable to create grant") - - return True - - -def update_key(connection, module, key): - changed = False - - changed |= cancel_key_deletion(connection, module, key) - changed |= ensure_enabled_disabled(connection, module, key, module.params['enabled']) - changed |= update_alias(connection, module, key, module.params['alias']) - changed |= update_description(connection, module, key, module.params['description']) - changed |= update_tags(connection, module, key, module.params['tags'], module.params.get('purge_tags')) - changed |= update_policy(connection, module, key, module.params.get('policy')) - changed |= update_grants(connection, module, key, module.params.get('grants'), module.params.get('purge_grants')) - changed |= update_key_rotation(connection, module, key, module.params.get('enable_key_rotation')) - - # make results consistent with kms_facts before returning - result = get_key_details(connection, module, key['key_arn']) - result['changed'] = changed - return result - - -def create_key(connection, module): - key_usage = module.params.get('key_usage') - key_spec = module.params.get('key_spec') - tags_list = ansible_dict_to_boto3_tag_list( - module.params['tags'] or {}, - # KMS doesn't use "Key" and "Value" as other APIs do. - tag_name_key_name='TagKey', tag_value_key_name='TagValue' - ) - params = dict(BypassPolicyLockoutSafetyCheck=False, - Tags=tags_list, - KeyUsage=key_usage, - CustomerMasterKeySpec=key_spec, - Origin='AWS_KMS') - - if module.check_mode: - return {'changed': True} - - if module.params.get('description'): - params['Description'] = module.params['description'] - if module.params.get('policy'): - params['Policy'] = module.params['policy'] - - try: - result = connection.create_key(**params)['KeyMetadata'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to create initial key") - - key = get_key_details(connection, module, result['KeyId']) - update_alias(connection, module, key, module.params['alias']) - update_key_rotation(connection, module, key, module.params.get('enable_key_rotation')) - - ensure_enabled_disabled(connection, module, key, module.params.get('enabled')) - update_grants(connection, module, key, module.params.get('grants'), False) - - # make results consistent with kms_facts - result = get_key_details(connection, module, key['key_id']) - result['changed'] = True - return result - - -def delete_key(connection, module, key_metadata): - changed = False - - changed |= start_key_deletion(connection, module, key_metadata) - - result = get_key_details(connection, module, key_metadata['Arn']) - result['changed'] = changed - return result - - -def get_arn_from_role_name(iam, rolename): - ret = iam.get_role(RoleName=rolename) - if ret.get('Role') and ret['Role'].get('Arn'): - return ret['Role']['Arn'] - raise Exception('could not find arn for name {0}.'.format(rolename)) - - -def canonicalize_alias_name(alias): - if alias is None: - return None - if alias.startswith('alias/'): - return alias - return 'alias/' + alias - - -def fetch_key_metadata(connection, module, key_id, alias): - # Note - fetching a key's metadata is very inconsistent shortly after any sort of update to a key has occurred. - # Combinations of manual waiters, checking expecting key values to actual key value, and static sleeps - # have all been exhausted, but none of those available options have solved the problem. - # Integration tests will wait for 10 seconds to combat this issue. - # See https://github.com/ansible-collections/community.aws/pull/1052. - - alias = canonicalize_alias_name(module.params.get('alias')) - - try: - # Fetch by key_id where possible - if key_id: - return get_kms_metadata_with_backoff(connection, key_id)['KeyMetadata'] - # Or try alias as a backup - return get_kms_metadata_with_backoff(connection, alias)['KeyMetadata'] - - except connection.exceptions.NotFoundException: - return None - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, 'Failed to fetch key metadata.') - - -def main(): - argument_spec = dict( - alias=dict(aliases=['key_alias']), - pending_window=dict(aliases=['deletion_delay'], type='int'), - key_id=dict(aliases=['key_arn']), - description=dict(), - enabled=dict(type='bool', default=True), - tags=dict(type='dict', aliases=['resource_tags']), - purge_tags=dict(type='bool', default=True), - grants=dict(type='list', default=[], elements='dict'), - policy=dict(type='json'), - purge_grants=dict(type='bool', default=False), - state=dict(default='present', choices=['present', 'absent']), - enable_key_rotation=(dict(type='bool')), - key_spec=dict(type='str', default='SYMMETRIC_DEFAULT', aliases=['customer_master_key_spec'], - choices=['SYMMETRIC_DEFAULT', 'RSA_2048', 'RSA_3072', 'RSA_4096', 'ECC_NIST_P256', 'ECC_NIST_P384', 'ECC_NIST_P521', 'ECC_SECG_P256K1']), - key_usage=dict(type='str', default='ENCRYPT_DECRYPT', choices=['ENCRYPT_DECRYPT', 'SIGN_VERIFY']), - ) - - module = AnsibleAWSModule( - supports_check_mode=True, - argument_spec=argument_spec, - required_one_of=[['alias', 'key_id']], - ) - - kms = module.client('kms') - - module.deprecate("The 'policies' return key is deprecated and will be replaced by 'key_policies'. Both values are returned for now.", - date='2024-05-01', collection_name='community.aws') - - key_metadata = fetch_key_metadata(kms, module, module.params.get('key_id'), module.params.get('alias')) - # We can't create keys with a specific ID, if we can't access the key we'll have to fail - if module.params.get('state') == 'present' and module.params.get('key_id') and not key_metadata: - module.fail_json(msg="Could not find key with id {0} to update".format(module.params.get('key_id'))) - - if module.params.get('state') == 'absent': - if key_metadata is None: - module.exit_json(changed=False) - result = delete_key(kms, module, key_metadata) - module.exit_json(**result) - - if key_metadata: - key_details = get_key_details(kms, module, key_metadata['Arn']) - result = update_key(kms, module, key_details) - module.exit_json(**result) - - result = create_key(kms, module) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/kms_key_info.py b/kms_key_info.py deleted file mode 100644 index 1ba01e50d61..00000000000 --- a/kms_key_info.py +++ /dev/null @@ -1,525 +0,0 @@ -#!/usr/bin/python -# -# 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 - - -DOCUMENTATION = r''' ---- -module: kms_key_info -version_added: 1.0.0 -short_description: Gather information about AWS KMS keys -description: - - Gather information about AWS KMS keys including tags and grants. - - Prior to release 5.0.0 this module was called C(community.aws.aws_kms_info). - The usage did not change. -author: - - "Will Thames (@willthames)" -options: - alias: - description: - - Alias for key. - - Mutually exclusive with I(key_id) and I(filters). - required: false - aliases: - - key_alias - type: str - version_added: 1.4.0 - key_id: - description: - - Key ID or ARN of the key. - - Mutually exclusive with I(alias) and I(filters). - required: false - aliases: - - key_arn - type: str - version_added: 1.4.0 - filters: - description: - - A dict of filters to apply. Each dict item consists of a filter key and a filter value. - The filters aren't natively supported by boto3, but are supported to provide similar - functionality to other modules. Standard tag filters (C(tag-key), C(tag-value) and - C(tag:tagName)) are available, as are C(key-id) and C(alias) - - Mutually exclusive with I(alias) and I(key_id). - type: dict - pending_deletion: - description: Whether to get full details (tags, grants etc.) of keys pending deletion. - default: False - type: bool -extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 -''' - -EXAMPLES = r''' -# Note: These examples do not set authentication details, see the AWS Guide for details. - -# Gather information about all KMS keys -- community.aws.kms_key_info: - -# Gather information about all keys with a Name tag -- community.aws.kms_key_info: - filters: - tag-key: Name - -# Gather information about all keys with a specific name -- community.aws.kms_key_info: - filters: - "tag:Name": Example -''' - -RETURN = r''' -kms_keys: - description: List of keys. - type: complex - returned: always - contains: - key_id: - description: ID of key. - type: str - returned: always - sample: abcd1234-abcd-1234-5678-ef1234567890 - key_arn: - description: ARN of key. - type: str - returned: always - sample: arn:aws:kms:ap-southeast-2:123456789012:key/abcd1234-abcd-1234-5678-ef1234567890 - key_state: - description: - - The state of the key. - - Will be one of C('Creating'), C('Enabled'), C('Disabled'), C('PendingDeletion'), C('PendingImport'), - C('PendingReplicaDeletion'), C('Unavailable'), or C('Updating'). - type: str - returned: always - sample: PendingDeletion - key_usage: - description: The cryptographic operations for which you can use the key. - type: str - returned: always - sample: ENCRYPT_DECRYPT - origin: - description: The source of the key's key material. When this value is C(AWS_KMS), - AWS KMS created the key material. When this value is C(EXTERNAL), the - key material was imported or the CMK lacks key material. - type: str - returned: always - sample: AWS_KMS - aws_account_id: - description: The AWS Account ID that the key belongs to. - type: str - returned: always - sample: 1234567890123 - creation_date: - description: Date and time of creation of the key. - type: str - returned: always - sample: "2017-04-18T15:12:08.551000+10:00" - deletion_date: - description: Date and time after which KMS deletes this KMS key. - type: str - returned: when key_state is PendingDeletion - sample: "2017-04-18T15:12:08.551000+10:00" - version_added: 3.3.0 - description: - description: Description of the key. - type: str - returned: always - sample: "My Key for Protecting important stuff" - enabled: - description: Whether the key is enabled. True if I(key_state) is C(Enabled). - type: bool - returned: always - sample: false - enable_key_rotation: - description: Whether the automatic annual key rotation is enabled. Returns None if key rotation status can't be determined. - type: bool - returned: always - sample: false - aliases: - description: list of aliases associated with the key. - type: list - returned: always - sample: - - aws/acm - - aws/ebs - tags: - description: Dictionary of tags applied to the key. Empty when access is denied even if there are tags. - type: dict - returned: always - sample: - Name: myKey - Purpose: protecting_stuff - policies: - description: List of policy documents for the key. Empty when access is denied even if there are policies. - type: list - returned: always - elements: str - sample: - Version: "2012-10-17" - Id: "auto-ebs-2" - Statement: - - Sid: "Allow access through EBS for all principals in the account that are authorized to use EBS" - Effect: "Allow" - Principal: - AWS: "*" - Action: - - "kms:Encrypt" - - "kms:Decrypt" - - "kms:ReEncrypt*" - - "kms:GenerateDataKey*" - - "kms:CreateGrant" - - "kms:DescribeKey" - Resource: "*" - Condition: - StringEquals: - kms:CallerAccount: "111111111111" - kms:ViaService: "ec2.ap-southeast-2.amazonaws.com" - - Sid: "Allow direct access to key metadata to the account" - Effect: "Allow" - Principal: - AWS: "arn:aws:iam::111111111111:root" - Action: - - "kms:Describe*" - - "kms:Get*" - - "kms:List*" - - "kms:RevokeGrant" - Resource: "*" - key_policies: - description: List of policy documents for the key. Empty when access is denied even if there are policies. - type: list - returned: always - elements: dict - sample: - Version: "2012-10-17" - Id: "auto-ebs-2" - Statement: - - Sid: "Allow access through EBS for all principals in the account that are authorized to use EBS" - Effect: "Allow" - Principal: - AWS: "*" - Action: - - "kms:Encrypt" - - "kms:Decrypt" - - "kms:ReEncrypt*" - - "kms:GenerateDataKey*" - - "kms:CreateGrant" - - "kms:DescribeKey" - Resource: "*" - Condition: - StringEquals: - kms:CallerAccount: "111111111111" - kms:ViaService: "ec2.ap-southeast-2.amazonaws.com" - - Sid: "Allow direct access to key metadata to the account" - Effect: "Allow" - Principal: - AWS: "arn:aws:iam::111111111111:root" - Action: - - "kms:Describe*" - - "kms:Get*" - - "kms:List*" - - "kms:RevokeGrant" - Resource: "*" - version_added: 3.3.0 - grants: - description: List of grants associated with a key. - type: list - elements: dict - returned: always - contains: - constraints: - description: Constraints on the encryption context that the grant allows. - See U(https://docs.aws.amazon.com/kms/latest/APIReference/API_GrantConstraints.html) for further details - type: dict - returned: always - sample: - encryption_context_equals: - "aws:lambda:_function_arn": "arn:aws:lambda:ap-southeast-2:012345678912:function:xyz" - creation_date: - description: Date of creation of the grant. - type: str - returned: always - sample: "2017-04-18T15:12:08+10:00" - grant_id: - description: The unique ID for the grant. - type: str - returned: always - sample: abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234 - grantee_principal: - description: The principal that receives the grant's permissions. - type: str - returned: always - sample: arn:aws:sts::0123456789012:assumed-role/lambda_xyz/xyz - issuing_account: - description: The AWS account under which the grant was issued. - type: str - returned: always - sample: arn:aws:iam::01234567890:root - key_id: - description: The key ARN to which the grant applies. - type: str - returned: always - sample: arn:aws:kms:ap-southeast-2:123456789012:key/abcd1234-abcd-1234-5678-ef1234567890 - name: - description: The friendly name that identifies the grant. - type: str - returned: always - sample: xyz - operations: - description: The list of operations permitted by the grant. - type: list - returned: always - sample: - - Decrypt - - RetireGrant - retiring_principal: - description: The principal that can retire the grant. - type: str - returned: always - sample: arn:aws:sts::0123456789012:assumed-role/lambda_xyz/xyz -''' - -import json - -try: - import botocore -except ImportError: - pass # Handled by AnsibleAWSModule - -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict - -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict - -# Caching lookup for aliases -_aliases = dict() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_keys_with_backoff(connection): - paginator = connection.get_paginator('list_keys') - return paginator.paginate().build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_aliases_with_backoff(connection): - paginator = connection.get_paginator('list_aliases') - return paginator.paginate().build_full_result() - - -def get_kms_aliases_lookup(connection): - if not _aliases: - for alias in get_kms_aliases_with_backoff(connection)['Aliases']: - # Not all aliases are actually associated with a key - if 'TargetKeyId' in alias: - # strip off leading 'alias/' and add it to key's aliases - if alias['TargetKeyId'] in _aliases: - _aliases[alias['TargetKeyId']].append(alias['AliasName'][6:]) - else: - _aliases[alias['TargetKeyId']] = [alias['AliasName'][6:]] - return _aliases - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_tags_with_backoff(connection, key_id, **kwargs): - return connection.list_resource_tags(KeyId=key_id, **kwargs) - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_grants_with_backoff(connection, key_id, **kwargs): - params = dict(KeyId=key_id) - if kwargs.get('tokens'): - params['GrantTokens'] = kwargs['tokens'] - paginator = connection.get_paginator('list_grants') - return paginator.paginate(**params).build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_kms_metadata_with_backoff(connection, key_id): - return connection.describe_key(KeyId=key_id) - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def list_key_policies_with_backoff(connection, key_id): - paginator = connection.get_paginator('list_key_policies') - return paginator.paginate(KeyId=key_id).build_full_result() - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_key_policy_with_backoff(connection, key_id, policy_name): - return connection.get_key_policy(KeyId=key_id, PolicyName=policy_name) - - -@AWSRetry.jittered_backoff(retries=5, delay=5, backoff=2.0) -def get_enable_key_rotation_with_backoff(connection, key_id): - try: - current_rotation_status = connection.get_key_rotation_status(KeyId=key_id) - except is_boto3_error_code(['AccessDeniedException', 'UnsupportedOperationException']) as e: - return None - - return current_rotation_status.get('KeyRotationEnabled') - - -def canonicalize_alias_name(alias): - if alias is None: - return None - if alias.startswith('alias/'): - return alias - return 'alias/' + alias - - -def get_kms_tags(connection, module, key_id): - # Handle pagination here as list_resource_tags does not have - # a paginator - kwargs = {} - tags = [] - more = True - while more: - try: - tag_response = get_kms_tags_with_backoff(connection, key_id, **kwargs) - tags.extend(tag_response['Tags']) - except is_boto3_error_code('AccessDeniedException'): - tag_response = {} - except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key tags") - if tag_response.get('NextMarker'): - kwargs['Marker'] = tag_response['NextMarker'] - else: - more = False - return tags - - -def get_kms_policies(connection, module, key_id): - try: - policies = list_key_policies_with_backoff(connection, key_id)['PolicyNames'] - return [get_key_policy_with_backoff(connection, key_id, policy)['Policy'] for - policy in policies] - except is_boto3_error_code('AccessDeniedException'): - return [] - except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key policies") - - -def key_matches_filter(key, filtr): - if filtr[0] == 'key-id': - return filtr[1] == key['key_id'] - if filtr[0] == 'tag-key': - return filtr[1] in key['tags'] - if filtr[0] == 'tag-value': - return filtr[1] in key['tags'].values() - if filtr[0] == 'alias': - return filtr[1] in key['aliases'] - if filtr[0].startswith('tag:'): - tag_key = filtr[0][4:] - if tag_key not in key['tags']: - return False - return key['tags'].get(tag_key) == filtr[1] - - -def key_matches_filters(key, filters): - if not filters: - return True - else: - return all(key_matches_filter(key, filtr) for filtr in filters.items()) - - -def get_key_details(connection, module, key_id, tokens=None): - if not tokens: - tokens = [] - try: - result = get_kms_metadata_with_backoff(connection, key_id)['KeyMetadata'] - # Make sure we have the canonical ARN, we might have been passed an alias - key_id = result['Arn'] - except is_boto3_error_code('NotFoundException'): - return None - except is_boto3_error_code('AccessDeniedException'): # pylint: disable=duplicate-except - module.warn('Permission denied fetching key metadata ({0})'.format(key_id)) - return None - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key metadata") - result['KeyArn'] = result.pop('Arn') - - try: - aliases = get_kms_aliases_lookup(connection) - except is_boto3_error_code('AccessDeniedException'): - module.warn('Permission denied fetching key aliases') - aliases = {} - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain aliases") - # We can only get aliases for our own account, so we don't need the full ARN - result['aliases'] = aliases.get(result['KeyId'], []) - result['enable_key_rotation'] = get_enable_key_rotation_with_backoff(connection, key_id) - - if module.params.get('pending_deletion'): - return camel_dict_to_snake_dict(result) - - try: - result['grants'] = get_kms_grants_with_backoff(connection, key_id, tokens=tokens)['Grants'] - except is_boto3_error_code('AccessDeniedException'): - module.warn('Permission denied fetching key grants ({0})'.format(key_id)) - result['grants'] = [] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to obtain key grants") - - tags = get_kms_tags(connection, module, key_id) - - result = camel_dict_to_snake_dict(result) - result['tags'] = boto3_tag_list_to_ansible_dict(tags, 'TagKey', 'TagValue') - result['policies'] = get_kms_policies(connection, module, key_id) - result['key_policies'] = [json.loads(policy) for policy in result['policies']] - return result - - -def get_kms_info(connection, module): - if module.params.get('key_id'): - key_id = module.params.get('key_id') - details = get_key_details(connection, module, key_id) - if details: - return [details] - return [] - elif module.params.get('alias'): - alias = canonicalize_alias_name(module.params.get('alias')) - details = get_key_details(connection, module, alias) - if details: - return [details] - return [] - else: - try: - keys = get_kms_keys_with_backoff(connection)['Keys'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to obtain keys") - return [get_key_details(connection, module, key['KeyId']) for key in keys] - - -def main(): - argument_spec = dict( - alias=dict(aliases=['key_alias']), - key_id=dict(aliases=['key_arn']), - filters=dict(type='dict'), - pending_deletion=dict(type='bool', default=False), - ) - - module = AnsibleAWSModule(argument_spec=argument_spec, - mutually_exclusive=[['alias', 'filters', 'key_id']], - supports_check_mode=True) - - try: - connection = module.client('kms') - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Failed to connect to AWS') - - module.deprecate("The 'policies' return key is deprecated and will be replaced by 'key_policies'. Both values are returned for now.", - date='2024-05-01', collection_name='community.aws') - - all_keys = get_kms_info(connection, module) - filtered_keys = [key for key in all_keys if key_matches_filters(key, module.params['filters'])] - ret_params = dict(kms_keys=filtered_keys) - - module.exit_json(**ret_params) - - -if __name__ == '__main__': - main()