---
schema_version: "1"
id: 61fe908b7c4c5284430febf94563b1b11f426a87d8ea073df5433e28c597ee99
title: Generate a Batch Mapping File for Externally Stored Files
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/ind/264-0-0/aps_external_storage_search_batch_mapping.md
canonical_url: https://help.salesforce.com/s/articleView?id=ind.aps_external_storage_search_batch_mapping.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: d3c4fc49d66cb3b97404044220260b655116499556ec399c96f681346a67c172
description: Prepare existing files for Amazon Bedrock or Amazon Kendra by generating a mapping file that associates Salesforce files with their Amazon S3 locations. You must complete this task only if you have existing files in Amazon S3 that you'd like to search.
products:
  - Public Sector
release: "264"
---
# Generate a Batch Mapping File for Externally Stored Files

Prepare existing files for Amazon Bedrock or Amazon Kendra by generating a mapping file that associates Salesforce files with their Amazon S3 locations. You must complete this task only if you have existing files in Amazon S3 that you'd like to search.

### Required Editions

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small permission">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="colgroup" class="slds-cell-wrap" colspan="2" style="text-align:center;vertical-align:top;" id="d105811e38">User Permissions Needed</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">To enable External Storage Search:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">Customize Application</td>

          </tr>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">To view permission sets:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">View Setup and Configuration</td>

          </tr>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">To assign permission sets:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">Assign Permission Sets</td>

          </tr>
          <tr class="">
            <td data-label="User Permissions Needed" class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">To create local External Client Apps:</td>

            <td class="slds-cell-wrap" style="vertical-align:top;" headers="d105811e38 ">Create, edit, and delete External Client Apps</td>

          </tr>
        </tbody>
      </table>

Before you begin, verify that you have:

- A Salesforce org with an Amazon S3 external data source of type ContentHubAWSS3. See [Set Up Semantic Search](https://help.salesforce.com/s/articleView?id=ind.aps_external_storage_search_setup.htm&release=264.0.0&type=5 "Configure AWS services, connect them to your Salesforce org, and add semantic search to record pages and Agentforce agents.").
- An AWS account with permission to create Lambda functions, IAM roles, and S3 buckets.
- An Amazon S3 bucket in which to store the mapping file.

## Create a Salesforce Integration User

1. From Salesforce Setup, create an integration user.
2. Configure the user with these values.
    
    | Field | Value |
    | --- | --- |
    | User License | Salesforce Integration |
    | Profile | Minimum Access - API Only Integrations |
3. Assign the Salesforce API Integration permission set license to the user.
4. Create a permission set that grants these permissions:
    - API Enabled
    - Query All Files
5. Assign the permission set to the integration user.

For more information, see [Integration Users](https://developer.salesforce.com/docs/platform/salesforce-slack-sdk/guide/events_integration_users.html?d=afx) in Salesforce Developer Documentation and [Create Permission Sets](https://help.salesforce.com/s/articleView?id=platform.perm_uapa_assign_required_permset.htm&type=5&language=en_US).

## Create an External Client App

Create a local external client app that uses the OAuth 2.0 client credentials flow. In the OAuth settings, add the Manage user data via APIs (api) scope and configure the Salesforce integration user as the run-as user.

For detailed instructions, see [Create a External Client App](https://help.salesforce.com/s/articleView?id=xcloud.create_a_local_external_client_app.htm&type=5&language=en_US) and [Configure a Client Credentials Flow](https://help.salesforce.com/s/articleView?id=xcloud.configure_client_credentials_flow_for_external_client_apps.htm&type=5&language=en_US).

## Create the Mapping File

Deploy an AWS Lambda function that queries Salesforce for externally stored files and generates a mapping between each Salesforce ContentDocument ID and its Amazon S3 location.

Configure the function through environment variables. Attach an IAM execution role with `s3:PutObject` that grants permission to write the mapping file to the designated S3 bucket and send logs to Amazon CloudWatch.

For instructions, see [Deploy Python Lambda functions](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html), [Using Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html), [Lambda execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) in the AWS documentation.

Here is reference Lambda code:

```
# 1. Authenticate to Salesforce (OAuth 2.0 Client Credentials)
token = sf_oauth_token(SF_INSTANCE_URL, SF_CLIENT_ID, SF_CLIENT_SECRET)

# 2. Query ContentVersion -> ContentDocumentId, ExternalDocumentInfo2, ExternalDataSource
soql = ("SELECT ContentDocumentId, ExternalDocumentInfo2, "
        "ExternalDataSource.NamedCredentialId, ExternalDataSource.MasterLabel "
        "FROM ContentVersion WHERE IsLatest=true AND ExternalDocumentInfo2!=null "
        "AND ExternalDataSource.Type='ContentHubAWSS3'")
records = sf_query_all(token, soql)

# 3. Resolve each Named Credential's S3 endpoint URL (Tooling API)
nc_url = {nc_id: sf_named_credential_url(token, nc_id)
          for nc_id in {r["ExternalDataSource"]["NamedCredentialId"] for r in records}}

# 4. Build the mapping keyed by the external document id
mapping = defaultdict(list)
for r in records:
    nc_id = r["ExternalDataSource"]["NamedCredentialId"]
    mapping[r["ExternalDocumentInfo2"]].append({
        "ContentDocumentId": r["ContentDocumentId"],
        "DataSourceLabel": r["ExternalDataSource"]["MasterLabel"],
        "NamedCredentialId": nc_id,
        "S3_Endpoint_URL": nc_url.get(nc_id),
    })

# 5. Upload the mapping JSON to S3
boto3.client("s3").put_object(
    Bucket=S3_BUCKET_NAME, Key=S3_FILE_NAME,
    Body=json.dumps(mapping, indent=4), ContentType="application/json")
```