def compare_tags(state_machine_arn, sfn_client, module): new_tags = module.params.get('tags') current_tags = sfn_client.list_tags_for_resource( resourceArn=state_machine_arn).get('tags') return compare_aws_tags(boto3_tag_list_to_ansible_dict(current_tags), new_tags if new_tags else {}, module.params.get('purge_tags'))
def update_tags(module, connection, group, tags): changed = False existing_tags = connection.list_tags_for_resource( ResourceName=group['DBParameterGroupArn'])['TagList'] to_update, to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict(existing_tags), tags, module.params['purge_tags']) if to_update: try: connection.add_tags_to_resource( ResourceName=group['DBParameterGroupArn'], Tags=ansible_dict_to_boto3_tag_list(to_update)) changed = True except botocore.exceptions.ClientError as e: module.fail_json(msg="Couldn't add tags to parameter group: %s" % str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except botocore.exceptions.ParamValidationError as e: # Usually a tag value has been passed as an int or bool, needs to be a string # The AWS exception message is reasonably ok for this purpose module.fail_json(msg="Couldn't add tags to parameter group: %s." % str(e), exception=traceback.format_exc()) if to_delete: try: connection.remove_tags_from_resource( ResourceName=group['DBParameterGroupArn'], TagKeys=to_delete) changed = True except botocore.exceptions.ClientError as e: module.fail_json( msg="Couldn't remove tags from parameter group: %s" % str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) return changed
def ensure_tags(client, module, resource_arn, existing_tags, tags, purge_tags): if tags is None: return False 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: try: client.add_tags_to_resource( ResourceName=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, "Couldn't add tags to snapshot {0}".format(resource_arn)) if tags_to_remove: try: client.remove_tags_from_resource(ResourceName=resource_arn, TagKeys=tags_to_remove) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Couldn't remove tags from snapshot {0}".format(resource_arn)) return changed
def update_role_tags(connection, module, params, role): new_tags = params.get('Tags') if new_tags is None: return False new_tags = boto3_tag_list_to_ansible_dict(new_tags) role_name = module.params.get('name') purge_tags = module.params.get('purge_tags') try: existing_tags = boto3_tag_list_to_ansible_dict(connection.list_role_tags(RoleName=role_name, aws_retry=True)['Tags']) except (ClientError, KeyError): existing_tags = {} tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, new_tags, purge_tags=purge_tags) if not module.check_mode: try: if tags_to_remove: connection.untag_role(RoleName=role_name, TagKeys=tags_to_remove, aws_retry=True) if tags_to_add: connection.tag_role(RoleName=role_name, Tags=ansible_dict_to_boto3_tag_list(tags_to_add), aws_retry=True) except (ClientError, BotoCoreError) as e: module.fail_json_aws(e, msg='Unable to set tags for role %s' % role_name) changed = bool(tags_to_add) or bool(tags_to_remove) return changed
def update_tags(client, queue_url, module): new_tags = module.params.get('tags') purge_tags = module.params.get('purge_tags') if new_tags is None: return False, {} try: existing_tags = client.list_queue_tags(QueueUrl=queue_url, aws_retry=True)['Tags'] except (ClientError, KeyError) as e: existing_tags = {} tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, new_tags, purge_tags=purge_tags) if not module.check_mode: if tags_to_remove: client.untag_queue(QueueUrl=queue_url, TagKeys=tags_to_remove, aws_retry=True) if tags_to_add: client.tag_queue(QueueUrl=queue_url, Tags=tags_to_add) existing_tags = client.list_queue_tags(QueueUrl=queue_url, aws_retry=True).get('Tags', {}) else: existing_tags = new_tags changed = bool(tags_to_remove) or bool(tags_to_add) return changed, existing_tags
def ensure_tags(self, tgw_id, tags, purge_tags): """ Ensures tags are applied to the transit gateway. Optionally will remove any existing tags not in the tags argument if purge_tags is set to true :param tgw_id: The AWS id of the transit gateway :param tags: list of tags to apply to the transit gateway. :param purge_tags: when true existing tags not in tags parms are removed :return: true if tags were updated """ tags_changed = False filters = ansible_dict_to_boto3_filter_list({'resource-id': tgw_id}) try: cur_tags = self._connection.describe_tags(Filters=filters) except (ClientError, BotoCoreError) as e: self._module.fail_json_aws(e, msg="Couldn't describe tags") to_update, to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) if to_update: try: if not self._check_mode: AWSRetry.exponential_backoff()( self._connection.create_tags)( Resources=[tgw_id], Tags=ansible_dict_to_boto3_tag_list(to_update)) self._results['changed'] = True tags_changed = True except (ClientError, BotoCoreError) as e: self._module.fail_json_aws( e, msg="Couldn't create tags {0} for resource {1}".format( ansible_dict_to_boto3_tag_list(to_update), tgw_id)) if to_delete: try: if not self._check_mode: tags_list = [] for key in to_delete: tags_list.append({'Key': key}) AWSRetry.exponential_backoff()( self._connection.delete_tags)(Resources=[tgw_id], Tags=tags_list) self._results['changed'] = True tags_changed = True except (ClientError, BotoCoreError) as e: self._module.fail_json_aws( e, msg="Couldn't delete tags {0} for resource {1}".format( ansible_dict_to_boto3_tag_list(to_delete), tgw_id)) return tags_changed
def ensure_tags(client, module, resource_arn, existing_tags, tags, purge_tags): if tags is None: return False 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: call_method( client, module, method_name='add_tags_to_resource', parameters={'ResourceName': resource_arn, 'Tags': ansible_dict_to_boto3_tag_list(tags_to_add)} ) if tags_to_remove: call_method( client, module, method_name='remove_tags_from_resource', parameters={'ResourceName': resource_arn, 'TagKeys': tags_to_remove} ) return changed
def ensure_tags(conn, module, subnet, tags, purge_tags, start_time): changed = False filters = ansible_dict_to_boto3_filter_list({'resource-id': subnet['id'], 'resource-type': 'subnet'}) try: cur_tags = conn.describe_tags(Filters=filters) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't describe tags") to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) if to_update: try: if not module.check_mode: AWSRetry.exponential_backoff( catch_extra_error_codes=['InvalidSubnetID.NotFound'] )(conn.create_tags)( Resources=[subnet['id']], Tags=ansible_dict_to_boto3_tag_list(to_update) ) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create tags") if to_delete: try: if not module.check_mode: tags_list = [] for key in to_delete: tags_list.append({'Key': key}) AWSRetry.exponential_backoff( catch_extra_error_codes=['InvalidSubnetID.NotFound'] )(conn.delete_tags)(Resources=[subnet['id']], Tags=tags_list) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete tags") if module.params['wait'] and not module.check_mode: # Wait for tags to be updated filters = [{'Name': 'tag:{0}'.format(k), 'Values': [v]} for k, v in tags.items()] handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) return changed
def ensure_tags(connection=None, module=None, resource_id=None, tags=None, purge_tags=None, check_mode=None): try: cur_tags = describe_tags_with_backoff(connection, resource_id) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Unable to list tags for VPC') to_add, to_delete = compare_aws_tags(cur_tags, tags, purge_tags) if not to_add and not to_delete: return {'changed': False, 'tags': cur_tags} if check_mode: if not purge_tags: tags = cur_tags.update(tags) return {'changed': True, 'tags': tags} if to_delete: try: connection.delete_tags(Resources=[resource_id], Tags=[{ 'Key': k } for k in to_delete]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete tags") if to_add: try: connection.create_tags(Resources=[resource_id], Tags=ansible_dict_to_boto3_tag_list(to_add)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create tags") try: latest_tags = describe_tags_with_backoff(connection, resource_id) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Unable to list tags for VPC') return {'changed': True, 'tags': latest_tags}
def set_tag(client, module, tags, function): if not hasattr(client, "list_tags"): module.fail_json(msg="Using tags requires botocore 1.5.40 or above") changed = False arn = function['Configuration']['FunctionArn'] try: current_tags = client.list_tags(Resource=arn).get('Tags', {}) except ClientError as e: module.fail_json(msg="Unable to list tags: {0}".format(to_native(e)), exception=traceback.format_exc()) tags_to_add, tags_to_remove = compare_aws_tags(current_tags, tags, purge_tags=True) try: if tags_to_remove: client.untag_resource( Resource=arn, TagKeys=tags_to_remove ) changed = True if tags_to_add: client.tag_resource( Resource=arn, Tags=tags_to_add ) changed = True except ClientError as e: module.fail_json(msg="Unable to tag resource {0}: {1}".format(arn, to_native(e)), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) except BotoCoreError as e: module.fail_json(msg="Unable to tag resource {0}: {1}".format(arn, to_native(e)), exception=traceback.format_exc()) return changed
def update_vpc_tags(connection, module, vpc_id, tags, name): if tags is None: tags = dict() tags.update({'Name': name}) tags = dict((k, to_native(v)) for k, v in tags.items()) try: current_tags = dict((t['Key'], t['Value']) for t in connection.describe_tags(Filters=[{'Name': 'resource-id', 'Values': [vpc_id]}])['Tags']) tags_to_update, dummy = compare_aws_tags(current_tags, tags, False) if tags_to_update: if not module.check_mode: tags = ansible_dict_to_boto3_tag_list(tags_to_update) vpc_obj = connection.create_tags(Resources=[vpc_id], Tags=tags, aws_retry=True) # Wait for tags to be updated expected_tags = boto3_tag_list_to_ansible_dict(tags) filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()] connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id], Filters=filters) return True else: return False except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to update tags")
def main(): module = AnsibleAWSModule( argument_spec={ 'name': dict(required=True), 'state': dict(choices=['present', 'absent'], default='present'), 'description': dict(default=""), 'kms_key_id': dict(), 'secret_type': dict(choices=['binary', 'string'], default="string"), 'secret': dict(default=""), 'tags': dict(type='dict', default={}), 'rotation_lambda': dict(), 'rotation_interval': dict(type='int', default=30), 'recovery_window': dict(type='int', default=30), }, supports_check_mode=True, ) changed = False state = module.params.get('state') secrets_mgr = SecretsManagerInterface(module) recovery_window = module.params.get('recovery_window') secret = Secret(module.params.get('name'), module.params.get('secret_type'), module.params.get('secret'), description=module.params.get('description'), kms_key_id=module.params.get('kms_key_id'), tags=module.params.get('tags'), lambda_arn=module.params.get('rotation_lambda'), rotation_interval=module.params.get('rotation_interval')) current_secret = secrets_mgr.get_secret(secret.name) if state == 'absent': if current_secret: if not current_secret.get("DeletedDate"): result = camel_dict_to_snake_dict( secrets_mgr.delete_secret(secret.name, recovery_window=recovery_window)) changed = True elif current_secret.get("DeletedDate") and recovery_window == 0: result = camel_dict_to_snake_dict( secrets_mgr.delete_secret(secret.name, recovery_window=recovery_window)) changed = True else: result = "secret does not exist" if state == 'present': if current_secret is None: result = secrets_mgr.create_secret(secret) changed = True else: if current_secret.get("DeletedDate"): secrets_mgr.restore_secret(secret.name) changed = True if not secrets_mgr.secrets_match(secret, current_secret): result = secrets_mgr.update_secret(secret) changed = True if not rotation_match(secret, current_secret): result = secrets_mgr.update_rotation(secret) changed = True current_tags = boto3_tag_list_to_ansible_dict( current_secret.get('Tags', [])) tags_to_add, tags_to_remove = compare_aws_tags( current_tags, secret.tags) if tags_to_add: secrets_mgr.tag_secret( secret.name, ansible_dict_to_boto3_tag_list(tags_to_add)) changed = True if tags_to_remove: secrets_mgr.untag_secret(secret.name, tags_to_remove) changed = True result = camel_dict_to_snake_dict(secrets_mgr.get_secret(secret.name)) result.pop("response_metadata") module.exit_json(changed=changed, secret=result)
def check_for_update(connection, module_params, vpn_connection_id): """ Determines if there are any tags or routes that need to be updated. Ensures non-modifiable attributes aren't expected to change. """ tags = module_params.get('tags') routes = module_params.get('routes') purge_tags = module_params.get('purge_tags') purge_routes = module_params.get('purge_routes') vpn_connection = find_connection(connection, module_params, vpn_connection_id=vpn_connection_id) current_attrs = camel_dict_to_snake_dict(vpn_connection) # Initialize changes dict changes = { 'tags_to_add': [], 'tags_to_remove': [], 'routes_to_add': [], 'routes_to_remove': [] } # Get changes to tags current_tags = boto3_tag_list_to_ansible_dict( current_attrs.get('tags', []), u'key', u'value') tags_to_add, changes['tags_to_remove'] = compare_aws_tags( current_tags, tags, purge_tags) changes['tags_to_add'] = ansible_dict_to_boto3_tag_list(tags_to_add) # Get changes to routes if 'Routes' in vpn_connection: current_routes = [ route['DestinationCidrBlock'] for route in vpn_connection['Routes'] ] if purge_routes: changes['routes_to_remove'] = [ old_route for old_route in current_routes if old_route not in routes ] changes['routes_to_add'] = [ new_route for new_route in routes if new_route not in current_routes ] # Check if nonmodifiable attributes are attempted to be modified for attribute in current_attrs: if attribute in ("tags", "routes", "state"): continue elif attribute == 'options': will_be = module_params.get('static_only', None) is_now = bool(current_attrs[attribute]['static_routes_only']) attribute = 'static_only' elif attribute == 'type': will_be = module_params.get("connection_type", None) is_now = current_attrs[attribute] else: is_now = current_attrs[attribute] will_be = module_params.get(attribute, None) if will_be is not None and to_text(will_be) != to_text(is_now): raise VPNConnectionException( msg= "You cannot modify {0}, the current value of which is {1}. Modifiable VPN " "connection attributes are tags and routes. The value you tried to change it to " "is {2}.".format(attribute, is_now, will_be)) return changes
def ensure_tags(self, igw_id, tags, add_only): final_tags = [] filters = ansible_dict_to_boto3_filter_list({ 'resource-id': igw_id, 'resource-type': 'internet-gateway' }) cur_tags = None try: cur_tags = self._connection.describe_tags(Filters=filters) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self._module.fail_json_aws(e, msg="Couldn't describe tags") purge_tags = bool(not add_only) to_update, to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) final_tags = boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')) if to_update: try: if self._check_mode: # update tags final_tags.update(to_update) else: AWSRetry.exponential_backoff()( self._connection.create_tags)( Resources=[igw_id], Tags=ansible_dict_to_boto3_tag_list(to_update)) self._results['changed'] = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self._module.fail_json_aws(e, msg="Couldn't create tags") if to_delete: try: if self._check_mode: # update tags for key in to_delete: del final_tags[key] else: tags_list = [] for key in to_delete: tags_list.append({'Key': key}) AWSRetry.exponential_backoff()( self._connection.delete_tags)(Resources=[igw_id], Tags=tags_list) self._results['changed'] = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self._module.fail_json_aws(e, msg="Couldn't delete tags") if not self._check_mode and (to_update or to_delete): try: response = self._connection.describe_tags(Filters=filters) final_tags = boto3_tag_list_to_ansible_dict( response.get('Tags')) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self._module.fail_json_aws(e, msg="Couldn't describe tags") return final_tags
def main(): argument_spec = dict( name=dict(required=True), state=dict(choices=['absent', 'present'], default='present'), tags=dict(type='dict'), ) required_if = [['state', 'present', ['tags']]] module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=False, required_if=required_if, ) if not HAS_BOTO3: module.fail_json(msg='boto3 and botocore are required for this module') name = module.params.get('name') state = module.params.get('state').lower() tags = module.params.get('tags') if tags: tags = ansible_dict_to_boto3_tag_list(tags, 'key', 'value') client = module.client('inspector') try: existing_target_arn = client.list_assessment_targets(filter={ 'assessmentTargetNamePattern': name }, ).get('assessmentTargetArns')[0] existing_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ existing_target_arn ], ).get('assessmentTargets')[0]) existing_resource_group_arn = existing_target.get('resource_group_arn') existing_resource_group_tags = client.describe_resource_groups( resourceGroupArns=[existing_resource_group_arn ], ).get('resourceGroups')[0].get('tags') target_exists = True except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to retrieve targets") except IndexError: target_exists = False if state == 'present' and target_exists: ansible_dict_tags = boto3_tag_list_to_ansible_dict(tags) ansible_dict_existing_tags = boto3_tag_list_to_ansible_dict( existing_resource_group_tags) tags_to_add, tags_to_remove = compare_aws_tags( ansible_dict_tags, ansible_dict_existing_tags) if not (tags_to_add or tags_to_remove): existing_target.update({'tags': ansible_dict_existing_tags}) module.exit_json(changed=False, **existing_target) else: try: updated_resource_group_arn = client.create_resource_group( resourceGroupTags=tags, ).get('resourceGroupArn') client.update_assessment_target( assessmentTargetArn=existing_target_arn, assessmentTargetName=name, resourceGroupArn=updated_resource_group_arn, ) updated_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ existing_target_arn ], ).get('assessmentTargets')[0]) updated_target.update({'tags': ansible_dict_tags}) module.exit_json(changed=True, **updated_target), except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to update target") elif state == 'present' and not target_exists: try: new_resource_group_arn = client.create_resource_group( resourceGroupTags=tags, ).get('resourceGroupArn') new_target_arn = client.create_assessment_target( assessmentTargetName=name, resourceGroupArn=new_resource_group_arn, ).get('assessmentTargetArn') new_target = camel_dict_to_snake_dict( client.describe_assessment_targets(assessmentTargetArns=[ new_target_arn ], ).get('assessmentTargets')[0]) new_target.update({'tags': boto3_tag_list_to_ansible_dict(tags)}) module.exit_json(changed=True, **new_target) except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to create target") elif state == 'absent' and target_exists: try: client.delete_assessment_target( assessmentTargetArn=existing_target_arn, ) module.exit_json(changed=True) except ( botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, ) as e: module.fail_json_aws(e, msg="trying to delete target") elif state == 'absent' and not target_exists: module.exit_json(changed=False)
def main(): argument_spec = dict(cluster_name=dict(required=True), resource=dict(required=False), tags=dict(type='dict'), purge_tags=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']), resource_type=dict(default='cluster', choices=[ 'cluster', 'task', 'service', 'task_definition', 'container' ])) required_if = [('state', 'present', ['tags']), ('state', 'absent', ['tags'])] module = AnsibleAWSModule(argument_spec=argument_spec, required_if=required_if, supports_check_mode=True) resource_type = module.params['resource_type'] cluster_name = module.params['cluster_name'] if resource_type == 'cluster': resource = cluster_name else: resource = module.params['resource'] tags = module.params['tags'] state = module.params['state'] purge_tags = module.params['purge_tags'] result = {'changed': False} ecs = module.client('ecs') resource_arn = get_arn(ecs, module, cluster_name, resource_type, resource) current_tags = get_tags(ecs, module, resource_arn) add_tags, remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags) remove_tags = {} if state == 'absent': for key in tags: if key in current_tags and (tags[key] is None or current_tags[key] == tags[key]): remove_tags[key] = current_tags[key] for key in remove: remove_tags[key] = current_tags[key] if remove_tags: result['changed'] = True result['removed_tags'] = remove_tags if not module.check_mode: try: ecs.untag_resource(resourceArn=resource_arn, tagKeys=list(remove_tags.keys())) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to remove tags {0} from resource {1}'.format( remove_tags, resource)) if state == 'present' and add_tags: result['changed'] = True result['added_tags'] = add_tags current_tags.update(add_tags) if not module.check_mode: try: tags = ansible_dict_to_boto3_tag_list( add_tags, tag_name_key_name='key', tag_value_key_name='value') ecs.tag_resource(resourceArn=resource_arn, tags=tags) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to set tags {0} on resource {1}'.format( add_tags, resource)) result['tags'] = get_tags(ecs, module, resource_arn) module.exit_json(**result)
def update_image(module, connection, image_id): launch_permissions = module.params.get('launch_permissions') image = get_image_by_id(module, connection, image_id) if image is None: module.fail_json(msg="Image %s does not exist" % image_id, changed=False) changed = False if launch_permissions is not None: current_permissions = image['LaunchPermissions'] current_users = set(permission['UserId'] for permission in current_permissions if 'UserId' in permission) desired_users = set(str(user_id) for user_id in launch_permissions.get('user_ids', [])) current_groups = set(permission['Group'] for permission in current_permissions if 'Group' in permission) desired_groups = set(launch_permissions.get('group_names', [])) to_add_users = desired_users - current_users to_remove_users = current_users - desired_users to_add_groups = desired_groups - current_groups to_remove_groups = current_groups - desired_groups to_add = [dict(Group=group) for group in to_add_groups] + [dict(UserId=user_id) for user_id in to_add_users] to_remove = [dict(Group=group) for group in to_remove_groups] + [dict(UserId=user_id) for user_id in to_remove_users] if to_add or to_remove: try: connection.modify_image_attribute(ImageId=image_id, Attribute='launchPermission', LaunchPermission=dict(Add=to_add, Remove=to_remove)) changed = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Error updating launch permissions of image %s" % image_id) desired_tags = module.params.get('tags') if desired_tags is not None: current_tags = boto3_tag_list_to_ansible_dict(image.get('Tags')) tags_to_add, tags_to_remove = compare_aws_tags(current_tags, desired_tags, purge_tags=module.params.get('purge_tags')) if tags_to_remove: try: connection.delete_tags(Resources=[image_id], Tags=[dict(Key=tagkey) for tagkey in tags_to_remove]) changed = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Error updating tags") if tags_to_add: try: connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags_to_add)) changed = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Error updating tags") description = module.params.get('description') if description and description != image['Description']: try: connection.modify_image_attribute(Attribute='Description ', ImageId=image_id, Description=dict(Value=description)) changed = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg="Error setting description for image %s" % image_id) if changed: module.exit_json(msg="AMI updated.", changed=True, **get_ami_info(get_image_by_id(module, connection, image_id))) else: module.exit_json(msg="AMI not updated.", changed=False, **get_ami_info(get_image_by_id(module, connection, image_id)))
def converge_file_system(self, name, tags, purge_tags, targets, throughput_mode, provisioned_throughput_in_mibps): """ Change attributes (mount targets and tags) of filesystem by name """ result = False fs_id = self.get_file_system_id(name) if tags is not None: tags_need_modify, tags_to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict( self.get_tags(FileSystemId=fs_id)), tags, purge_tags) if tags_to_delete: try: self.connection.delete_tags(FileSystemId=fs_id, TagKeys=tags_to_delete) except (ClientError, BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to delete tags.") result = True if tags_need_modify: try: self.connection.create_tags( FileSystemId=fs_id, Tags=ansible_dict_to_boto3_tag_list(tags_need_modify)) except (ClientError, BotoCoreError) as e: self.module.fail_json_aws(e, msg="Unable to create tags.") result = True if targets is not None: incomplete_states = [self.STATE_CREATING, self.STATE_DELETING] wait_for( lambda: len( self.get_mount_targets_in_state(fs_id, incomplete_states)), 0) current_targets = _index_by_key( 'SubnetId', self.get_mount_targets(FileSystemId=fs_id)) targets = _index_by_key('SubnetId', targets) targets_to_create, intersection, targets_to_delete = dict_diff( current_targets, targets, True) # To modify mount target it should be deleted and created again changed = [ sid for sid in intersection if not targets_equal( ['SubnetId', 'IpAddress', 'NetworkInterfaceId'], current_targets[sid], targets[sid]) ] targets_to_delete = list(targets_to_delete) + changed targets_to_create = list(targets_to_create) + changed if targets_to_delete: for sid in targets_to_delete: self.connection.delete_mount_target( MountTargetId=current_targets[sid]['MountTargetId']) wait_for( lambda: len( self.get_mount_targets_in_state( fs_id, incomplete_states)), 0) result = True if targets_to_create: for sid in targets_to_create: self.connection.create_mount_target(FileSystemId=fs_id, **targets[sid]) wait_for( lambda: len( self.get_mount_targets_in_state( fs_id, incomplete_states)), 0, self.wait_timeout) result = True # If no security groups were passed into the module, then do not change it. security_groups_to_update = [ sid for sid in intersection if 'SecurityGroups' in targets[sid] and current_targets[sid] ['SecurityGroups'] != targets[sid]['SecurityGroups'] ] if security_groups_to_update: for sid in security_groups_to_update: self.connection.modify_mount_target_security_groups( MountTargetId=current_targets[sid]['MountTargetId'], SecurityGroups=targets[sid].get( 'SecurityGroups', None)) result = True return result
def create_or_update_target_group(connection, module): changed = False new_target_group = False params = dict() target_type = module.params.get("target_type") params['Name'] = module.params.get("name") params['TargetType'] = target_type if target_type != "lambda": params['Protocol'] = module.params.get("protocol").upper() params['Port'] = module.params.get("port") params['VpcId'] = module.params.get("vpc_id") tags = module.params.get("tags") purge_tags = module.params.get("purge_tags") deregistration_delay_timeout = module.params.get( "deregistration_delay_timeout") stickiness_enabled = module.params.get("stickiness_enabled") stickiness_lb_cookie_duration = module.params.get( "stickiness_lb_cookie_duration") stickiness_type = module.params.get("stickiness_type") health_option_keys = [ "health_check_path", "health_check_protocol", "health_check_interval", "health_check_timeout", "healthy_threshold_count", "unhealthy_threshold_count", "successful_response_codes" ] health_options = any([ module.params[health_option_key] is not None for health_option_key in health_option_keys ]) # Set health check if anything set if health_options: if module.params.get("health_check_protocol") is not None: params['HealthCheckProtocol'] = module.params.get( "health_check_protocol").upper() if module.params.get("health_check_port") is not None: params['HealthCheckPort'] = module.params.get("health_check_port") if module.params.get("health_check_interval") is not None: params['HealthCheckIntervalSeconds'] = module.params.get( "health_check_interval") if module.params.get("health_check_timeout") is not None: params['HealthCheckTimeoutSeconds'] = module.params.get( "health_check_timeout") if module.params.get("healthy_threshold_count") is not None: params['HealthyThresholdCount'] = module.params.get( "healthy_threshold_count") if module.params.get("unhealthy_threshold_count") is not None: params['UnhealthyThresholdCount'] = module.params.get( "unhealthy_threshold_count") # Only need to check response code and path for http(s) health checks protocol = module.params.get("health_check_protocol") if protocol is not None and protocol.upper() in ['HTTP', 'HTTPS']: if module.params.get("health_check_path") is not None: params['HealthCheckPath'] = module.params.get( "health_check_path") if module.params.get("successful_response_codes") is not None: params['Matcher'] = {} params['Matcher']['HttpCode'] = module.params.get( "successful_response_codes") # Get target type if target_type == 'ip': fail_if_ip_target_type_not_supported(module) # Get target group tg = get_target_group(connection, module) if tg: diffs = [ param for param in ('Port', 'Protocol', 'VpcId') if tg.get(param) != params.get(param) ] if diffs: module.fail_json( msg="Cannot modify %s parameter(s) for a target group" % ", ".join(diffs)) # Target group exists so check health check parameters match what has been passed health_check_params = dict() # Modify health check if anything set if health_options: # Health check protocol if 'HealthCheckProtocol' in params and tg[ 'HealthCheckProtocol'] != params['HealthCheckProtocol']: health_check_params['HealthCheckProtocol'] = params[ 'HealthCheckProtocol'] # Health check port if 'HealthCheckPort' in params and tg['HealthCheckPort'] != params[ 'HealthCheckPort']: health_check_params['HealthCheckPort'] = params[ 'HealthCheckPort'] # Health check interval if 'HealthCheckIntervalSeconds' in params and tg[ 'HealthCheckIntervalSeconds'] != params[ 'HealthCheckIntervalSeconds']: health_check_params['HealthCheckIntervalSeconds'] = params[ 'HealthCheckIntervalSeconds'] # Health check timeout if 'HealthCheckTimeoutSeconds' in params and tg[ 'HealthCheckTimeoutSeconds'] != params[ 'HealthCheckTimeoutSeconds']: health_check_params['HealthCheckTimeoutSeconds'] = params[ 'HealthCheckTimeoutSeconds'] # Healthy threshold if 'HealthyThresholdCount' in params and tg[ 'HealthyThresholdCount'] != params['HealthyThresholdCount']: health_check_params['HealthyThresholdCount'] = params[ 'HealthyThresholdCount'] # Unhealthy threshold if 'UnhealthyThresholdCount' in params and tg[ 'UnhealthyThresholdCount'] != params[ 'UnhealthyThresholdCount']: health_check_params['UnhealthyThresholdCount'] = params[ 'UnhealthyThresholdCount'] # Only need to check response code and path for http(s) health checks if tg['HealthCheckProtocol'] in ['HTTP', 'HTTPS']: # Health check path if 'HealthCheckPath' in params and tg[ 'HealthCheckPath'] != params['HealthCheckPath']: health_check_params['HealthCheckPath'] = params[ 'HealthCheckPath'] # Matcher (successful response codes) # TODO: required and here? if 'Matcher' in params: current_matcher_list = tg['Matcher']['HttpCode'].split(',') requested_matcher_list = params['Matcher'][ 'HttpCode'].split(',') if set(current_matcher_list) != set( requested_matcher_list): health_check_params['Matcher'] = {} health_check_params['Matcher']['HttpCode'] = ','.join( requested_matcher_list) try: if health_check_params: connection.modify_target_group( TargetGroupArn=tg['TargetGroupArn'], **health_check_params) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't update target group") # Do we need to modify targets? if module.params.get("modify_targets"): # get list of current target instances. I can't see anything like a describe targets in the doco so # describe_target_health seems to be the only way to get them try: current_targets = connection.describe_target_health( TargetGroupArn=tg['TargetGroupArn']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get target group health") if module.params.get("targets"): if target_type != "lambda": params['Targets'] = module.params.get("targets") # Correct type of target ports for target in params['Targets']: target['Port'] = int( target.get('Port', module.params.get('port'))) current_instance_ids = [] for instance in current_targets[ 'TargetHealthDescriptions']: current_instance_ids.append(instance['Target']['Id']) new_instance_ids = [] for instance in params['Targets']: new_instance_ids.append(instance['Id']) add_instances = set(new_instance_ids) - set( current_instance_ids) if add_instances: instances_to_add = [] for target in params['Targets']: if target['Id'] in add_instances: instances_to_add.append({ 'Id': target['Id'], 'Port': target['Port'] }) changed = True try: connection.register_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_add) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't register targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, tg['TargetGroupArn'], instances_to_add, 'healthy') if not status_achieved: module.fail_json( msg= 'Error waiting for target registration to be healthy - please check the AWS console' ) remove_instances = set(current_instance_ids) - set( new_instance_ids) if remove_instances: instances_to_remove = [] for target in current_targets[ 'TargetHealthDescriptions']: if target['Target']['Id'] in remove_instances: instances_to_remove.append({ 'Id': target['Target']['Id'], 'Port': target['Target']['Port'] }) changed = True try: connection.deregister_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused') if not status_achieved: module.fail_json( msg= 'Error waiting for target deregistration - please check the AWS console' ) # register lambda target else: try: changed = False target = module.params.get("targets")[0] if len(current_targets["TargetHealthDescriptions"] ) == 0: changed = True else: for item in current_targets[ "TargetHealthDescriptions"]: if target["Id"] != item["Target"]["Id"]: changed = True break # only one target is possible with lambda if changed: if target.get("Id"): response = connection.register_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=[{ "Id": target['Id'] }]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't register targets") else: if target_type != "lambda": current_instances = current_targets[ 'TargetHealthDescriptions'] if current_instances: instances_to_remove = [] for target in current_targets[ 'TargetHealthDescriptions']: instances_to_remove.append({ 'Id': target['Target']['Id'], 'Port': target['Target']['Port'] }) changed = True try: connection.deregister_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused') if not status_achieved: module.fail_json( msg= 'Error waiting for target deregistration - please check the AWS console' ) # remove lambda targets else: changed = False if current_targets["TargetHealthDescriptions"]: changed = True # only one target is possible with lambda target_to_remove = current_targets[ "TargetHealthDescriptions"][0]["Target"]["Id"] if changed: connection.deregister_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=[{ "Id": target_to_remove }]) else: try: connection.create_target_group(**params) changed = True new_target_group = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create target group") tg = get_target_group(connection, module) if module.params.get("targets"): if target_type != "lambda": params['Targets'] = module.params.get("targets") try: connection.register_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=params['Targets']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't register targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, tg['TargetGroupArn'], params['Targets'], 'healthy') if not status_achieved: module.fail_json( msg= 'Error waiting for target registration to be healthy - please check the AWS console' ) else: try: target = module.params.get("targets")[0] response = connection.register_targets( TargetGroupArn=tg['TargetGroupArn'], Targets=[{ "Id": target["Id"] }]) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't register targets") # Now set target group attributes update_attributes = [] # Get current attributes current_tg_attributes = get_tg_attributes(connection, module, tg['TargetGroupArn']) if deregistration_delay_timeout is not None: if str(deregistration_delay_timeout) != current_tg_attributes[ 'deregistration_delay_timeout_seconds']: update_attributes.append({ 'Key': 'deregistration_delay.timeout_seconds', 'Value': str(deregistration_delay_timeout) }) if stickiness_enabled is not None: if stickiness_enabled and current_tg_attributes[ 'stickiness_enabled'] != "true": update_attributes.append({ 'Key': 'stickiness.enabled', 'Value': 'true' }) if stickiness_lb_cookie_duration is not None: if str(stickiness_lb_cookie_duration) != current_tg_attributes[ 'stickiness_lb_cookie_duration_seconds']: update_attributes.append({ 'Key': 'stickiness.lb_cookie.duration_seconds', 'Value': str(stickiness_lb_cookie_duration) }) if stickiness_type is not None and "stickiness_type" in current_tg_attributes: if stickiness_type != current_tg_attributes['stickiness_type']: update_attributes.append({ 'Key': 'stickiness.type', 'Value': stickiness_type }) if update_attributes: try: connection.modify_target_group_attributes( TargetGroupArn=tg['TargetGroupArn'], Attributes=update_attributes) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # Something went wrong setting attributes. If this target group was created during this task, delete it to leave a consistent state if new_target_group: connection.delete_target_group( TargetGroupArn=tg['TargetGroupArn']) module.fail_json_aws(e, msg="Couldn't delete target group") # Tags - only need to play with tags if tags parameter has been set to something if tags: # Get tags current_tags = get_target_group_tags(connection, module, tg['TargetGroupArn']) # Delete necessary tags tags_need_modify, tags_to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict(current_tags), tags, purge_tags) if tags_to_delete: try: connection.remove_tags(ResourceArns=[tg['TargetGroupArn']], TagKeys=tags_to_delete) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't delete tags from target group") changed = True # Add/update tags if tags_need_modify: try: connection.add_tags( ResourceArns=[tg['TargetGroupArn']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't add tags to target group") changed = True # Get the target group again tg = get_target_group(connection, module) # Get the target group attributes again tg.update(get_tg_attributes(connection, module, tg['TargetGroupArn'])) # Convert tg to snake_case snaked_tg = camel_dict_to_snake_dict(tg) snaked_tg['tags'] = boto3_tag_list_to_ansible_dict( get_target_group_tags(connection, module, tg['TargetGroupArn'])) module.exit_json(changed=changed, **snaked_tg)
def main(): argument_spec = dict( resource=dict(required=True), tags=dict(type='dict'), purge_tags=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent', 'list']), ) required_if = [('state', 'present', ['tags']), ('state', 'absent', ['tags'])] module = AnsibleAWSModule(argument_spec=argument_spec, required_if=required_if, supports_check_mode=True) resource = module.params['resource'] tags = module.params['tags'] state = module.params['state'] purge_tags = module.params['purge_tags'] result = {'changed': False} ec2 = module.client('ec2') current_tags = get_tags(ec2, module, resource) if state == 'list': module.deprecate( 'Using the "list" state has been deprecated. Please use the ec2_tag_info module instead', version='2.14') module.exit_json(changed=False, tags=current_tags) add_tags, remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags) remove_tags = {} if state == 'absent': for key in tags: if key in current_tags and (tags[key] is None or current_tags[key] == tags[key]): remove_tags[key] = current_tags[key] for key in remove: remove_tags[key] = current_tags[key] if remove_tags: result['changed'] = True result['removed_tags'] = remove_tags if not module.check_mode: try: ec2.delete_tags( Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(remove_tags)) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to remove tags {0} from resource {1}'.format( remove_tags, resource)) if state == 'present' and add_tags: result['changed'] = True result['added_tags'] = add_tags current_tags.update(add_tags) if not module.check_mode: try: ec2.create_tags(Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(add_tags)) except (BotoCoreError, ClientError) as e: module.fail_json_aws( e, msg='Failed to set tags {0} on resource {1}'.format( add_tags, resource)) result['tags'] = get_tags(ec2, module, resource) module.exit_json(**result)
def create_or_update_elb(elb_obj): """Create ELB or modify main attributes. json_exit here""" if elb_obj.elb: # ELB exists so check subnets, security groups and tags match what has been passed # Subnets if not elb_obj.compare_subnets(): elb_obj.modify_subnets() # Security Groups if not elb_obj.compare_security_groups(): elb_obj.modify_security_groups() # Tags - only need to play with tags if tags parameter has been set to something if elb_obj.tags is not None: # Delete necessary tags tags_need_modify, tags_to_delete = compare_aws_tags( boto3_tag_list_to_ansible_dict(elb_obj.elb['tags']), boto3_tag_list_to_ansible_dict(elb_obj.tags), elb_obj.purge_tags) if tags_to_delete: elb_obj.delete_tags(tags_to_delete) # Add/update tags if tags_need_modify: elb_obj.modify_tags() else: # Create load balancer elb_obj.create_elb() # ELB attributes elb_obj.update_elb_attributes() elb_obj.modify_elb_attributes() # Listeners listeners_obj = ELBListeners(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn']) listeners_to_add, listeners_to_modify, listeners_to_delete = listeners_obj.compare_listeners( ) # Delete listeners for listener_to_delete in listeners_to_delete: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_delete, elb_obj.elb['LoadBalancerArn']) listener_obj.delete() listeners_obj.changed = True # Add listeners for listener_to_add in listeners_to_add: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_add, elb_obj.elb['LoadBalancerArn']) listener_obj.add() listeners_obj.changed = True # Modify listeners for listener_to_modify in listeners_to_modify: listener_obj = ELBListener(elb_obj.connection, elb_obj.module, listener_to_modify, elb_obj.elb['LoadBalancerArn']) listener_obj.modify() listeners_obj.changed = True # If listeners changed, mark ELB as changed if listeners_obj.changed: elb_obj.changed = True # Rules of each listener for listener in listeners_obj.listeners: if 'Rules' in listener: rules_obj = ELBListenerRules(elb_obj.connection, elb_obj.module, elb_obj.elb['LoadBalancerArn'], listener['Rules'], listener['Port']) rules_to_add, rules_to_modify, rules_to_delete = rules_obj.compare_rules( ) # Delete rules if elb_obj.module.params['purge_rules']: for rule in rules_to_delete: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, {'RuleArn': rule}, rules_obj.listener_arn) rule_obj.delete() elb_obj.changed = True # Add rules for rule in rules_to_add: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn) rule_obj.create() elb_obj.changed = True # Modify rules for rule in rules_to_modify: rule_obj = ELBListenerRule(elb_obj.connection, elb_obj.module, rule, rules_obj.listener_arn) rule_obj.modify() elb_obj.changed = True # Get the ELB again elb_obj.update() # Get the ELB listeners again listeners_obj.update() # Update the ELB attributes elb_obj.update_elb_attributes() # Convert to snake_case and merge in everything we want to return to the user snaked_elb = camel_dict_to_snake_dict(elb_obj.elb) snaked_elb.update(camel_dict_to_snake_dict(elb_obj.elb_attributes)) snaked_elb['listeners'] = [] for listener in listeners_obj.current_listeners: # For each listener, get listener rules listener['rules'] = get_elb_listener_rules(elb_obj.connection, elb_obj.module, listener['ListenerArn']) snaked_elb['listeners'].append(camel_dict_to_snake_dict(listener)) # Change tags to ansible friendly dict snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(snaked_elb['tags']) elb_obj.module.exit_json(changed=elb_obj.changed, **snaked_elb)
def update_key(connection, module, key): changed = False alias = module.params.get('alias') key_id = key['key_arn'] if alias: changed = update_alias(connection, module, key_id, alias) or changed 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' changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Failed to cancel key deletion") changed = ensure_enabled_disabled(connection, module, key) or changed 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: 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") # 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 = {} 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") desired_grants = module.params.get('grants') existing_grants = key['grants'] to_add, to_remove = compare_grants(existing_grants, desired_grants, module.params.get('purge_grants')) if to_remove: 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") # make results consistent with kms_facts before returning result = get_key_details(connection, module, key_id) module.exit_json(changed=changed, **result)