# Custom Identity Mappings

### Overview

Custom Identity Mappings allow you to define relationships between user identities and groups across different systems integrated with Veza. When your organization's access federation doesn't automatically create these connections in Veza, you can specify patterns to map users between systems (for example, connecting an Okta user `tom.shaw@veza.com` to a SQL Server login `DOMAIN\tshaw`).

Use custom identity mappings to:

* Connect IdP users (such as Okta users) to local accounts (such as Trino users)
* Connect IdP groups to groups in downstream systems (such as Active Directory Group to Okta Group, or Azure AD Group to GitHub Team)
* Define custom mapping rules for each integration, or use one mapping rule to link IdP identities or groups across multiple connected systems
* Correlate identities in a [custom IdP](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-identity-provider-template) to those in another integrated IdP such as Okta
* Map IdP users to local users in a [custom application](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/oaa/templates/custom-application-template) (as an alternative to using [ids](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/oaa/best-practices/cross-service-connections))
* Define access-granting relationships for any user or group with the same name, email, or another property in the Veza graph database
* Identify local account ownership using consistent naming patterns

### Native vs Custom Identity Mappings

Veza automatically creates identity relationships for certain integration pairs without requiring custom configuration. This is called **native identity mapping**.

#### Integration Pairs with Native Mapping

The following integration pairs automatically create bidirectional identity relationships:

| Integration Pair                | What Gets Mapped | Matching Criteria                                                                                             |
| ------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------- |
| **Active Directory ↔ Azure AD** | Users and Groups | Azure AD `onPremisesUserPrincipalName` matches AD User Principal Name (requires `onPremisesSyncEnabled` flag) |
| **Okta ↔ Google Workspace**     | Users and Groups | Okta App User email matches Google Workspace user email (requires matching domain)                            |

**When do you need custom identity mappings?**

* You're connecting integrations that don't have native mapping
* You need to map users/groups using different attributes than the native logic
* You're connecting custom applications or custom identity providers built with OAA

{% hint style="warning" %}
**Important**: For Active Directory ↔ Azure AD and Okta ↔ Google Workspace, configuring a custom identity mapping will **disable native mapping** between those integrations. Remove the custom mapping configuration to restore native behavior.
{% endhint %}

### Examples

You can configure mappings for one or more target data sources based on entity attributes or use templates to correlate identities and groups across multiple destination data sources.

#### User Identity Mapping

1. Active Directory to SQL Server:
   * Source: AD User email `admin@yourdomain.com`
   * Destination: SQL Login `YOURDOMAIN\admin`
   * Configuration: Map email to unique ID, enable "ignore domain"
2. Okta to Custom Application:
   * Source: Okta user email `jane.doe@company.com`
   * Destination: App username `jdoe`
   * Configuration: Map email to custom property `username`
3. Azure AD to PingOne:
   * Source: Azure AD user principal name `admin@company.com`
   * Destination: PingOne user `admin@company.com`
   * Configuration: Mapping User Principal Name to Email for cross-service access reviews

#### Group Identity Mapping

1. Azure AD to GitHub:
   * Source: Azure AD Group `Engineering-Team`
   * Destination: GitHub Team `Engineering Team`
   * Configuration: Map name to name, enable "ignore special characters"
2. Okta to Snowflake:
   * Source: Okta Group `DataAnalysts`
   * Destination: Snowflake Role `DATA_ANALYSTS`
   * Configuration: Map name to name, apply "UPPER" transformation
3. Azure AD to PingOne:
   * Source: Azure AD Group `Engineering-Team`
   * Destination: PingOne Group `Engineering Team`
   * Configuration: Map name to name, enable "ignore special characters"
4. Okta to Custom Application:
   * Source: Okta Group `Engineering-Team`
   * Destination: Custom Application Group `Engineering-Team`
   * Configuration: Map name to name for group-based access visibility in custom OAA applications
5. Multiple Resource Mapping:
   * Source: Active Directory Security Group `Finance-Staff`
   * Destinations:
     * Salesforce Group `Finance Users`
     * AWS IAM Group `finance-users`
     * Box Group `Finance Department`
   * Configuration: Single mapping configuration applying to multiple destination systems

#### Role to user identity mapping (AWS only)

AWS integrations support mapping IAM Roles directly to local users in downstream applications. This is useful when IAM roles represent service accounts or federated identities that correspond to application-level users.

1. AWS IAM Role to Custom Application User:
   * Source: AWS IAM Role `DataAnalyticsRole`
   * Destination: Custom Application User `analytics-service`
   * Configuration: Map role name to custom property `service_account`
2. AWS IAM Role to Snowflake User:
   * Source: AWS IAM Role `DataEngineerRole`
   * Destination: Snowflake User `data_engineer`
   * Configuration: Map role name to unique ID

To configure role to user mapping:

1. Edit your AWS integration and navigate to **Mapping Configuration**
2. Select **Roles to Users** as the Mapping Mode
3. Choose a destination data source type (Custom Application, Snowflake, SQL Server, etc.)
4. Add property matchers to define how role attributes map to user attributes

{% hint style="info" %}
The Roles to Users mapping mode is currently only available for AWS integrations.
{% endhint %}

### Prerequisites

Before configuring identity mappings:

* Ensure both the source and destination systems are successfully integrated with Veza
* Verify you have the necessary permissions to modify integration configurations
* Identify the common attributes or patterns used to correlate identities across your systems

### Enabling Identity Correlation

To enable custom mappings for an Identity or Cloud Provider:

1. Navigate to the **Integrations** page
2. Select a cloud or identity provider from the list and click **Edit**
3. Scroll down to the **Mapping Configuration** tab
4. Click **Add Mapping Configuration**
   1. Enable **Use Email By Default** to automatically create an email-to-email property matcher when no other matchers are configured. Configuring explicit property matchers below will take precedence and this setting will be ignored.
   2. For **Mapping Mode**, choose:
      * **Users** to correlate individual identities across systems
      * **Groups** to connect source and destination groups
      * **Roles to Users** (AWS only) to map AWS IAM Roles to application local users
   3. For **Destination Data Source Type**, select the target system for identity mapping

      **Identity Mapping for Multiple Resources**: If you need to configure identity mappings to many target systems, Veza supports using a single identity mapping configuration to connect users in the IdP to any number of destinations. Contact your Veza support representative to enable this feature. When enabled, you can select more than one **Destination Data Source Types** from the dropdown menu.
   4. Click **Add Property Matcher** to create a mapping rule
   5. Under **Property Matchers**, choose the source system attribute:
      * **Email** or **Unique ID** for native integrations like Okta
      * **Template** for pattern-based matching (see Template Transformations below)
      * **Custom Property** for OAA template integrations (enter the [custom property](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/developers/api/oaa/best-practices/oaa-custom-properties), e.g. `idp_id`)
   6. Select the matching destination system property (*Email*, *Unique ID*, *Template*, or *Custom*)
   7. Configure optional transformations:
      * **Ignore Special Characters**: Match identities that differ only by special characters (`_`, `-`, `.`)
      * **Ignore Domain**: Match identities after removing domain portions
5. Add additional property matchers as needed (combined with `OR` logic)
6. Click **Save Configuration**

{% hint style="warning" %}
If you configure any property matchers, the *Use Email By Default* setting is ignored. For example, *Use Email By Default* is enabled along with a property matcher for `username → user_id`, Veza will only use the username mapping and ignore the email default setting.
{% endhint %}

### Identity Matchers

Add identity matchers to correlate specific identities that don't meet the conditions of another property matcher:

1. Click *Add Identity Matcher* to add a mapping rule
2. In the leftmost dropdown, choose a specific identity from the source integration
3. Use the rightmost dropdown to pick the corresponding identity in the destination data source

### Template Transformations

{% hint style="info" %}
**Note:** When you select **Template** as a Property Matcher option, enter a template expression using the properties and transformation functions documented below. Templates let you construct custom matching patterns — for example, combining `{FirstName}` and `{LastName}` with case transformations to match usernames across systems.
{% endhint %}

Template transformations use property values and transformation functions to match identities across systems with different naming conventions. This feature is particularly useful when:

* Source and destination systems use different naming conventions
* You need to normalize user identifiers across systems
* You want to define global mapping rules that work across multiple applications

#### Template Syntax

The following sections document the template patterns and transformation functions that Veza uses internally to match identities. When you select a template from the dropdown menu, Veza applies these patterns automatically. This reference helps you understand which pre-configured template option to choose for your identity matching needs.

Templates use property placeholders with optional transformation functions:

```txt
{PropertyName | FUNCTION1 | FUNCTION2,...}
```

For example, a template pattern that transforms a user's name from "JOHN DOE" to "jdoe" would be written as:

```txt
{FirstInitial | LOWER}{LastName | LOWER}
```

#### Supported Properties

Templates can reference **any property on the source or destination entity** by name (case-insensitive). For example, `{email}`, `{principal_name}`, or `{employee_id}` all work if that property exists on the entity being matched.

In addition, Veza automatically injects the following special properties when a user's name is available:

* `FirstName`: User's first name
* `LastName`: User's last name
* `FirstInitial`: First character of first name (equivalent to `{FirstName | SUB_STRING,0,1}`)
* `LastInitial`: First character of last name (equivalent to `{LastName | SUB_STRING,0,1}`)

If a referenced property is not present on an entity, that entity will not match the template expression.

#### Transformation Functions

Templates can use transformation functions to match identities based on a partial match or variation of the source attribute. Identity mapping supports a subset of the [LCM transformer library](https://docs.veza.com/4yItIzMvkpAvMVFAamTf/features/lifecycle-management/transformers/transformer-reference) — see that reference for additional examples and pipeline patterns. Templates are validated when saved — an error is returned if the expression references an unknown transformer or has invalid syntax.

**Case conversion**

**LOWER**

Converts all characters to lowercase.

* Example: `{FirstName | LOWER}` for "John" returns "john"

**UPPER**

Converts all characters to uppercase.

* Example: `{FirstName | UPPER}` for "John" returns "JOHN"

**TITLE\_CASE**

Capitalizes the first letter of each word and lowercases the rest.

* Example: `{LastName | TITLE_CASE}` for "SMITH" returns "Smith"

**SENTENCE\_CASE**

Capitalizes the first letter only and lowercases the rest.

* Example: `{LastName | SENTENCE_CASE}` for "SMITH" returns "Smith"

**LOWER\_SNAKE\_CASE**

Converts to lowercase with underscores replacing spaces and other separators.

* Example: `{LastName | LOWER_SNAKE_CASE}` for "Van Der Berg" returns "van\_der\_berg"

**UPPER\_SNAKE\_CASE**

Converts to uppercase with underscores replacing spaces and other separators.

* Example: `{LastName | UPPER_SNAKE_CASE}` for "Van Der Berg" returns "VAN\_DER\_BERG"

**LOWER\_CAMEL\_CASE**

Converts to camelCase with the first letter lowercase.

* Example: `{FirstName | LOWER_CAMEL_CASE}` for "John Smith" returns "johnSmith"

**UPPER\_CAMEL\_CASE**

Converts to PascalCase with the first letter uppercase.

* Example: `{FirstName | UPPER_CAMEL_CASE}` for "John Smith" returns "JohnSmith"

**Substring extraction**

**SUB\_STRING**

Extracts a portion of text.

* Parameters:
  * start\_index: Starting position (0-based)
  * length: Number of characters to extract
* Example: `{FirstName | SUB_STRING,0,3}` for "John" returns "Joh"

**FIRST\_N**

Returns the first N characters.

* Parameters:
  * length: Number of characters to return
* Example: `{LastName | FIRST_N,4}` for "Smith" returns "Smit"

**LAST\_N**

Returns the last N characters.

* Parameters:
  * length: Number of characters to return
* Example: `{LastName | LAST_N,3}` for "Smith" returns "ith"

**SPLIT**

Splits on a delimiter and returns the element at the specified index.

* Parameters:
  * delimiter: The string to split on
  * index: Zero-based position of the element to return
* Example: `{Email | SPLIT,"@",0}` for "<john.doe@company.com>" returns "john.doe"

**Trim**

**TRIM**

Removes leading and trailing whitespace.

* Example: `{FirstName | TRIM}` for " John " returns "John"

**TRIM\_CHARS**

Removes specified characters from both ends.

* Parameters:
  * characters: The characters to trim
* Example: `{FirstName | TRIM_CHARS,"."}` for ".john." returns "john"

**TRIM\_CHARS\_LEFT**

Removes specified characters from the start.

* Parameters:
  * characters: The characters to trim
* Example: `{FirstName | TRIM_CHARS_LEFT,"0"}` for "00123" returns "123"

**TRIM\_CHARS\_RIGHT**

Removes specified characters from the end.

* Parameters:
  * characters: The characters to trim
* Example: `{FirstName | TRIM_CHARS_RIGHT,"."}` for "john." returns "john"

**Character removal**

**REMOVE\_CHARS**

Removes all occurrences of specified characters.

* Parameters:
  * characters: The characters to remove
* Example: `{Username | REMOVE_CHARS,".-_"}` for "john.doe\_1-2" returns "johndoe12"

**REMOVE\_WHITESPACE**

Removes all whitespace characters.

* Example: `{Username | REMOVE_WHITESPACE}` for "john doe" returns "johndoe"

**String modification**

**REPLACE\_ALL**

Replaces all occurrences of a string.

* Parameters:
  * original: The string to replace
  * replacement: The replacement string (omit to delete)
* Example: `{Email | REPLACE_ALL,"@company.com",""}` for "<john@company.com>" returns "john"

**APPEND**

Appends a value to the end.

* Parameters:
  * value: The string to append
* Example: `{Username | APPEND,"@example.com"}` for "john" returns "<john@example.com>"

**PREPEND**

Prepends a value to the start.

* Parameters:
  * value: The string to prepend
* Example: `{Username | PREPEND,"CN="}` for "john" returns "CN=john"

**Padding**

**LEFT\_PAD**

Pads on the left to reach a specified total length.

* Parameters:
  * length: Target total length
  * pad character: Character to use for padding (default is space)
* Example: `{Username | LEFT_PAD,8,"0"}` for "john" returns "0000john"

**RIGHT\_PAD**

Pads on the right to reach a specified total length.

* Parameters:
  * length: Target total length
  * pad character: Character to use for padding (default is space)
* Example: `{Username | RIGHT_PAD,8,"."}` for "john" returns "john...."

**ZERO\_PAD**

Left-pads with zeros to reach a specified total length.

* Parameters:
  * length: Target total length
* Example: `{EmployeeId | ZERO_PAD,6}` for "42" returns "000042"

**Domain and character encoding**

**REMOVE\_DOMAIN**

Removes the domain portion from an email address.

* Example: `{Email | REMOVE_DOMAIN}` for "<john@company.com>" returns "john"

**ASCII**

Converts non-ASCII characters to their closest ASCII equivalents and removes control characters.

* Example: `{FirstName | ASCII}` for "Łukasz" returns "Lukasz"

**REMOVE\_DIACRITICS**

Removes diacritical marks (accents) from characters.

* Example: `{FirstName | REMOVE_DIACRITICS}` for "résumé" returns "resume"

{% hint style="info" %}
**Functions not supported in identity mappings**

The following LCM transformer categories are available in attribute sync and provisioning workflows but are excluded from identity mapping templates:

* **Generators** (RANDOM\_STRING\_GENERATOR, RANDOM\_NUMBER\_GENERATOR, UUID\_GENERATOR, and others): produce non-deterministic output unsuitable for identity matching.
* **Date/time** (DATE\_FORMAT, DATE\_ADJUST, ASSUME\_TIME\_ZONE, and others): context-dependent and non-deterministic for matching purposes.
* **Lookup/entity** (FROM\_ENTITY\_ATTRIBUTE, FROM\_MANY\_ENTITIES\_ATTRIBUTE, LOOKUP): require graph cache or lookup table infrastructure not available in the identity mapping context.
* **Formatting** (COUNTRY\_CODE\_ISO3166, LANGUAGE\_RFC5646, PHONE\_NUMBER\_E164, FOR\_EACH, JSON\_FORMAT, JSON\_EXTRACT): not applicable to identity string matching.
  {% endhint %}

#### Function Composition

Multiple functions can be chained together, applied left to right:

```txt
{FirstName | TRIM | SUB_STRING,0,1 | UPPER}.{LastName | LOWER}
```

For a user "John Smith", this produces: "J.smith"

#### Common Template Patterns

Here are some frequently used template patterns:

1. First initial + last name:

   ```txt
   {FirstInitial}{LastName}
   ```

   Example: "John Smith" → "jsmith"
2. First name + last initial:

   ```txt
   {FirstName}.{LastInitial}
   ```

   Example: "John Smith" → "john.s"
3. Template on both source and destination:

   ```txt
   {principal_name | REMOVE_DOMAIN | LOWER} → {email | SPLIT,"@",0}
   ```

   Matches users when the username portion of their principal name equals the local part of their email address. Both source and destination templates support arbitrary entity properties.

#### Using OR Logic with Templates

Multiple property matchers can be combined using OR logic. The builder indicates these combinations with "OR" separators. For example:

```txt
Template: {FirstName}.{LastName} OR
Template: {FirstInitial}{LastName} OR
Property: email
```

This configuration would match any of these patterns for a user "John Smith":

* john.smith
* jsmith
* `john.smith@company.com`

When using templates with multiple property matchers, a match on any single pattern is sufficient to create the identity mapping.

{% hint style="info" %}
You cannot map an identity provider to itself (for example, between two Okta domains).
{% endhint %}

### Identity Mapping Use Cases

Common identity mapping combinations include:

**Target Systems:**

* AWS IAM
* AWS Redshift
* AWS RDS MySQL
* AWS RDS Postgres
* SQL Server
* Trino
* Snowflake
* GitHub
* Salesforce
* Box
* PingOne
* Privacera (PrivaceraUser and PrivaceraPortalUser)
* Custom Application (OAA data provider)
* Custom IDP (OAA identity provider)

**Identity Providers:**

* Active Directory
* Azure AD
* Google Workspace
* Okta
* OneLogin
* AWS Identity Center
* PingOne
* Custom IDP (OAA identity provider)

**AWS-Specific:**

* AWS IAM Roles → Local Users in any destination system (Roles to Users mode)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.veza.com/4yItIzMvkpAvMVFAamTf/integrations/configuration/custom-identity-mappings.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
