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 an export configuration — create/delete behavior, the precondition, the target container, and the full attribute rule set — without binding it to any one connector. Once saved, a template can be applied to another connector or object type to reproduce the same export configuration without rebuilding it by hand.
What it is
When you build a working export for a connector, that work lives in a Projection Rule (see the Projection Pipeline reference) plus a set of Projection Attribute Rules. A template freezes that pair into a reusable definition:
- Projection-rule settings —
MeshObjectType,TargetObjectType,CreateIfMissing,DeleteIfUnlinked,PreconditionScript, andTargetContainer. - The attribute rule set — every Projection Attribute Rule
(source attribute → target attribute, with any transform script),
serialized together as
AttributeRulesJson.
Because the template stores what to project and how, not which connector, the same template can be applied to any connector that has a compatible target object type.
Why it exists
- Reuse an export configuration across connectors. A tested “user provisioning” export you built for one Active Directory connector can be applied to a second AD domain (or a lab connector) without re-authoring every attribute mapping.
- Building block for the planned MIM-sync migration wizard. Templates are the unit the wizard will use to translate an existing MIM sync-rule set into IdentityMesh projection rules — capture once, apply many.
Data model
Templates are persisted in the IM_ProjectionTemplates table:
| Column | Description |
|---|---|
TemplateId | Primary key (GUID). |
Name | Template display name (e.g. “AD User Export”). |
Description | Optional operator notes. |
MeshObjectType | Mesh object type the rule projects (e.g. person). |
TargetObjectType | Target object type in the connector (e.g. user). |
CreateIfMissing | Whether apply creates objects when no Management Space object exists yet. |
DeleteIfUnlinked | Whether the applied rule may export a Delete when its precondition stops holding for an object that exists in the target. See DeleteIfUnlinked semantics below; this flag does real work as of this release. |
PreconditionScript | Optional C# precondition carried into the applied rule. |
TargetContainer | Default target container for created objects (e.g. an AD OU DN). |
AttributeRulesJson | The serialized projection-attribute rule set. |
CreatedUtc | Timestamp the template was captured. |
API
All template operations live under the /api/projection-templates
family. Listing requires rules.read; every mutating operation
(save-as-template, apply, delete) requires rules.write — the same
permissions that gate projection rules themselves.
List — GET /api/projection-templates
Returns the saved templates (id, name, description, object types, and
the capture timestamp). Requires rules.read.
curl -X GET https://identitymesh.example.com/api/projection-templates \
-H "Authorization: Bearer $TOKEN"
Save as template
Captures an existing connector’s projection rule — together with its
projection-attribute rules — into a new IM_ProjectionTemplates row.
You point it at a source connector’s projection rule; the engine reads
back the rule settings and serializes the attribute rule set into
AttributeRulesJson. Requires rules.write.
Apply
Applies a saved template to a target connector. The engine
materializes a projection rule on that connector from the template’s
MeshObjectType / TargetObjectType / CreateIfMissing /
DeleteIfUnlinked / PreconditionScript / TargetContainer, and
recreates the projection-attribute rules from AttributeRulesJson.
Requires rules.write.
Review the applied rule before enabling exports — in particular confirm
TargetContainer and any required attributes make sense for the target
connector (see Templates vs. rules below).
Delete
Removes a saved template. This deletes only the template row; projection
rules previously created by applying it are unaffected. Requires
rules.write.
Permissions
| Permission | Required for |
|---|---|
rules.read | Listing templates |
rules.write | Save-as-template, apply, delete |
Worked example — “AD User Export”
This example uses a template captured from a working Active Directory user-provisioning rule (save one from your own working rule first):
| Field | Value |
|---|---|
Name | AD User Export |
MeshObjectType | person |
TargetObjectType | user |
CreateIfMissing | true |
DeleteIfUnlinked | false |
TargetContainer | OU=Users,DC=example,DC=com |
Reusing it against a second AD connector:
- Confirm the template exists.
GET /api/projection-templatesand locate “AD User Export” (rules.read). - Apply it to the target connector. Apply the template to the new
AD connector (
rules.write). The engine creates a projection rule mappingperson → user, withCreateIfMissing = true,DeleteIfUnlinked = false, the captured precondition, and every attribute rule fromAttributeRulesJson. - Adjust the target container if needed. The template’s
TargetContainer(OU=Users,DC=example,DC=com) is carried over as the default. If the target domain uses a different OU, edit the applied projection rule to point at the correct container before enabling exports. - Verify, then export. Confirm all required attributes for the AD
userclass are covered (a create withCreateIfMissing = truefails if a required attribute is unmapped, and a create that produces no anchor now fails with a reason instead of being confirmed), then let the projection pipeline run.
Use
example.complaceholders in your own templates. Never store a real production OU path, credential, or secret in a template — theTargetContaineris meant to be reviewed and adjusted per target.
DeleteIfUnlinked semantics
DeleteIfUnlinked changed meaning in this release, and the change
reaches every rule a template is applied to, so it is worth reading
before you re-apply an old template.
Before. The flag was stored on the rule and carried into templates,
but the engine never acted on it. A rule with DeleteIfUnlinked = true
behaved exactly like one without.
Now. 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 and counts it as Deleted in the run. What
the connector does with a Delete is the connector’s own policy — for
Active Directory that is DeleteMode (disable, disable-or-delete or
delete); for Entra it removes the group.
The flag on its own never deletes a healthy object. An object whose precondition still holds is projected as before; only the transition from “precondition true” to “precondition false” (or the mesh object becoming unlinked) produces a Delete.
What the upgrade does. Because every existing install has rules that were ticked when the flag was inert, the upgrade migration:
- clears
DeleteIfUnlinkedon every existing operator rule, and never on the seeded templates (the self-service group templates are meant to delete); - records the list of rules it reset under the system setting
Export:DeleteIfUnlinkedReset, so you can see exactly what changed; - leaves it to an administrator to re-tick the rules that should really delete.
The engine logs a startup warning for every rule that still carries the flag, so a rule that can delete is never silent.
Safety rails around a confirmed delete.
- After the connector confirms a Delete, the object’s Management Space row is marked deleted and counts as absent for later exports: the next projection of that mesh object is a create, not an update against a row that no longer exists.
- For group membership, if more than
Export:MaxDeletedMemberFraction(system setting, default0.5) of a group’s members resolve only to deleted rows, the member write is deferred rather than stripping the group. Raise the fraction only if you understand why so many members are gone. - The export preview in the Admin UI shows what the engine would
actually do for an object — it no longer shows Delete for a healthy
object under a
DeleteIfUnlinkedrule.
Templates vs. rules
A projection rule is live: it belongs to a specific connector and drives exports right now. A template is an inert, connector-agnostic copy of a rule’s configuration — it does nothing on its own until it is applied to a connector, at which point a real rule is materialized from it. Editing a template never changes any live rule, and editing a live rule never updates the template it came from; re-capture the rule if you want the template to reflect later changes.
Current limitation
There is no dedicated UI page for templates yet — projection templates
are API- and engine-driven. Save, apply, and delete via the
/api/projection-templates endpoints above (or through the migration
tooling that consumes them). A management surface in the Admin UI is
planned alongside the MIM-sync migration wizard.
Related
- Projection Pipeline reference — how projection rules and projection-attribute rules drive exports (the configuration a template captures).
composer.md— rule-based group composition; another export-shaping surface that flows through the same projection pipeline.