Provisioning Mappings and Lookups
Write-mappings, sync_mode, operators, provisioning lookups, and cross-system references.
Anatomy of a write-mapping
Each ConnectorInstance.provisioning_field_mappings
entry is a JSONB dictionary with these fields:
target_field— the name of the field on the target API (e.g.userPrincipalName)job_type—account_create/account_modify/grant/*(all)operator— how the value is rendered (see below)sync_mode—enforce/set_once/ignore_drift/skipapplies_to_account_types— empty = all types; a list = only for specific types
Operators — ways to compute a value
-
passthrough — a direct copy:
source_field: "identity.email"→ the target receives the identity's email. The fastest path for 80% of mappings. -
expression — a Jinja template:
{"\"{{ identity.first_name }}.{{ identity.last_name }}@example.com\""}. Can filter or transform:{"| lower"},{"| if_null('default')"}. -
constant — a fixed value for every account:
constant: "Member". -
lookup — query another system for the value.
lookup_schema_idpoints to a provisioning lookup row that describes the filter and return field. See the section below. -
account_attr — a cross-system reference:
account_attr("ad-corp", "samaccountname")reads the AD account's samAccountName for the same identity. This drives thedepends_on_systemsdependency graph (AD must sync before Entra). -
builtin — helpers:
gen_username(3 styles + accent normalization),slug,gen_uuid.
sync_mode — what the platform does when the target diverges
-
enforce — drift is written back. For attributes the platform treats as the source of truth (displayName, mail, job function).
-
set_once — write on create, then the target wins. For immutable identifiers (userPrincipalName after rollout).
-
ignore_drift — write on create and on modify, but do not write drift back during reconciliation. For attributes the target may legitimately change (last_signin_at, user-edited bio fields).
-
skip — never write. For read-only mappings that serve display only.
Provisioning lookups — cross-system reference resolution
Lookups let a mapping query the target API itself to resolve references. Example: a manager_id mapping on Entra:
lookup.target_object_name— which object to query (users)lookup.filter_template—{"userPrincipalName eq '{{ value }}'"}lookup.returns_field— which field to return (id)lookup.cache_scope—per_job/per_sync/per_tenantlookup.accept_stale_on_failure— on a target error: use the last cached value, or fail?
ℹ Good to know
Lookups are the number-one cause of slow provisioning on large tenants. Be careful with
per_jobscope on large batches; aper_syncscope with a higher time-to-live (TTL) often works better.
Per-account-type targeting
applies_to_account_types filters per type. Use
case: personal accounts get userPrincipalName
from identity.email, while admin-privileged accounts
get a-{"{first.last}"}@domain. Both mappings
write to the same target_field but for different
types.
Specificity: a mapping that names a type explicitly
wins over a generic (empty applies_to_account_types)
mapping on the same target_field.
Further reading: