Refactor aws_kms to bring down the complexity score (#66037)

* Remove dead code key_matches_filter/key_matches_filters

* Fail more cleanly when we don't recognise the 'shape' of KMS policy

* Refactor aws_kms to bring down the complexity

* Minor docs tweaks

* Changelog fragment

* Fixups from review
This commit is contained in:
Mark Chappell 2020-02-20 15:35:41 +01:00 committed by GitHub
parent 0dc08f6b97
commit 335108ac62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 309 additions and 287 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- 'aws_kms: code refactor, some error messages updated'

View file

@ -32,7 +32,8 @@ options:
type: str
key_id:
description:
- Key ID or ARN of the key. One of C(alias) or C(key_id) are required.
- Key ID or ARN of the key.
- One of I(alias) or I(key_id) are required.
required: false
aliases:
- key_arn
@ -50,7 +51,8 @@ options:
type: str
policy_role_name:
description:
- (deprecated) Role to allow/deny access. One of C(policy_role_name) or C(policy_role_arn) are required.
- (deprecated) Role to allow/deny access.
- One of I(policy_role_name) or I(policy_role_arn) are required.
- Used for modifying the Key Policy rather than modifying a grant and only
works on the default policy created through the AWS Console.
- This option has been deprecated, and will be removed in 2.13. Use I(policy) instead.
@ -60,7 +62,8 @@ options:
type: str
policy_role_arn:
description:
- (deprecated) ARN of role to allow/deny access. One of C(policy_role_name) or C(policy_role_arn) are required.
- (deprecated) ARN of role to allow/deny access.
- One of I(policy_role_name) or I(policy_role_arn) are required.
- Used for modifying the Key Policy rather than modifying a grant and only
works on the default policy created through the AWS Console.
- This option has been deprecated, and will be removed in 2.13. Use I(policy) instead.
@ -70,7 +73,8 @@ options:
- role_arn
policy_grant_types:
description:
- (deprecated) List of grants to give to user/role. Likely "role,role grant" or "role,role grant,admin". Required when C(policy_mode=grant).
- (deprecated) List of grants to give to user/role. Likely "role,role grant" or "role,role grant,admin".
- Required when I(policy_mode=grant).
- Used for modifying the Key Policy rather than modifying a grant and only
works on the default policy created through the AWS Console.
- This option has been deprecated, and will be removed in 2.13. Use I(policy) instead.
@ -400,9 +404,7 @@ from ansible.module_utils.ec2 import AWSRetry, camel_dict_to_snake_dict
from ansible.module_utils.ec2 import boto3_tag_list_to_ansible_dict, ansible_dict_to_boto3_tag_list
from ansible.module_utils.ec2 import compare_aws_tags, compare_policies
from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_native
import traceback
import json
try:
@ -502,26 +504,6 @@ def get_kms_policies(connection, module, key_id):
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:'):
return key['Tags'][filtr[0][4:]] == 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 camel_to_snake_grant(grant):
''' camel_to_snake_grant snakifies everything except the encryption context '''
constraints = grant.get('Constraints', {})
@ -625,134 +607,193 @@ def compare_grants(existing_grants, desired_grants, purge_grants=False):
return to_add, to_remove
def ensure_enabled_disabled(connection, module, key):
changed = False
if key['key_state'] == 'Disabled' and module.params['enabled']:
try:
connection.enable_key(KeyId=key['key_arn'])
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to enable key")
def start_key_deletion(connection, module, key_metadata):
if key_metadata['KeyState'] == 'PendingDeletion':
return False
if key['key_state'] == 'Enabled' and not module.params['enabled']:
try:
connection.disable_key(KeyId=key['key_arn'])
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to disable key")
return changed
if module.check_mode:
return True
try:
connection.schedule_key_deletion(KeyId=key_metadata['Arn'])
return True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to schedule key for deletion")
def update_alias(connection, module, key_id, alias):
if not alias.startswith('alias/'):
alias = 'alias/' + alias
aliases = get_kms_aliases_with_backoff(connection)['Aliases']
if key_id:
# We will only add new aliases, not rename existing ones
if alias not in [_alias['AliasName'] for _alias in aliases]:
try:
connection.create_alias(KeyId=key_id, AliasName=alias)
return True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed create key alias")
return False
def update_key(connection, module, key):
changed = False
alias = module.params.get('alias')
def cancel_key_deletion(connection, module, key):
key_id = key['key_arn']
if key['key_state'] != 'PendingDeletion':
return False
if alias:
changed = update_alias(connection, module, key_id, alias) or changed
if module.check_mode:
return True
if key['key_state'] == 'PendingDeletion':
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.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'
changed = True
connection.create_alias(TargetKeyId=key_id, AliasName=alias)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to cancel key deletion")
module.fail_json_aws(e, msg="Failed create key alias")
changed = ensure_enabled_disabled(connection, module, key) or changed
return True
description = module.params.get('description')
# don't update description if description is not set
# (means you can't remove a description completely)
if description and key['description'] != description:
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)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to update key description")
desired_tags = module.params.get('tags')
to_add, to_remove = compare_aws_tags(key['tags'], desired_tags,
module.params.get('purge_tags'))
if to_remove:
try:
connection.untag_resource(KeyId=key_id, TagKeys=to_remove)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to remove or update tag")
if to_add:
try:
connection.tag_resource(KeyId=key_id,
Tags=[{'TagKey': tag_key, 'TagValue': desired_tags[tag_key]}
for tag_key in to_add])
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to add tag to key")
return True
# Update existing policy before trying to tweak grants
if module.params.get('policy'):
policy = module.params.get('policy')
try:
keyret = connection.get_key_policy(KeyId=key_id, PolicyName='default')
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
# If we can't fetch the current policy assume we're making a change
# Could occur if we have PutKeyPolicy without GetKeyPolicy
original_policy = {}
def update_tags(connection, module, key, desired_tags, purge_tags):
# purge_tags needs to be explicitly set, so an empty tags list means remove
# all tags
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'])
try:
new_policy = json.loads(policy)
except ValueError as e:
module.fail_json_aws(e, msg="Unable to parse new policy as JSON")
if compare_policies(original_policy, new_policy):
changed = True
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")
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 = {}
desired_grants = module.params.get('grants')
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_grants(connection, module, key, desired_grants, purge_grants):
existing_grants = key['grants']
to_add, to_remove = compare_grants(existing_grants, desired_grants,
module.params.get('purge_grants'))
if to_remove:
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'])
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to retire grant")
if to_add:
for grant in to_add:
grant_params = convert_grant_params(grant, key)
try:
connection.create_grant(**grant_params)
changed = True
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'))
# make results consistent with kms_facts before returning
result = get_key_details(connection, module, key_id)
module.exit_json(changed=changed, **result)
result = get_key_details(connection, module, key['key_arn'])
result['changed'] = changed
return result
def create_key(connection, module):
@ -771,58 +812,25 @@ def create_key(connection, module):
module.fail_json_aws(e, msg="Failed to create initial key")
key = get_key_details(connection, module, result['KeyId'])
alias = module.params['alias']
if not alias.startswith('alias/'):
alias = 'alias/' + alias
try:
connection.create_alias(AliasName=alias, TargetKeyId=key['key_id'])
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to create alias")
update_alias(connection, module, key, module.params['alias'])
ensure_enabled_disabled(connection, module, key)
for grant in module.params.get('grants'):
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="Failed to add grant to key")
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'])
module.exit_json(changed=True, **result)
result['changed'] = True
return result
def delete_key(connection, module, key_metadata, key_id):
def delete_key(connection, module, key_metadata):
changed = False
if key_metadata['KeyState'] != 'PendingDeletion':
try:
connection.schedule_key_deletion(KeyId=key_id)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to schedule key for deletion")
changed |= start_key_deletion(connection, module, key_metadata)
result = get_key_details(connection, module, key_id)
module.exit_json(changed=changed, **result)
def get_arn_from_kms_alias(kms, aliasname):
ret = kms.list_aliases()
key_id = None
for a in ret['Aliases']:
if a['AliasName'] == aliasname:
key_id = a['TargetKeyId']
break
if not key_id:
raise Exception('could not find alias {0}'.format(aliasname))
# now that we have the ID for the key, we need to get the key's ARN. The alias
# has an ARN but we need the key itself.
ret = kms.list_keys()
for k in ret['Keys']:
if k['KeyId'] == key_id:
return k['KeyArn']
raise Exception('could not find key from id: {0}'.format(key_id))
result = get_key_details(connection, module, key_metadata['Arn'])
result['changed'] = changed
return result
def get_arn_from_role_name(iam, rolename):
@ -832,78 +840,85 @@ def get_arn_from_role_name(iam, rolename):
raise Exception('could not find arn for name {0}.'.format(rolename))
def do_policy_grant(module, kms, keyarn, role_arn, granttypes, mode='grant', dry_run=True, clean_invalid_entries=True):
def _clean_statement_principals(statement, clean_invalid_entries):
# create Principal and 'AWS' so we can safely use them later.
if not isinstance(statement.get('Principal'), dict):
statement['Principal'] = dict()
# If we have a single AWS Principal, ensure we still have a list (to manipulate)
if 'AWS' in statement['Principal'] and isinstance(statement['Principal']['AWS'], string_types):
statement['Principal']['AWS'] = [statement['Principal']['AWS']]
if not isinstance(statement['Principal'].get('AWS'), list):
statement['Principal']['AWS'] = list()
invalid_entries = [item for item in statement['Principal']['AWS'] if not item.startswith('arn:aws:iam::')]
valid_entries = [item for item in statement['Principal']['AWS'] if item.startswith('arn:aws:iam::')]
if bool(invalid_entries) and clean_invalid_entries:
statement['Principal']['AWS'] = valid_entries
return True
return False
def _do_statement_grant(statement, role_arn, grant_types, mode, grant_type):
if mode == 'grant':
if grant_type in grant_types:
if role_arn not in statement['Principal']['AWS']: # needs to be added.
statement['Principal']['AWS'].append(role_arn)
return 'add'
elif role_arn in statement['Principal']['AWS']: # not one the places the role should be
statement['Principal']['AWS'].remove(role_arn)
return 'remove'
return None
if mode == 'deny' and role_arn in statement['Principal']['AWS']:
# we don't selectively deny. that's a grant with a
# smaller list. so deny=remove all of this arn.
statement['Principal']['AWS'].remove(role_arn)
return 'remove'
return None
def do_policy_grant(module, kms, keyarn, role_arn, grant_types, mode='grant', dry_run=True, clean_invalid_entries=True):
ret = {}
keyret = get_key_policy_with_backoff(kms, keyarn, 'default')
policy = json.loads(keyret['Policy'])
policy = json.loads(get_key_policy_with_backoff(kms, keyarn, 'default')['Policy'])
changes_needed = {}
assert_policy_shape(policy)
assert_policy_shape(module, policy)
had_invalid_entries = False
for statement in policy['Statement']:
for granttype in ['role', 'role grant', 'admin']:
# do we want this grant type? Are we on its statement?
# and does the role have this grant type?
# We already tested that these are the only types in the statements
for grant_type in statement_label:
# Are we on this grant type's statement?
if statement['Sid'] != statement_label[grant_type]:
continue
# create Principal and 'AWS' so we can safely use them later.
if not isinstance(statement.get('Principal'), dict):
statement['Principal'] = dict()
if 'AWS' in statement['Principal'] and isinstance(statement['Principal']['AWS'], string_types):
# convert to list
statement['Principal']['AWS'] = [statement['Principal']['AWS']]
if not isinstance(statement['Principal'].get('AWS'), list):
statement['Principal']['AWS'] = list()
if mode == 'grant' and statement['Sid'] == statement_label[granttype]:
# we're granting and we recognize this statement ID.
if granttype in granttypes:
invalid_entries = [item for item in statement['Principal']['AWS'] if not item.startswith('arn:aws:iam::')]
if clean_invalid_entries and invalid_entries:
# we have bad/invalid entries. These are roles that were deleted.
# prune the list.
valid_entries = [item for item in statement['Principal']['AWS'] if item.startswith('arn:aws:iam::')]
statement['Principal']['AWS'] = valid_entries
had_invalid_entries = True
if role_arn not in statement['Principal']['AWS']: # needs to be added.
changes_needed[granttype] = 'add'
if not dry_run:
statement['Principal']['AWS'].append(role_arn)
elif role_arn in statement['Principal']['AWS']: # not one the places the role should be
changes_needed[granttype] = 'remove'
if not dry_run:
statement['Principal']['AWS'].remove(role_arn)
elif mode == 'deny' and statement['Sid'] == statement_label[granttype] and role_arn in statement['Principal']['AWS']:
# we don't selectively deny. that's a grant with a
# smaller list. so deny=remove all of this arn.
changes_needed[granttype] = 'remove'
if not dry_run:
statement['Principal']['AWS'].remove(role_arn)
try:
if len(changes_needed) and not dry_run:
policy_json_string = json.dumps(policy)
kms.put_key_policy(KeyId=keyarn, PolicyName='default', Policy=policy_json_string)
# returns nothing, so we have to just assume it didn't throw
ret['changed'] = True
except Exception as e:
module.fail_json(msg='Could not update key_policy', new_policy=policy_json_string, details=to_native(e), exception=traceback.format_exc())
raise
had_invalid_entries |= _clean_statement_principals(statement, clean_invalid_entries)
change = _do_statement_grant(statement, role_arn, grant_types, mode, grant_type)
if change:
changes_needed[grant_type] = change
ret['changes_needed'] = changes_needed
ret['had_invalid_entries'] = had_invalid_entries
ret['new_policy'] = policy
if dry_run:
# true if changes > 0
ret['changed'] = len(changes_needed) > 0
ret['changed'] = bool(changes_needed)
if dry_run or not ret['changed']:
return ret
try:
policy_json_string = json.dumps(policy)
kms.put_key_policy(KeyId=keyarn, PolicyName='default', Policy=policy_json_string)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Could not update key_policy', new_policy=policy_json_string)
return ret
def assert_policy_shape(policy):
def assert_policy_shape(module, policy):
'''Since the policy seems a little, uh, fragile, make sure we know approximately what we're looking at.'''
errors = []
if policy['Version'] != "2012-10-17":
@ -915,13 +930,61 @@ def assert_policy_shape(policy):
if statement['Sid'] == sidlabel:
found_statement_type[label] = True
for statementtype in statement_label.keys():
for statementtype in statement_label:
if not found_statement_type.get(statementtype):
errors.append('Policy is missing {0}.'.format(statementtype))
if len(errors):
raise Exception('Problems asserting policy shape. Cowardly refusing to modify it: {0}'.format(' '.join(errors)) + "\n" + str(policy))
return None
if errors:
module.fail_json(msg='Problems asserting policy shape. Cowardly refusing to modify it', errors=errors, policy=policy)
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):
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 update_policy_grants(connection, module, key_metadata, mode):
iam = module.client('iam')
key_id = key_metadata['Arn']
if module.params.get('policy_role_name') and not module.params.get('policy_role_arn'):
module.params['policy_role_arn'] = get_arn_from_role_name(iam, module.params['policy_role_name'])
if not module.params.get('policy_role_arn'):
module.fail_json(msg='policy_role_arn or policy_role_name is required to {0}'.format(module.params['policy_mode']))
# check the grant types for 'grant' only.
if mode == 'grant':
for grant_type in module.params['policy_grant_types']:
if grant_type not in statement_label:
module.fail_json(msg='{0} is an unknown grant type.'.format(grant_type))
return do_policy_grant(module, connection,
key_id,
module.params['policy_role_arn'],
module.params['policy_grant_types'],
mode=mode,
dry_run=module.check_mode,
clean_invalid_entries=module.params['policy_clean_invalid_entries'])
def main():
@ -949,77 +1012,34 @@ def main():
required_one_of=[['alias', 'key_id']],
)
result = {}
mode = module.params['policy_mode']
kms = module.client('kms')
iam = module.client('iam')
key_id = module.params.get('key_id')
alias = module.params.get('alias')
if alias and alias.startswith('alias/'):
alias = alias[6:]
# Fetch/Canonicalize key_id where possible
if key_id:
try:
# Don't use get_key_details it triggers module.fail when the key
# doesn't exist
key_metadata = get_kms_metadata_with_backoff(kms, key_id)['KeyMetadata']
key_id = key_metadata['Arn']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
# 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':
module.fail_json(msg="Could not find key with id %s to update")
key_metadata = None
elif alias:
try:
key_metadata = get_kms_metadata_with_backoff(kms, 'alias/%s' % alias)['KeyMetadata']
key_id = key_metadata['Arn']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
key_metadata = None
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 %s to update")
if module.params.get('policy_grant_types') or mode == 'deny':
module.deprecate('Managing the KMS IAM Policy via policy_mode and policy_grant_types is fragile'
' and has been deprecated in favour of the policy option.', version='2.13')
if module.params.get('policy_role_name') and not module.params.get('policy_role_arn'):
module.params['policy_role_arn'] = get_arn_from_role_name(iam, module.params['policy_role_name'])
if not module.params.get('policy_role_arn'):
module.fail_json(msg='policy_role_arn or policy_role_name is required to {0}'.format(module.params['policy_mode']))
# check the grant types for 'grant' only.
if mode == 'grant':
for g in module.params['policy_grant_types']:
if g not in statement_label:
module.fail_json(msg='{0} is an unknown grant type.'.format(g))
ret = do_policy_grant(module, kms,
key_id,
module.params['policy_role_arn'],
module.params['policy_grant_types'],
mode=mode,
dry_run=module.check_mode,
clean_invalid_entries=module.params['policy_clean_invalid_entries'])
result.update(ret)
result = update_policy_grants(kms, module, key_metadata, mode)
module.exit_json(**result)
else:
if module.params.get('state') == 'present':
if key_metadata:
key_details = get_key_details(kms, module, key_id)
update_key(kms, module, key_details)
else:
if key_id:
module.fail_json(msg="Could not find key with id %s to update" % key_id)
else:
create_key(kms, module)
else:
if key_metadata:
delete_key(kms, module, key_metadata, key_id)
else:
module.exit_json(changed=False)
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__':