

# Connecting to Salesforce Marketing Cloud
<a name="connecting-to-data-salesforce-marketing-cloud"></a>

Salesforce Marketing Cloud is a provider of marketing automation and analytics software for email, mobile, social, and online marketing. It also offers consulting and implementation services. As a Salesforce Marketing Cloud user, you can connect AWS Glue to your Salesforce Marketing Cloud account. Then, you can use Salesforce Marketing Cloud as a data source or destination in your ETL jobs. Run these jobs to transfer data between Salesforce Marketing Cloud and AWS services or other supported applications.

**Topics**
+ [AWS Glue support for Salesforce Marketing Cloud](salesforce-marketing-cloud-support.md)
+ [Policies containing the API operations for creating and using connections](salesforce-marketing-cloud-configuring-iam-permissions.md)
+ [Configuring Salesforce Marketing Cloud](salesforce-marketing-cloud-configuring.md)
+ [Configuring Salesforce Marketing Cloud connections](salesforce-marketing-cloud-configuring-connections.md)
+ [Reading from Salesforce Marketing Cloud entities](salesforce-marketing-cloud-reading-from-entities.md)
+ [Writing to Salesforce Marketing Cloud entities](salesforce-marketing-cloud-writing-to-entities.md)
+ [Salesforce Marketing Cloud connection options](salesforce-marketing-cloud-connection-options.md)
+ [Limitations and notes for Salesforce Marketing Cloud connector](salesforce-marketing-cloud-connector-limitations.md)

# AWS Glue support for Salesforce Marketing Cloud
<a name="salesforce-marketing-cloud-support"></a>

AWS Glue supports Salesforce Marketing Cloud as follows:

**Supported as a source?**  
Yes. You can use AWS Glue ETL jobs to query data from Salesforce Marketing Cloud.

**Supported as a target?**  
No.

**Supported Salesforce Marketing Cloud API versions**  
The following Salesforce Marketing Cloud API versions are supported:
+ v1

# Policies containing the API operations for creating and using connections
<a name="salesforce-marketing-cloud-configuring-iam-permissions"></a>

The following sample policy describes the required AWS IAM permissions for creating and using connections. If you are creating a new role, create a policy that contains the following:

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "glue:ListConnectionTypes",
        "glue:DescribeConnectionType",
        "glue:RefreshOAuth2Tokens",
        "glue:ListEntities",
        "glue:DescribeEntity"
      ],
      "Resource": "*"
    }
  ]
}
```

------

If you don't want to use the above method, alternatively use the following managed IAM policies:
+ [AWSGlueServiceRole](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole) – Grants access to resources that various AWS Glue processes require to run on your behalf. These resources include AWS Glue, Amazon S3, IAM, CloudWatch Logs, and Amazon EC2. If you follow the naming convention for resources specified in this policy, AWS Glue processes have the required permissions. This policy is typically attached to roles specified when defining crawlers, jobs, and development endpoints.
+ [AWSGlueConsoleFullAccess](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess) – Grants full access to AWS Glue resources when an identity that the policy is attached to uses the AWS Management Console. If you follow the naming convention for resources specified in this policy, users have full console capabilities. This policy is typically attached to users of the AWS Glue console.

# Configuring Salesforce Marketing Cloud
<a name="salesforce-marketing-cloud-configuring"></a>

Before you can use AWS Glue to transfer data from Salesforce Marketing Cloud, you must meet these requirements:

## Minimum requirements
<a name="salesforce-marketing-cloud-configuring-min-requirements"></a>

The following are minimum requirements:
+ You have a Salesforce Marketing Cloud account. For more information, see [Creating a Salesforce Marketing Cloud account](#salesforce-marketing-cloud-configuring-creating-salesforce-marketing-cloud-account).
+ Your Salesforce Marketing Cloud account is enabled for API access. API access is enabled by default for the Enterprise, Unlimited, Developer, and Performance editions.

If you meet these requirements, you’re ready to connect AWS Glue to your Salesforce Marketing Cloud account. For typical connections, you don't need do anything else in Salesforce Marketing Cloud.

## Creating a Salesforce Marketing Cloud account
<a name="salesforce-marketing-cloud-configuring-creating-salesforce-marketing-cloud-account"></a>

For Salesforce Marketing cloud, you need to contact the vendor for account creation. If you or your company has an association with Salesforce, contact your Salesforce account manager to request a Salesforce Marketing Cloud license. Otherwise, you can request contact from a Salesforce representative as follows: 

1. Go to https://www.salesforce.com/in/products/marketing-cloud/overview/ and choose **Sign up**.

1. Select the **Contact Us** link on the top right of the page.

1. Enter the required information in the form and choose **Contact Me**.

A Salesforce representative will contact you to discuss your requirements.

## Creating a project and OAuth 2.0 credentials
<a name="salesforce-marketing-cloud-configuring-creating-salesforce-marketing-cloud-project-oauth"></a>

To get a project and OAuth 2.0 credentials:

1. Log into your [Salesforce Marketing Cloud instance](https://mc.login.exacttarget.com/hub-cas/login) with your username and password and authenticate using your registered mobile number.

1. Click on your profile at the top right corner and then go to **Setup**.

1. Under **Platform Tools** choose **Apps** and then choose **Installed Packages**.  
![\[\]](http://docs.aws.amazon.com/glue/latest/dg/images/sfmc-platform-tools.png)

1. On the **Installed Packages** page, click **New** at the top right corner. Provide the name and description of the package.

   Save the package. After the package is saved, you can view the package details.

1. On the **Details** page of the package, under the **Component** section, choose **Add Component**.   
![\[\]](http://docs.aws.amazon.com/glue/latest/dg/images/sfmc-add-component.png)

1. Select the **Component Type** as 'API Integration' and click **Next**.

1. Select the **Integration Type** as 'Server-to-Server' (which has the client credentials OAuth grant type) and click **Next**.

1. Add the scopes based on your requirements and click **Save**.

# Configuring Salesforce Marketing Cloud connections
<a name="salesforce-marketing-cloud-configuring-connections"></a>

Salesforce Marketing Cloud supports the CLIENT CREDENTIALS grant type for OAuth2.
+ This grant type is considered 2-legged OAuth 2.0 as it is used by clients to obtain an access token outside of the context of a user. AWS Glue is able to use the client ID and client secret to authenticate the Salesforce Marketing Cloud APIs which are provided by custom services that you define.
+ Each custom service is owned by an API-only user which has a set of roles and permissions which authorize the service to perform specific actions. An access token is associated with a single custom service.
+ This grant type results in an access token which is short lived, and may be renewed by calling an identity endpoint.
+ For public Salesforce Marketing Cloud documentation for OAuth 2.0 with client credentials, see [Set Up Your Development Environment for Enhanced Packages](https://developer.salesforce.com/docs/marketing/marketing-cloud/guide/mc-dev-setup-enhanced.html).

To configure a Salesforce Marketing Cloud connection:

1. In AWS Secrets Manager, create a secret with the following details:

   1. For the customer managed connected app, the Secret should contain the connected app Consumer Secret with `USER_MANAGED_CLIENT_APPLICATION_CLIENT_SECRET` as key.

   1. Note: You must create a secret for your connections in AWS Glue.

1. In AWS Glue Glue Studio, create a connection under **Data Connections** by following the steps below:

   1. When selecting a **Connection type**, select Salesforce Marketing Cloud.

   1. Provide the `Subdomain Endpoint` of the Salesforce Marketing Cloud you want to connect to.

   1. Select the AWS IAM role which AWS Glue can assume and has permissions for following actions:

------
#### [ JSON ]

****  

      ```
      {
        "Version":"2012-10-17",		 	 	 
        "Statement": [
          {
            "Effect": "Allow",
            "Action": [
              "secretsmanager:DescribeSecret",
              "secretsmanager:GetSecretValue",
              "secretsmanager:PutSecretValue",
              "ec2:CreateNetworkInterface",
              "ec2:DescribeNetworkInterfaces",
              "ec2:DeleteNetworkInterface"
            ],
            "Resource": "*"
          }
        ]
      }
      ```

------

   1. Select the `secretName` which you want to use for this connection in AWS Glue to put the tokens.

   1. Select the network options if you want to use your network.

1. Grant the IAM role associated with your AWS Glue job permission to read `secretName`.

# Reading from Salesforce Marketing Cloud entities
<a name="salesforce-marketing-cloud-reading-from-entities"></a>

**Prerequisite**

A Salesforce Marketing Cloud object you would like to read from. You will need the object name such as `Activity` or `Campaigns`. The following table shows the supported entities.

**Supported entities for source**:


| Entity | Interface | Can be filtered | Supports limit | Supports Order by | Supports SELECT \$1 | Supports partitioning | 
| --- | --- | --- | --- | --- | --- | --- | 
| Event Notification Callback | REST | No | No | No | Yes | No | 
| Seed-List | REST | No | Yes | No | Yes | No | 
| Setup | REST | Yes | Yes | No | Yes | No | 
| Domain Verification | REST | Yes | Yes | Yes | Yes | No | 
| Objects Nested Tags | REST | Yes | No | No | Yes | No | 
| Contact | REST | No | Yes | No | Yes | No | 
| Event Notification Subscription | REST | No | No | No | Yes | No | 
| Messaging | REST | No | Yes | No | Yes | No | 
| Activity | SOAP | No | No | No | Yes | Yes | 
| Bounce Event | SOAP | No | No | No | Yes | Yes | 
| Click Event | SOAP | No | No | No | Yes | Yes | 
| Content Area | SOAP | No | No | No | Yes | Yes | 
| Data Extension | SOAP | No | Yes | No | Yes | Yes | 
| Email | SOAP | No | Yes | No | Yes | Yes | 
| Forwarded Email Event | SOAP | No | Yes | No | Yes | Yes | 
| Forward Email OptInEvent | SOAP | No | Yes | No | Yes | Yes | 
| Link | SOAP | No | Yes | No | Yes | Yes | 
| Link Send | SOAP | No | Yes | No | Yes | Yes | 
| List | SOAP | No | Yes | No | Yes | Yes | 
| List Subscriber | SOAP | No | Yes | No | Yes | Yes | 
| Not Sent Event | SOAP | No | Yes | No | Yes | Yes | 
| Open Event | SOAP | No | Yes | No | Yes | Yes | 
| Send | SOAP | No | Yes | No | Yes | Yes | 
| Sent Event | SOAP | No | Yes | No | Yes | Yes | 
| Subscriber | SOAP | No | Yes | No | Yes | Yes | 
| Survey Event | SOAP | No | Yes | No | Yes | Yes | 
| Unsub Event | SOAP | No | Yes | No | Yes | Yes | 
| Audit Events | REST | No | Yes | Yes | Yes | No | 
| Campaigns | REST | No | Yes | Yes | Yes | No | 
| Interactions | REST | No | Yes | Yes | Yes | No | 
| Content Assets | REST | No | Yes | Yes | Yes | No | 

**Example for REST**:

```
salesforcemarketingcloud_read = glueContext.create_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "Campaigns",
        "API_VERSION": "v1",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

**Example for SOAP**:

```
salesforcemarketingcloud_read = glueContext.create_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "Activity",
        "API_VERSION": "v1",
        "INSTANCE_URL": "https://**********************.soap.marketingcloudapis.com"
    }
)
```

**Salesforce Marketing Cloud entity and field details**:

The following tables describe the Salesforce Marketing Cloud entities. There are REST entities with static metadata and SOAP entities with dynamic metadata.

**REST entities with static metadata**:

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/glue/latest/dg/salesforce-marketing-cloud-reading-from-entities.html)

**SOAP entities with dynamic metadata**:

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/glue/latest/dg/salesforce-marketing-cloud-reading-from-entities.html)

## Partitioning queries
<a name="salesforce-marketing-cloud-reading-partitioning-queries"></a>

In Salesforce Marketing Cloud, the Integer and DateTime datatype fields support field-based partitioning.

You can provide the additional Spark options `PARTITION_FIELD`, `LOWER_BOUND`, `UPPER_BOUND`, and `NUM_PARTITIONS` if you want to utilize concurrency in Spark. With these parameters, the original query would be split into `NUM_PARTITIONS` number of sub-queries that can be executed by Spark tasks concurrently.
+ `PARTITION_FIELD`: the name of the field to be used to partition the query.
+ `LOWER_BOUND`: an **inclusive** lower bound value of the chosen partition field.

  For the timestamp field, we accept the Spark timestamp format used in Spark SQL queries.

  Examples of valid value:

  ```
  “2024-05-07T02:03:00.00Z"
  ```
+ `UPPER_BOUND`: an **exclusive** upper bound value of the chosen partition field.
+ `NUM_PARTITIONS`: the number of partitions.

Example:

```
salesforcemarketingcloud_read = glueContext.create_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "ListSubscriber",
        "API_VERSION": "v1",
        "PARTITION_FIELD": "CreatedDate",
        "LOWER_BOUND": "2023-09-07T02:03:00.000Z",
        "UPPER_BOUND": "2024-05-07T02:03:00.000Z",
        "NUM_PARTITIONS": "10"
    }
)
```

# Writing to Salesforce Marketing Cloud entities
<a name="salesforce-marketing-cloud-writing-to-entities"></a>

**Prerequisites**
+ A Salesforce Marketing object you wish to write to. You will need to specify the object’s name such as `List` or `Campaigns` or any of the other entities outlined in the table below.
+ The Salesforce Marketing Cloud connector supports three write operations:
  + INSERT
  + UPSERT
  + UPDATE

  When using the `UPDATE` and `UPSERT` write operations, you must provide the `ID_FIELD_NAMES` option to specify the external ID field for the records. 

**Supported entities for destination**:


| Entity | Priority | Interface (REST, SOAP, etc) | Can be Inserted | Can be Updated | Can be Upserted | 
| --- | --- | --- | --- | --- | --- | 
| Campaigns | P0 | REST | Y- Single | Y- Single | N | 
| Content Assets | P0 | REST | Y- Single, Bulk | Y- Single | N | 
| Contact | P1 | REST | Y- Single | Y- Single | N | 
| Domain Verification | P1 | REST | Y- Single | Y- Single, Bulk | N | 
| Event Notification Callback | P1 | REST | Y- Single | Y- Single | N | 
| Event Notification Subscription | P1 | REST | Y- Single | Y- Single | N | 
| Messaging | P1 | REST | Y- Single | N | N | 
| Object Nested Tag | P2 | REST | Y- Single | Y- Single | N | 
| Seed-List | P1 | REST | Y- Single | Y- Single | N | 
| Setup | P1 | REST | Y- Single | Y- Single | N | 
| Data Extension | P0 | SOAP | Y- Single | Y- Single | Y- Single | 
| Email | P0 | SOAP | Y- Single | Y- Single | N | 
| List | P0 | SOAP | Y- Single | Y- Single | N | 
| Send | P0 | SOAP | Y- Single | N | N | 
| Subscriber | P0 | SOAP | Y- Single | Y- Single | N | 

**Example for INSERT operation for REST**:

```
salesforcemarketingcloud_write = glueContext.write_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "Campaigns",
        "API_VERSION": "v1",
        "writeOperation" : "INSERT",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

**Example for INSERT operation for SOAP**:

```
salesforcemarketingcloud_write = glueContext.write_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "List",
        "API_VERSION": "v1",
        "writeOperation" : "INSERT",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

**Example for UPDATE operation for REST**:

```
salesforcemarketingcloud_write = glueContext.write_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "Campaigns",
        "API_VERSION": "v1",
        "writeOperation" : "UPDATE",
         "ID_FIELD_NAMES": "id",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

**Example for UPDATE operation for SOAP**:

```
salesforcemarketingcloud_write = glueContext.write_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "List",
        "API_VERSION": "v1",
        "writeOperation" : "UPDATE",
         "ID_FIELD_NAMES": "id",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

**Example for UPSERT operation for SOAP**:

```
salesforcemarketingcloud_write = glueContext.write_dynamic_frame.from_options(
    connection_type="salesforcemarketingcloud",
    connection_options={
        "connectionName": "connectionName",
        "ENTITY_NAME": "DataExtension/Insert-***E/6*******3",
        "API_VERSION": "v1",
        "writeOperation" : "UPSERT",
        "INSTANCE_URL": "https://**********************.rest.marketingcloudapis.com"
    }
)
```

# Salesforce Marketing Cloud connection options
<a name="salesforce-marketing-cloud-connection-options"></a>

The following are connection options for Salesforce Marketing Cloud:
+ `ENTITY_NAME`(String) - (Required) Used for Read. The name of your object in Salesforce Marketing Cloud.
+ `API_VERSION`(String) - (Required) Used for Read. Salesforce Marketing Cloud Rest and SOAP API version you want to use.
+ `SELECTED_FIELDS`(List<String>) - Default: empty(SELECT \$1). Used for Read. Columns you want to select for the object.
+ `FILTER_PREDICATE`(String) - Default: empty. Used for Read. It should be in the Spark SQL format.
+ `QUERY`(String) - Default: empty. Used for Read. Full Spark SQL query.
+ `PARTITION_FIELD`(String) - Used for Read. Field to be used to partition query.
+ `LOWER_BOUND`(String)- Used for Read. An inclusive lower bound value of the chosen partition field.
+ `UPPER_BOUND`(String) - Used for Read. An exclusive upper bound value of the chosen partition field. 
+ `NUM_PARTITIONS`(Integer) - Default: 1. Used for Read. Number of partitions for read.
+ `WRITE_OPERATION`(String) - Default: INSERT. Used for write. Value should be INSERT, UPDATE, UPSERT.
+ `ID_FIELD_NAMES`(String) - Default: null. Required for UPDATE/UPSERT.

# Limitations and notes for Salesforce Marketing Cloud connector
<a name="salesforce-marketing-cloud-connector-limitations"></a>

The following are limitations or notes for the Salesforce Marketing Cloud connector:
+ When using filter on DateTime datatype field, you need to pass the value in the format "yyyy-mm-ddThh:MM:ssZ".
+ In Data Preview, the Boolean Datatype value comes as a Blank.
+ For SOAP entities, you can define a maximum of two filters, and for REST entities, you can define only one filter, which restricts testing partitioning with filters.
+ Several unexpected behaviors have been observed from the SaaS side: the `Link.Alias` field in the `linksend` entity does not support the CONTAINS operator (for example, `Link.Alias CONTAINS "ViewPrivacyPolicy"`), and filter operators for Data Extension entities (such as EQUALS and GREATER THAN) do not return the expected results.
+ The SFMC ClickEvent SOAP API has a delay in reflecting newly created records so, the records created recently may not be immediately available in the API response.

  Example: If you create 5 new ClickEvent records at **2025-01-10T14:30:00** and immediately fetch them using the SOAP API, the response might not include all 5 records. It may take up to 5 minutes for the newly created records to appear in the API response. This delay can affect both data retrieval and scheduled runs as well.
+ Two different DateTime formats: **2025-03-11T04:46:00** (without milliseconds) and **2025-03-11T04:46:00.000Z** are supported when performing write operations in AWS Glue (with milliseconds).
+ For the Event Notification Subscription entity, a subscription can only be created for a verified callback URL, and you can have up to 200 subscriptions per callback.
+ For the Event Notification Callback entity, a maximum of 50 records can be created per account.