def config_details(client, module): """ Returns configuration details for one or all lambda functions. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ lambda_info = dict() function_name = module.params.get('function_name') if function_name: try: lambda_info.update(client.get_function_configuration(aws_retry=True, FunctionName=function_name)) except is_boto3_error_code('ResourceNotFoundException'): lambda_info.update(function={}) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name)) else: try: lambda_info.update(function_list=_paginate(client, 'list_functions')['Functions']) except is_boto3_error_code('ResourceNotFoundException'): lambda_info.update(function_list=[]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Trying to get function list") functions = dict() for func in lambda_info.pop('function_list', []): func['tags'] = client.get_function(FunctionName=func['FunctionName']).get('Tags', {}) functions[func['FunctionName']] = camel_dict_to_snake_dict(func) return functions return {function_name: camel_dict_to_snake_dict(lambda_info)}
def setup_removal(client, module): params = dict() changed = False params['DryRun'] = module.check_mode if isinstance(module.params.get('vpc_endpoint_id'), string_types): params['VpcEndpointIds'] = [module.params.get('vpc_endpoint_id')] else: params['VpcEndpointIds'] = module.params.get('vpc_endpoint_id') try: result = client.delete_vpc_endpoints(**params)['Unsuccessful'] if len(result) < len(params['VpcEndpointIds']): changed = True # For some reason delete_vpc_endpoints doesn't throw exceptions it # returns a list of failed 'results' instead. Throw these so we can # catch them the way we expect for r in result: try: raise botocore.exceptions.ClientError(r, 'delete_vpc_endpoints') except is_boto3_error_code('InvalidVpcEndpoint.NotFound'): continue except is_boto3_error_code('DryRunOperation'): changed = True result = 'Would have deleted VPC Endpoint if not in check mode' except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, "Failed to delete VPC endpoint") return changed, result
def await_stack_set_operation(module, cfn, stack_set_name, operation_id, max_wait): wait_start = datetime.datetime.now() operation = None for i in range(max_wait // 15): try: operation = cfn.describe_stack_set_operation(StackSetName=stack_set_name, OperationId=operation_id) if operation['StackSetOperation']['Status'] not in ('RUNNING', 'STOPPING'): # Stack set has completed operation break except is_boto3_error_code('StackSetNotFound'): # pylint: disable=duplicate-except pass except is_boto3_error_code('OperationNotFound'): # pylint: disable=duplicate-except pass time.sleep(15) if operation and operation['StackSetOperation']['Status'] not in ('FAILED', 'STOPPED'): await_stack_instance_completion( module, cfn, stack_set_name=stack_set_name, # subtract however long we waited already max_wait=int(max_wait - (datetime.datetime.now() - wait_start).total_seconds()), ) elif operation and operation['StackSetOperation']['Status'] in ('FAILED', 'STOPPED'): pass else: module.warn( "Timed out waiting for operation {0} on stack set {1} after {2} seconds. Returning unfinished operation".format( operation_id, stack_set_name, max_wait ) )
def update_stack_set(module, stack_params, cfn): # if the state is present and the stack already exists, we try to update it. # AWS will tell us if the stack template and parameters are the same and # don't need to be updated. try: cfn.update_stack_set(**stack_params) except is_boto3_error_code('StackSetNotFound') as err: # pylint: disable=duplicate-except module.fail_json_aws(err, msg="Failed to find stack set. Check the name & region.") except is_boto3_error_code('StackInstanceNotFound') as err: # pylint: disable=duplicate-except module.fail_json_aws(err, msg="One or more stack instances were not found for this stack set. Double check " "the `accounts` and `regions` parameters.") except is_boto3_error_code('OperationInProgressException') as err: # pylint: disable=duplicate-except module.fail_json_aws( err, msg="Another operation is already in progress on this stack set - please try again later. When making " "multiple cloudformation_stack_set calls, it's best to enable `wait: yes` to avoid unfinished op errors.") except (ClientError, BotoCoreError) as err: # pylint: disable=duplicate-except module.fail_json_aws(err, msg="Could not update stack set.") if module.params.get('wait'): await_stack_set_operation( module, cfn, operation_id=stack_params['OperationId'], stack_set_name=stack_params['StackSetName'], max_wait=module.params.get('wait_timeout'), ) return True
def update_resource(client, module, params, result): current_params = client.describe_delivery_channels( DeliveryChannelNames=[params['name']], aws_retry=True, ) if params != current_params['DeliveryChannels'][0]: try: retry_unavailable_iam_on_put_delivery( client.put_delivery_channel, )(DeliveryChannel=params, ) result['changed'] = True result['channel'] = camel_dict_to_snake_dict( resource_exists(client, module, params)) return result except is_boto3_error_code('InvalidS3KeyPrefixException') as e: module.fail_json_aws( e, msg= "The `s3_prefix` parameter was invalid. Try '/' for no prefix") except is_boto3_error_code('InsufficientDeliveryPolicyException') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="The `s3_prefix` or `s3_bucket` parameter is invalid. " "Make sure the bucket exists and is available") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Couldn't create AWS Config delivery channel")
def create_eigw(module, conn, vpc_id): """ Create EIGW. module : AnsibleAWSModule object conn : boto3 client connection object vpc_id : ID of the VPC we are operating on """ gateway_id = None changed = False try: response = conn.create_egress_only_internet_gateway(DryRun=module.check_mode, VpcId=vpc_id) except is_boto3_error_code('DryRunOperation'): # When boto3 method is run with DryRun=True it returns an error on success # We need to catch the error and return something valid changed = True except is_boto3_error_code('InvalidVpcID.NotFound') as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="invalid vpc ID '{0}' provided".format(vpc_id)) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Could not create Egress-Only Internet Gateway for vpc ID {0}".format(vpc_id)) if not module.check_mode: gateway = response.get('EgressOnlyInternetGateway', {}) state = gateway.get('Attachments', [{}])[0].get('State') gateway_id = gateway.get('EgressOnlyInternetGatewayId') if gateway_id and state in ('attached', 'attaching'): changed = True else: # EIGW gave back a bad attachment state or an invalid response so we error out module.fail_json(msg='Unable to create and attach Egress Only Internet Gateway to VPCId: {0}. Bad or no state in response'.format(vpc_id), **camel_dict_to_snake_dict(response)) return changed, gateway_id
def existing_templates(module): ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) matches = None try: if module.params.get('template_id'): matches = ec2.describe_launch_templates(LaunchTemplateIds=[module.params.get('template_id')], aws_retry=True) elif module.params.get('template_name'): matches = ec2.describe_launch_templates(LaunchTemplateNames=[module.params.get('template_name')], aws_retry=True) except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException') as e: # no named template was found, return nothing/empty versions return None, [] except is_boto3_error_code('InvalidLaunchTemplateId.Malformed') as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Launch template with ID {0} is not a valid ID. It should start with `lt-....`'.format( module.params.get('launch_template_id'))) except is_boto3_error_code('InvalidLaunchTemplateId.NotFoundException') as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg='Launch template with ID {0} could not be found, please supply a name ' 'instead so that a new template can be created'.format(module.params.get('launch_template_id'))) except (ClientError, BotoCoreError, WaiterError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Could not check existing launch templates. This may be an IAM permission problem.') else: template = matches['LaunchTemplates'][0] template_id, template_version, template_default = template['LaunchTemplateId'], template['LatestVersionNumber'], template['DefaultVersionNumber'] try: return template, ec2.describe_launch_template_versions(LaunchTemplateId=template_id, aws_retry=True)['LaunchTemplateVersions'] except (ClientError, BotoCoreError, WaiterError) as e: module.fail_json_aws(e, msg='Could not find launch template versions for {0} (ID: {1}).'.format(template['LaunchTemplateName'], template_id))
def get_parameter_value(self, client, ssm_dict, term, on_missing, on_denied): ssm_dict["Name"] = term try: response = client.get_parameter(**ssm_dict) return response['Parameter']['Value'] except is_boto3_error_code('ParameterNotFound'): if on_missing == 'error': raise AnsibleError( "Failed to find SSM parameter %s (ResourceNotFound)" % term) elif on_missing == 'warn': self._display.warning( 'Skipping, did not find SSM parameter %s' % term) except is_boto3_error_code('AccessDeniedException'): # pylint: disable=duplicate-except if on_denied == 'error': raise AnsibleError( "Failed to access SSM parameter %s (AccessDenied)" % term) elif on_denied == 'warn': self._display.warning( 'Skipping, access denied for SSM parameter %s' % term) except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except raise AnsibleError("SSM lookup exception: {0}".format( to_native(e))) return None
def delete_metrics_configuration(client, module): bucket_name = module.params.get('bucket_name') mc_id = module.params.get('id') try: client.get_bucket_metrics_configuration(aws_retry=True, Bucket=bucket_name, Id=mc_id) except is_boto3_error_code('NoSuchConfiguration'): module.exit_json(changed=False) except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to get bucket metrics configuration") if module.check_mode: module.exit_json(changed=True) try: client.delete_bucket_metrics_configuration(aws_retry=True, Bucket=bucket_name, Id=mc_id) except is_boto3_error_code('NoSuchConfiguration'): module.exit_json(changed=False) except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Failed to delete bucket metrics configuration '%s'" % mc_id) module.exit_json(changed=True)
def _describe_nat_gateways(client, module, **params): try: paginator = client.get_paginator('describe_nat_gateways') return paginator.paginate(**params).build_full_result()['NatGateways'] except is_boto3_error_code('InvalidNatGatewayID.NotFound'): module.exit_json(msg="NAT gateway not found.") except is_boto3_error_code('NatGatewayMalformed'): # pylint: disable=duplicate-except module.fail_json_aws(msg="NAT gateway id is malformed.")
def get_dynamodb_table(): table_name = module.params.get('name') try: table = _describe_table(TableName=table_name) except is_boto3_error_code('ResourceNotFoundException'): return None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failed to describe table') table = table['Table'] try: tags = client.list_tags_of_resource( aws_retry=True, ResourceArn=table['TableArn'])['Tags'] except is_boto3_error_code('AccessDeniedException'): module.warn('Permission denied when listing tags') tags = [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failed to list table tags') tags = boto3_tag_list_to_ansible_dict(tags) table = camel_dict_to_snake_dict(table) # Put some of the values into places people will expect them table['arn'] = table['table_arn'] table['name'] = table['table_name'] table['status'] = table['table_status'] table['id'] = table['table_id'] table['size'] = table['table_size_bytes'] table['tags'] = tags if 'table_class_summary' in table: table['table_class'] = table['table_class_summary']['table_class'] # billing_mode_summary doesn't always seem to be set but is always set for PAY_PER_REQUEST # and when updating the billing_mode if 'billing_mode_summary' in table: table['billing_mode'] = table['billing_mode_summary']['billing_mode'] else: table['billing_mode'] = "PROVISIONED" # convert indexes into something we can easily search against attributes = table['attribute_definitions'] global_index_map = dict() local_index_map = dict() for index in table.get('global_secondary_indexes', []): idx = _decode_index(index, attributes, type_prefix='global_') global_index_map[idx['name']] = idx for index in table.get('local_secondary_indexes', []): idx = _decode_index(index, attributes) local_index_map[idx['name']] = idx table['_global_index_map'] = global_index_map table['_local_index_map'] = local_index_map return table
def create_vpc_endpoint(client, module): params = dict() changed = False token_provided = False params['VpcId'] = module.params.get('vpc_id') params['VpcEndpointType'] = module.params.get('vpc_endpoint_type') params['ServiceName'] = module.params.get('service') params['DryRun'] = module.check_mode if module.params.get('route_table_ids'): params['RouteTableIds'] = module.params.get('route_table_ids') if module.params.get('client_token'): token_provided = True request_time = datetime.datetime.utcnow() params['ClientToken'] = module.params.get('client_token') policy = None if module.params.get('policy'): try: policy = json.loads(module.params.get('policy')) except ValueError as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) elif module.params.get('policy_file'): try: with open(module.params.get('policy_file'), 'r') as json_data: policy = json.load(json_data) except Exception as e: module.fail_json(msg=str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response)) if policy: params['PolicyDocument'] = json.dumps(policy) try: changed = True result = camel_dict_to_snake_dict(client.create_vpc_endpoint(**params)['VpcEndpoint']) if token_provided and (request_time > result['creation_timestamp'].replace(tzinfo=None)): changed = False elif module.params.get('wait') and not module.check_mode: status_achieved, result = wait_for_status(client, module, result['vpc_endpoint_id'], 'available') if not status_achieved: module.fail_json(msg='Error waiting for vpc endpoint to become available - please check the AWS console') except is_boto3_error_code('DryRunOperation'): changed = True result = 'Would have created VPC Endpoint if not in check mode' except is_boto3_error_code('IdempotentParameterMismatch'): # pylint: disable=duplicate-except module.fail_json(msg="IdempotentParameterMismatch - updates of endpoints are not allowed by the API") except is_boto3_error_code('RouteAlreadyExists'): # pylint: disable=duplicate-except module.fail_json(msg="RouteAlreadyExists for one of the route tables - update is not allowed by the API") except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to create VPC.") return changed, result
def ensure_present(module, connection): groupname = module.params['name'] tags = module.params.get('tags') changed = False errors = [] try: response = connection.describe_db_parameter_groups( aws_retry=True, DBParameterGroupName=groupname) except is_boto3_error_code('DBParameterGroupNotFound'): response = None except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't access parameter group information") if not response: params = dict(DBParameterGroupName=groupname, DBParameterGroupFamily=module.params['engine'], Description=module.params['description']) if tags: params['Tags'] = ansible_dict_to_boto3_tag_list(tags) if not module.check_mode: try: response = connection.create_db_parameter_group(aws_retry=True, **params) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create parameter group") else: group = response['DBParameterGroups'][0] if tags: changed = update_tags(module, connection, group, tags) if module.params.get('params'): params_changed, errors = update_parameters(module, connection) changed = changed or params_changed try: response = connection.describe_db_parameter_groups( aws_retry=True, DBParameterGroupName=groupname) group = camel_dict_to_snake_dict(response['DBParameterGroups'][0]) except is_boto3_error_code('DBParameterGroupNotFound'): module.exit_json(changed=True, errors=errors) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't obtain parameter group information") try: tags = connection.list_tags_for_resource( aws_retry=True, ResourceName=group['db_parameter_group_arn'])['TagList'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't obtain parameter group tags") group['tags'] = boto3_tag_list_to_ansible_dict(tags) module.exit_json(changed=changed, errors=errors, **group)
def get_eip_allocation_id_by_address(client, module, eip_address): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client module: AnsibleAWSModule class instance eip_address (str): The Elastic IP Address of the EIP. Basic Usage: >>> client = boto3.client('ec2') >>> module = AnsibleAWSModule(...) >>> eip_address = '52.87.29.36' >>> get_eip_allocation_id_by_address(client, module, eip_address) 'eipalloc-36014da3' Returns: Tuple (str, str) """ params = { 'PublicIps': [eip_address], } allocation_id = None msg = '' try: allocations = client.describe_addresses(aws_retry=True, **params)['Addresses'] if len(allocations) == 1: allocation = allocations[0] else: allocation = None if allocation: if allocation.get('Domain') != 'vpc': msg = ( "EIP {0} is a non-VPC EIP, please allocate a VPC scoped EIP" .format(eip_address)) else: allocation_id = allocation.get('AllocationId') except is_boto3_error_code('InvalidAddress.Malformed') as e: module.fail_json(msg='EIP address {0} is invalid.'.format(eip_address)) except is_boto3_error_code('InvalidAddress.NotFound') as e: # pylint: disable=duplicate-except msg = ("EIP {0} does not exist".format(eip_address)) allocation_id = None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e) return allocation_id, msg
def delete(module, connection, name): """ Delete an ElastiCache backup. """ try: response = connection.delete_snapshot(SnapshotName=name) changed = True except is_boto3_error_code('SnapshotNotFoundFault'): response = {} changed = False except is_boto3_error_code('InvalidSnapshotState'): # pylint: disable=duplicate-except module.fail_json(msg="Error: InvalidSnapshotState. The snapshot is not in an available state or failed state to allow deletion." "You may need to wait a few minutes.") except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Unable to delete the snapshot.") return response, changed
def create_or_update_metrics_configuration(client, module): bucket_name = module.params.get('bucket_name') mc_id = module.params.get('id') filter_prefix = module.params.get('filter_prefix') filter_tags = module.params.get('filter_tags') try: response = client.get_bucket_metrics_configuration(aws_retry=True, Bucket=bucket_name, Id=mc_id) metrics_configuration = response['MetricsConfiguration'] except is_boto3_error_code('NoSuchConfiguration'): metrics_configuration = None except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to get bucket metrics configuration") new_configuration = _create_metrics_configuration(mc_id, filter_prefix, filter_tags) if metrics_configuration: if metrics_configuration == new_configuration: module.exit_json(changed=False) if module.check_mode: module.exit_json(changed=True) try: client.put_bucket_metrics_configuration( aws_retry=True, Bucket=bucket_name, Id=mc_id, MetricsConfiguration=new_configuration ) except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to put bucket metrics configuration '%s'" % mc_id) module.exit_json(changed=True)
def instance_info(module, conn): instance_name = module.params.get('db_instance_identifier') filters = module.params.get('filters') params = dict() if instance_name: params['DBInstanceIdentifier'] = instance_name if filters: params['Filters'] = ansible_dict_to_boto3_filter_list(filters) paginator = conn.get_paginator('describe_db_instances') try: results = paginator.paginate(**params).build_full_result()['DBInstances'] except is_boto3_error_code('DBInstanceNotFound'): results = [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, "Couldn't get instance information") for instance in results: try: instance['Tags'] = boto3_tag_list_to_ansible_dict(conn.list_tags_for_resource(ResourceName=instance['DBInstanceArn'], aws_retry=True)['TagList']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't get tags for instance %s" % instance['DBInstanceIdentifier']) return dict(changed=False, instances=[camel_dict_to_snake_dict(instance, ignore_list=['Tags']) for instance in results])
def get_subnet_group(name): try: groups = client.describe_cache_subnet_groups( aws_retry=True, CacheSubnetGroupName=name, )['CacheSubnetGroups'] except is_boto3_error_code('CacheSubnetGroupNotFoundFault'): return None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to describe subnet group") if not groups: return None if len(groups) > 1: module.fail_aws( msg="Found multiple matches for subnet group", cache_subnet_groups=camel_dict_to_snake_dict(groups), ) subnet_group = camel_dict_to_snake_dict(groups[0]) subnet_group['name'] = subnet_group['cache_subnet_group_name'] subnet_group['description'] = subnet_group['cache_subnet_group_description'] subnet_ids = list(s['subnet_identifier'] for s in subnet_group['subnets']) subnet_group['subnet_ids'] = subnet_ids return subnet_group
def describe_transit_gateways(self): """ Describe transit gateways. module : AnsibleAWSModule object connection : boto3 client connection object """ # collect parameters filters = ansible_dict_to_boto3_filter_list( self._module.params['filters']) transit_gateway_ids = self._module.params['transit_gateway_ids'] # init empty list for return vars transit_gateway_info = list() # Get the basic transit gateway info try: response = self._connection.describe_transit_gateways( TransitGatewayIds=transit_gateway_ids, Filters=filters) except is_boto3_error_code('InvalidTransitGatewayID.NotFound'): self._results['transit_gateways'] = [] return for transit_gateway in response['TransitGateways']: transit_gateway_info.append( camel_dict_to_snake_dict(transit_gateway, ignore_list=['Tags'])) # convert tag list to ansible dict transit_gateway_info[-1]['tags'] = boto3_tag_list_to_ansible_dict( transit_gateway.get('Tags', [])) self._results['transit_gateways'] = transit_gateway_info return
def delete_table(current_table): if not current_table: return False if module.check_mode: return True table_name = module.params.get('name') # If an index is mid-update then we have to wait for the update to complete # before deletion will succeed long_retry = AWSRetry.jittered_backoff( retries=45, delay=5, max_delay=30, catch_extra_error_codes=['LimitExceededException', 'ResourceInUseException'], ) try: long_retry(client.delete_table)(TableName=table_name) except is_boto3_error_code('ResourceNotFoundException'): return False except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg='Failed to delete table') if module.params.get('wait'): wait_not_exists() return True
def find_address(ec2, module, public_ip, device_id, is_instance=True): """ Find an existing Elastic IP address """ filters = [] kwargs = {} if public_ip: kwargs["PublicIps"] = [public_ip] elif device_id: if is_instance: filters.append({"Name": 'instance-id', "Values": [device_id]}) else: filters.append({'Name': 'network-interface-id', "Values": [device_id]}) if len(filters) > 0: kwargs["Filters"] = filters elif len(filters) == 0 and public_ip is None: return None try: addresses = ec2.describe_addresses(**kwargs) except is_boto3_error_code('InvalidAddress.NotFound') as e: # If we're releasing and we can't find it, it's already gone... if module.params.get('state') == 'absent': module.exit_json(changed=False) module.fail_json_aws(e, msg="Couldn't obtain list of existing Elastic IP addresses") addresses = addresses["Addresses"] if len(addresses) == 1: return addresses[0] elif len(addresses) > 1: msg = "Found more than one address using args {0}".format(kwargs) msg += "Addresses found: {0}".format(addresses) module.fail_json_aws(botocore.exceptions.ClientError, msg=msg)
def version_details(client, module): """ Returns all lambda function versions. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ lambda_info = dict() function_name = module.params.get('function_name') if function_name: params = dict() if module.params.get('max_items'): params['MaxItems'] = module.params.get('max_items') if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') try: lambda_info.update(versions=client.list_versions_by_function( FunctionName=function_name, **params)['Versions']) except is_boto3_error_code('ResourceNotFoundException'): lambda_info.update(versions=[]) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Trying to get {0} versions".format(function_name)) else: module.fail_json( msg='Parameter function_name required for query=versions.') return {function_name: camel_dict_to_snake_dict(lambda_info)}
def delete_unused_regex_pattern(self, regex_pattern_set_id): try: regex_pattern_set = self.client.get_regex_pattern_set( RegexPatternSetId=regex_pattern_set_id)['RegexPatternSet'] updates = list() for regex_pattern_string in regex_pattern_set[ 'RegexPatternStrings']: updates.append({ 'Action': 'DELETE', 'RegexPatternString': regex_pattern_string }) run_func_with_change_token_backoff( self.client, self.module, { 'RegexPatternSetId': regex_pattern_set_id, 'Updates': updates }, self.client.update_regex_pattern_set) run_func_with_change_token_backoff( self.client, self.module, {'RegexPatternSetId': regex_pattern_set_id}, self.client.delete_regex_pattern_set, wait=True) except is_boto3_error_code('WAFNonexistentItemException'): return except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except self.module.fail_json_aws(e, msg='Could not delete regex pattern')
def delete_eigw(module, connection, eigw_id): """ Delete EIGW. module : AnsibleAWSModule object connection : boto3 client connection object eigw_id : ID of the EIGW to delete """ changed = False try: response = connection.delete_egress_only_internet_gateway( aws_retry=True, DryRun=module.check_mode, EgressOnlyInternetGatewayId=eigw_id) except is_boto3_error_code('DryRunOperation'): changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Could not delete Egress-Only Internet Gateway {0} from VPC {1}" .format(eigw_id, module.vpc_id)) if not module.check_mode: changed = response.get('ReturnCode', False) return changed
def list_services_with_backoff(self, **kwargs): paginator = self.ecs.get_paginator('list_services') try: return paginator.paginate(**kwargs).build_full_result() except is_boto3_error_code('ClusterNotFoundException') as e: self.module.fail_json_aws( e, "Could not find cluster to list services")
def policy_details(client, module): """ Returns policy attached to a lambda function. :param client: AWS API client reference (boto3) :param module: Ansible module reference :return dict: """ if module.params.get('max_items') or module.params.get('next_marker'): module.fail_json( msg='Cannot specify max_items nor next_marker for query=policy.') lambda_info = dict() function_name = module.params.get('function_name') if function_name: try: # get_policy returns a JSON string so must convert to dict before reassigning to its key lambda_info.update(policy=json.loads( client.get_policy(FunctionName=function_name)['Policy'])) except is_boto3_error_code('ResourceNotFoundException'): lambda_info.update(policy={}) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws( e, msg="Trying to get {0} policy".format(function_name)) else: module.fail_json( msg='Parameter function_name required for query=policy.') return {function_name: camel_dict_to_snake_dict(lambda_info)}
def get_policy_statement(module, client): """Checks that policy exists and if so, that statement ID is present or absent. :param module: :param client: :return: """ sid = module.params['statement_id'] # set API parameters api_params = set_api_params(module, ('function_name', )) qualifier = get_qualifier(module) if qualifier: api_params.update(Qualifier=qualifier) policy_results = None # check if function policy exists try: policy_results = client.get_policy(**api_params) except is_boto3_error_code('ResourceNotFoundException'): return {} except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="retrieving function policy") # get_policy returns a JSON string so must convert to dict before reassigning to its key policy = json.loads(policy_results.get('Policy', '{}')) return extract_statement(policy, sid)
def get_subnet_group(name): try: groups = client.describe_cluster_subnet_groups( aws_retry=True, ClusterSubnetGroupName=name, )['ClusterSubnetGroups'] except is_boto3_error_code('ClusterSubnetGroupNotFoundFault'): return None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Failed to describe subnet group") if not groups: return None if len(groups) > 1: module.fail_aws( msg="Found multiple matches for subnet group", cluster_subnet_groups=camel_dict_to_snake_dict(groups), ) # No support for managing tags yet, but make sure that we don't need to # change the return value structure after it's been available in a release. tags = boto3_tag_list_to_ansible_dict(groups[0]['Tags']) subnet_group = camel_dict_to_snake_dict(groups[0]) subnet_group['tags'] = tags subnet_group['name'] = subnet_group['cluster_subnet_group_name'] subnet_ids = list(s['subnet_identifier'] for s in subnet_group['subnets']) subnet_group['subnet_ids'] = subnet_ids return subnet_group
def common_snapshot_info(module, conn, method, prefix, params): paginator = conn.get_paginator(method) try: results = paginator.paginate(**params).build_full_result()['%ss' % prefix] except is_boto3_error_code('%sNotFound' % prefix): results = [] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, "trying to get snapshot information") for snapshot in results: try: if snapshot['SnapshotType'] != 'shared': snapshot['Tags'] = boto3_tag_list_to_ansible_dict( conn.list_tags_for_resource(ResourceName=snapshot['%sArn' % prefix], aws_retry=True)['TagList']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, "Couldn't get tags for snapshot %s" % snapshot['%sIdentifier' % prefix]) return [ camel_dict_to_snake_dict(snapshot, ignore_list=['Tags']) for snapshot in results ]
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') as e: return None return current_rotation_status.get('KeyRotationEnabled')