Projection Pipeline
The projection pipeline controls how mesh objects are provisioned and updated in target connectors. It determines which objects are exported, how attributes are mapped, and what the connector receives on create and update operations.
Pipeline Overview
When a mesh object is created or its attributes change, the projection pipeline determines whether it should be exported to one or more connectors. The pipeline flows through four stages:
Enqueue
Mesh object created or attributes changed. If a matching Projection Rule exists with at least one Projection Attribute Rule covering the changed attributes, an export queue item is created.
Build Projection
The Projection Engine assembles the full outbound attribute set by applying Projection Attribute Rules and Outbound Flow Rules, including any transform scripts.
Compute Delta
Compare projected attributes against the last exported snapshot. Only changed or new attributes are included in the export payload. On first export (create), all projected attributes are sent.
Export to Connector
Call the connector's export API with the delta. Determine the action: Create (no MS Object exists and CreateIfMissing = true), Update (MS Object already exists) or Delete (MS Object exists, the precondition no longer holds, and DeleteIfUnlinked = true). After success, save the new snapshot.
Key Concepts
Mesh Object
The authoritative, unified identity record in the mesh. Mesh objects aggregate attributes from all connected systems via inbound attribute flow rules. Each mesh object has a type (e.g. "User", "Group") and a set of key-value attributes.
Management Space (MS) Object
A connector-specific representation of an identity. Each MS Object belongs to a single connector and is linked to a mesh object. MS Objects store the connector's view of the identity — the external ID, object type, and raw attributes as imported from or exported to the connected system. When Identity Mesh creates or updates an object in a target connector, the resulting record is tracked as an MS Object.
Projection Rule
Defines whether a mesh object type should be projected to a specific connector. Maps a mesh object type to a target object type in the connector, and controls create/delete behavior.
Projection Attribute Rule
Defines how individual mesh attributes map to target connector attributes. Each rule maps one mesh attribute to one target attribute, with an optional transform script.
Outbound Flow Rule
Supplementary attribute mappings applied during projection. These work like Projection Attribute Rules but are defined as Attribute Flow Rules with Direction = "Outbound". Applied after Projection Attribute Rules.
Exported Attribute Snapshot
After each successful export, the full set of projected attributes is saved as a snapshot. On the next export cycle, the engine compares the new projection against this snapshot to compute a delta — only changed attributes are sent to the connector.
Attribute Lifecycle
Attributes pass through several stages before they become available in scripts, rules, and the Admin UI. Understanding this pipeline is essential for configuring connectors and writing transform or precondition scripts.
Configure AttributesToLoad
On the Connectors page, use Discover Attributes to see available attributes from the source system (e.g. Active Directory). Select the attributes you need and click Apply Selected, then Save the connector. This sets the AttributesToLoad list in the connector's configuration, controlling which attributes the connector reads during import.
Run Import Sync
When the sync engine runs an import for this connector, it reads the configured attributes from the source system and stores them as Management Space (MS) Objects. Until an import runs, no connector-side attributes exist in Identity Mesh. After import, the connector's attribute names become available in the MS Attribute dropdowns on the Rules page.
Inbound Flow Rules Map to Mesh
Create Inbound Attribute Flow Rules that map connector attributes (MS Attributes) to mesh attributes. For example, map givenName → firstName. During sync, the engine applies these rules (including any transform scripts) to populate mesh object attributes.
Mesh Attributes Available in Scripts
Once mesh objects have attributes (from inbound flow), those attribute names appear as clickable badges in the script editor and are available as meshAttributes.* in transform and precondition scripts. They also populate the Mesh Attribute dropdowns in rule forms.
Connector Attributes vs. Mesh Attributes
These are two separate pools. Connector attributes (MS Attributes) are the raw field names from the source system (e.g. sAMAccountName, givenName). Mesh attributes are the unified names you define via inbound flow rules (e.g. firstName, primaryEmail). Scripts and preconditions operate on mesh attributes. If you don't see attributes in the script editor, check that: (1) the connector has run an import, and (2) inbound flow rules exist to map connector attributes into the mesh.
Projection Rules
A Projection Rule links a mesh object type to a target connector. Without a Projection Rule, no outbound export occurs for that object type to that connector.
{
"connectorId": "conn_ad_corp",
"meshObjectType": "User",
"targetObjectType": "user",
"createIfMissing": true,
"deleteIfUnlinked": false,
"preconditionScript": "return attrs["employeeStatus"] == "Active";"
}true, create a new object in the connector when no MS Object exists yet. If false, only update existing objects.true, export a Delete when the rule's precondition stops holding for an object that already exists in the target, or when the mesh object is unlinked. The flag alone never deletes a healthy object. Changed in this release — see Delete semantics.true to proceed, false to skip. Use this to filter which objects are eligible for export (e.g. only active employees).Precondition Scripts
Precondition scripts are optional C# expressions that gate whether a rule is applied. They are evaluated as C# expressions and must return a bool value.
Return Value Behavior
| Script Returns | Behavior |
|---|---|
| true | Rule is applied normally |
| false | Rule is skipped |
| (empty / not set) | Rule is always applied — no precondition check |
Script Context
Precondition scripts receive the mesh object's attributes as a IReadOnlyDictionary<string, object?> named Attributes. Use dictionary methods to read attribute values.
Behavior by Rule Type
Attribute Flow Rules
When false, the individual attribute mapping is skipped. Other flow rules for the same object continue to evaluate.
Projection Rules
When false, the entire projection is blocked — no attributes are exported. If the object already exists in the target and the rule has DeleteIfUnlinked = true, the engine exports a Delete for it instead (see Delete semantics). An object that was never exported is simply skipped.
Examples
Flow Rule: Only flow if mail is not empty
// Flow Rule precondition: only flow if mail is not empty
Attributes.ContainsKey("mail") &&
!string.IsNullOrEmpty(Attributes["mail"]?.ToString())Projection Rule: Only provision active employees
// Projection Rule precondition: only provision active employees
Attributes["employeeStatus"]?.ToString() == "Active"Complex: Active user with a corporate email
// Combined check: active user with an email in the right domain
Attributes["objectType"]?.ToString() == "user" &&
Attributes["employeeStatus"]?.ToString() == "Active" &&
(Attributes["mail"]?.ToString()?.EndsWith("@corp.com") ?? false)Admin UI Preview
The script editor in the Admin UI uses a JavaScript-based preview for convenience. The preview uses meshAttributes.propertyName syntax, while the sync engine evaluates the C# expression using Attributes["propertyName"]. The preview is an approximation — always test with real data before relying on complex scripts.
Projection Attribute Rules
Each Projection Attribute Rule maps a single mesh attribute to a target connector attribute. Together, these rules define the full outbound attribute set for a connector.
// Projection Attribute Rules for "Corporate AD" connector
[
{ "meshAttribute": "firstName", "targetAttribute": "givenName" },
{ "meshAttribute": "lastName", "targetAttribute": "sn" },
{ "meshAttribute": "displayName", "targetAttribute": "displayName" },
{ "meshAttribute": "primaryEmail", "targetAttribute": "mail",
"transformScript": "return value?.ToString()?.ToLowerInvariant();" },
{ "meshAttribute": "upn", "targetAttribute": "userPrincipalName" },
{ "meshAttribute": "samAccount", "targetAttribute": "sAMAccountName" },
{ "meshAttribute": "department", "targetAttribute": "department" },
{ "meshAttribute": "title", "targetAttribute": "title" },
{ "meshAttribute": "manager", "targetAttribute": "manager",
"transformScript": "return "CN=" + value + ",OU=Users,DC=corp,DC=com";" }
]Transform Scripts
Transform scripts are C# expressions evaluated with the source attribute value as value. The script must return the transformed value. Use transforms for formatting, concatenation, DN construction, or any custom logic.
- • The source mesh attribute can be left empty for generated values (e.g. passwords). In that case,
valueisnulland the transform must produce the entire value. - • If a source attribute is specified but missing on the object, the rule is silently skipped.
- • If no transform script is set, the source value is passed through to the target unchanged.
- • Available built-in functions:
ToLower,ToUpper,Trim,Replace,Substring,Concat,Coalesce,NullIfEmpty,NormalizeEmail,EqualsIgnoreCase,GeneratePassword(length),ms("attrName"),attr("attrName"). - • Example:
GeneratePassword(16)generates a random password meeting AD complexity requirements.
Outbound Flow Rules
In addition to Projection Attribute Rules, you can define Outbound Flow Rules (Attribute Flow Rules with Direction = "Outbound"). These are applied after Projection Attribute Rules and add supplementary attributes to the projected output.
// Outbound Attribute Flow Rules (Direction = "Outbound")
[
{
"connectorId": "conn_ad_corp",
"msAttribute": "description",
"meshAttribute": "jobTitle",
"preconditionScript": "return value != null && value.ToString().Length > 0;"
},
{
"connectorId": "conn_ad_corp",
"msAttribute": "physicalDeliveryOfficeName",
"meshAttribute": "officeLocation"
}
]Object Creation: Required Attributes
When CreateIfMissing = true and no MS Object exists for a mesh object in the target connector, Identity Mesh will attempt to create the object. The connector will receive all projected attributes (no delta — everything is new).
Critical: Target systems require specific attributes to create objects
If your Projection Attribute Rules and Outbound Flow Rules do not map all required attributes for the target system, the create operation will fail. The export engine will retry (up to MaxRetries) and then mark the queue item as Failed.
Each connector type has its own set of mandatory attributes that must be present for object creation. Your projection rules must ensure these attributes are mapped from the mesh object.
// Active Directory: Required attributes for object creation
//
// Without these, AD will reject the create (Add) operation.
//
// Projection Attribute Rules MUST map all of these:
{
"sAMAccountName": "jsmith", // REQUIRED - unique logon name
"userPrincipalName": "jsmith@corp.com", // REQUIRED - UPN for sign-in
"cn": "John Smith", // REQUIRED - common name (RDN)
"displayName": "John Smith", // REQUIRED - display name
"givenName": "John", // Recommended
"sn": "Smith" // Recommended
}
// Entra ID: Required attributes for object creation
{
"displayName": "John Smith", // REQUIRED
"userPrincipalName": "jsmith@corp.com", // REQUIRED
"mailNickname": "jsmith", // REQUIRED
"accountEnabled": true, // REQUIRED
"passwordProfile": { ... } // REQUIRED for new users
}
// Okta: Required attributes for object creation
{
"login": "jsmith@corp.com", // REQUIRED
"email": "jsmith@corp.com", // REQUIRED
"firstName": "John", // REQUIRED
"lastName": "Smith" // REQUIRED
}Active Directory Required Attributes
| Target Attribute | Required | Description |
|---|---|---|
| sAMAccountName | Required | Pre-Windows 2000 logon name (max 20 chars, unique in domain) |
| userPrincipalName | Required | UPN for user sign-in (format: user@domain.com) |
| cn | Required | Common Name — the RDN of the object's DN |
| displayName | Required | Display name shown in address book and UI |
| givenName | Recommended | First name |
| sn | Recommended | Surname (last name) |
| unicodePwd | Conditional | Password — required if account must be enabled at creation |
Entra ID Required Attributes
| Target Attribute | Required | Description |
|---|---|---|
| displayName | Required | Display name for the user |
| userPrincipalName | Required | UPN in email format (must be verified domain) |
| mailNickname | Required | Mail alias (no spaces or special characters) |
| accountEnabled | Required | Whether the account is enabled (true/false) |
| passwordProfile | Required | Password profile for new users (must meet complexity requirements) |
Okta Required Attributes
| Target Attribute | Required | Description |
|---|---|---|
| login | Required | Unique login identifier (typically email) |
| Required | Primary email address | |
| firstName | Required | First name |
| lastName | Required | Last name |
Create vs. Update Decision
The Export Engine determines the action for each queue item based on whether an MS Object already exists in the target connector:
Create (Add)
No MS Object exists for this mesh object in the target connector, and CreateIfMissing = true.
- • All projected attributes are sent (no delta — there's no previous snapshot)
- • Target system's required attributes must all be present
- • A new MS Object is created to track the connector-side record
- • The full attribute set is saved as the initial snapshot
- • A create that comes back with no anchor (the connector wrote nothing it can identify) fails with a reason rather than being confirmed
Update (Modify)
An MS Object already exists. The engine computes a delta against the last snapshot.
- • Only changed or new attributes are sent to the connector
- • Attributes removed from projection are flagged for removal
- • If no delta exists (nothing changed), the export is skipped entirely
- • The snapshot is replaced with the new full projection
Delete
An MS Object exists, the Projection Rule precondition now returns false (or the mesh object is unlinked), and DeleteIfUnlinked = true.
- • A Delete change is sent to the connector and counted as Deleted in the run
- • What happens in the target is the connector's policy (for Active Directory, its
DeleteMode) - • After the connector confirms, the MS Object is marked deleted and counts as absent from then on
Skip
No MS Object exists and CreateIfMissing = false, or the Projection Rule precondition returned false for an object that was never exported, or no delta was detected. No connector call is made.
Delete Semantics (changed in this release)
Until this release the DeleteIfUnlinked flag on a Projection Rule was stored but never acted on. It now does real work: when a rule's precondition stops holding for a mesh object that already exists in the target connector, the engine exports a Delete for that object. An object whose precondition still holds is never touched by the flag, and an object that was never exported is skipped, not deleted.
What the upgrade does to existing rules
Because rules may have been ticked while the flag was inert, the installer's database step clears DeleteIfUnlinked on every existing operator rule during the upgrade. Seeded templates (such as the self-service group templates) are never reset. The rules that were reset are recorded under the system setting Export:DeleteIfUnlinkedReset.
An administrator must re-tick each rule that should really delete. The engine logs a startup warning for every rule still carrying the flag, so a rule that can delete is never silent.
Safety rails
0.5. If more than this fraction of a group's members resolve only to deleted rows, the member write is deferred rather than stripping the group. Raise it only when you understand why so many members are gone.DeleteIfUnlinked rule.DeleteMode (disable never removes anything; disable-or-delete disables users and deletes the rest; delete removes). Check it before re-ticking a rule. See Connector Configuration.Delta Computation
Identity Mesh only sends changed attributes to minimize connector load and avoid unnecessary writes. The delta is computed by comparing the new projected output against the snapshot saved after the object's last successful export.
// Delta computation example
//
// Last exported snapshot (saved after the previous successful export):
// { "givenName": "John", "sn": "Smith", "department": "Engineering" }
//
// New projected output:
// { "givenName": "John", "sn": "Smith", "department": "Product", "title": "PM" }
//
// Computed delta (only changed/new attributes sent to connector):
// { "department": "Product", "title": "PM" }
//
// On CREATE (no snapshot exists), ALL projected attributes are sent.Export Queue
Export operations are processed asynchronously from the export queue, which you can inspect on the Export Queue page in the Admin UI. Items are dequeued in batches, sorted by priority (descending) then creation time (ascending).
// Export Queue Item
{
"exportQueueId": 42,
"meshObjectId": "a1b2c3d4-...",
"connectorId": null,
"reason": "Create",
"priority": 0,
"status": "Pending",
"retryCount": 0,
"maxRetries": 3
}Queue Reasons
Per-Export Timeout
IdentityMesh:Export:PerExportTimeoutSeconds (default 120; 0 disables) bounds connector initialisation plus the export call for a single queue item. A timed-out item fails with a message naming which phase ran out, and is retried like any other failure. Raise it for slow targets.
Retry Behavior
Failed exports are automatically retried with exponential backoff. The item is locked for 2^retryCount minutes before the next attempt.
| Retry | Backoff | Status |
|---|---|---|
| 1st failure | 2 minutes | Pending (locked) |
| 2nd failure | 4 minutes | Pending (locked) |
| 3rd failure | 8 minutes | Pending (locked) |
| Max retries exceeded | — | Failed (requires manual retry) |
Projection Templates
A projection template is a saved, connector-agnostic snapshot of a Projection Rule and its Projection Attribute Rules. It captures the shape of a working export — CreateIfMissing, DeleteIfUnlinked, the precondition, the target container, and the full attribute rule set — without binding it to any one connector. Apply a template to another connector or object type to reproduce that export configuration without rebuilding it by hand.
Templates are managed through the /api/projection-templates endpoints (list, save-as-template, apply, delete). Listing requires rules.read; save, apply, and delete require rules.write. There is no dedicated Admin UI page for projection templates — they are managed through the API.
Sync Rule Templates
A sync rule template is the inbound counterpart of a projection template: a saved, connector-agnostic snapshot of a connector's Join Rules and its inbound and outbound Attribute Flow Rules. A template carries an optional connector-type hint (for example Entra or Okta) so that matching templates sort first when you pick one, but the hint is advisory — any template can be applied to any connector.
Two templates ship with every install and upgrade. Entra ID users and groups creates, for User and for Group, join rules onPremisesSecurityIdentifier → objectSid and onPremisesSamAccountName → sAMAccountName followed by an anchor join rule id → anchorValue, plus inbound flows id → anchorValue and displayName → displayName for both types, member → member for Group, and an ungated onPremisesSyncEnabled → onPremisesSyncEnabled for both types. Every flow except the last carries the precondition Not(ms("onPremisesSyncEnabled")). Okta users and groups anchors both types on id only, with no preconditions, and its group display-name flow is name → displayName, because Okta groups carry their name in profile.name. Together these are exactly the rules the Entra ID and Okta connectors need before an imported group's member list resolves into mesh membership relationships.
The Entra template is written for hybrid tenants. A user or group that Entra Connect synchronises from Active Directory (onPremisesSyncEnabled is true) already exists in the mesh through the AD connector, and its cloud id can never match that object. The SID join links the synced object to its AD-mastered mesh object instead, and the SAM-name join is a fallback for estates that do not flow objectSid (the SID is forest-unique; a SAM name is unique only within a domain, so the exact key is tried first), and the precondition keeps the cloud side from anchoring, renaming, or re-membering anything Entra Connect owns: a synced object that does not join is not projected either, so no duplicate person is created. Cloud-only objects return no value for the flag, and Not of an absent value is true, so they anchor and flow exactly as before. Synced objects still import and still link to the person, so attributes that live only in the cloud can still be written back to them. If your AD connector flows the SID or the SAM account name into a mesh attribute with a different name, edit the applied join rule's mesh attribute to match.
Applying a template is additive and idempotent: rules that already exist on the connector (same attributes, object type, and direction, compared case-insensitively) are skipped rather than duplicated, and the result reports how many join rules and flow rules were created and how many were skipped. Applying the same template twice therefore creates nothing the second time.
In the Admin UI, open the Rules page and expand a connector. From Template on the Join Rules or Flow Rules tab opens the template picker and applies the chosen template to that connector; Save as Template above the tabs snapshots the connector's current join and flow rules as a new template, with the connector's type recorded as the hint.
The API exposes GET /api/sync-rule-templates to list them, POST /api/connectors/{id}/sync-rule-templates/{templateId}/apply to apply one, POST /api/connectors/{id}/sync-rule-templates/save-as-template to save the connector's rules as a new template, and DELETE /api/sync-rule-templates/{templateId} to remove one. Listing requires rules.read; apply, save, and delete require rules.write.
Best Practices
Map all required attributes before enabling CreateIfMissing
Review the target connector's required attributes and ensure every one is covered by a Projection Attribute Rule or Outbound Flow Rule. Missing required attributes will cause create failures.
Use precondition scripts to gate provisioning
Add a precondition on your Projection Rule to only export objects that meet your criteria (e.g. return attrs["employeeStatus"] == "Active";). This prevents provisioning incomplete or ineligible records.
Start with CreateIfMissing = false
When first configuring a connector, set CreateIfMissing = false and use Export Preview in the Admin UI to verify attribute mappings. Once you've confirmed the projected output looks correct, enable creation.
Monitor the export queue for failures
Check the Export Queue in the Admin UI regularly. Failed items indicate misconfigured attribute mappings, missing required attributes, or connector permission issues. Use the error message to diagnose and fix.
Use transforms for format differences
Different systems expect different formats. Use transform scripts to handle case conversion, DN construction, date formatting, or attribute concatenation between the mesh and target systems.