Runbooks/Self Service Groups

Self-Service Group Management

Operator runbook for owner-managed Active Directory and Entra ID groups: an end user creates a group, other users ask to join it, the owner approves, and IdentityMesh provisions the result through its own connectors. Membership, ownership, expiry and the audit trail all live in the mesh; the directory is a target, not the master. This is the Microsoft Identity Manager (MIM) self-service group parity feature.

New in this release. Run the three round trips at the end of this runbook (create, approved join, delete) on a test directory before opening the portal to users, and treat the Entra (Graph) side as the part to watch most closely.

Also new: groups that already exist in Active Directory can now be brought under self-service management without being recreated. See Onboarding groups you already have, including the two acceptance groups to run first and the one known issue to read before applying the AD template on an estate that imports groups.

Before upgrading

This release changes what DeleteIfUnlinked does on a projection rule. It is the one thing to read before installing, and it affects every rule in the estate, not just self-service groups.

Before, a rule with Delete if unlinked ticked did nothing when an object’s precondition stopped holding: the export carried an empty change and every connector turned it into no modification. The flag was inert. Now the same configuration emits a real Delete, and the object is removed from the target on the first export drain after the upgrade.

Run this before the upgrade

Run this query in SQL Server Management Studio against the IdentityMesh database before you run the new MSI:

SELECT r.ProjectionRuleId, c.Name AS ConnectorName, r.MeshObjectType,
       r.TargetObjectType, r.CreateIfMissing, r.DeleteIfUnlinked, r.PreconditionScript
FROM dbo.IM_ProjectionRules r
JOIN dbo.IM_Connectors c ON c.ConnectorId = r.ConnectorId
WHERE r.DeleteIfUnlinked = 1;

Every row is a rule that would now delete target objects. For each one, read its precondition and count how many objects it currently excludes: that is how many directory objects the first drain would delete. Keep the output as the record of the estate before the migration touched it.

What the upgrade does with them

The installer’s database step runs a one-time migration that clears DeleteIfUnlinked on every existing rule and records which rules it reset under the system setting Export:DeleteIfUnlinkedReset. Re-tick the flag only on the rules where deleting the target object is what you want.

After the reset the Sync Engine logs one warning at startup for every rule that still carries the flag, naming the rule, its connector and both object types. That is your standing inventory of deletion paths.

How a group is modelled

A self-service group is an ordinary mesh Group object, the same kind MeshComposer creates, plus a small set of side tables. There is no second provisioning path: the export engine, the projection rules and the export queue do the work exactly as for every other object.

What makes it self-service is a set of marker attributes the API stamps on the mesh object:

MarkerValuesRead by
imProvisiontrue / falsethe AD projection rule’s precondition
imSyncToEntratrue / falsethe Entra projection rule’s precondition
imMembershipAuthoritativetrueboth preconditions, and the export projection
entraGroupKindSecurity / Microsoft365the Entra connector

The markers are the lifecycle switch. Deleting a group flips imProvision and imSyncToEntra to false; the preconditions then fail and the engine emits a Delete for every target the group is linked on. imMembershipAuthoritative is what lets a group be emptied: with it, an edge-less group projects an empty member value, which is how expiry strips the directory group. Composer groups do not carry it and are unaffected; imported groups do not carry it until an administrator onboards them.

Ownership is managedBy relationship edges (group to person) plus an owners table holding the full list and its history. The API also stamps a managedBy attribute holding the primary owner’s mesh object id, so the projection rule has a source to flow. On AD the engine swaps it for the owner’s DN (AD models managedBy as one DN, so co-owners are not represented). On Entra the engine expands it to every owner, but only when the rule flows a target attribute named exactly owners.

Membership is member_of edges with source SelfService. Composer edges carry their own source, and each reconciler only diffs its own, so the two never disturb each other.

A person, for every purpose here (owner, member, join requester, the people picker, the members an onboarding carries), is a live mesh object whose type is User or Person. Connectors project people as User unless a projection rule renames the type. A mesh that uses another name lists it in the API’s Groups:PersonObjectTypes setting (a JSON array, compared case-insensitively); the list replaces the default, so include User if it is still in play.

Setup

1. Deploy the schema

The installer’s database step creates the group tables and seeds the two projection templates. No policy row is seeded: the documented defaults apply until someone opens the configuration page, so the feature works before it is configured.

Upgrading from a release that already had the group tables: this release adds an Origin column to IM_Groups (Created or Adopted). The installer’s database step adds it during the upgrade. A database updated outside the installer needs the database step run before the Admin API starts, or every group call fails with an invalid column name.

2. Apply the projection templates

Apply the two shipped templates from Connector → Projection → Apply template, against the AD connector and, if you sync to the cloud, the Entra connector. See the projection templates runbook.

TemplateMesh → targetPrecondition
Self-Service Group Export (AD)Group → groupimProvision and imMembershipAuthoritative are true
Self-Service Group Export (Entra ID)Group → groupimSyncToEntra and imMembershipAuthoritative are true

Both preconditions test two markers. imProvision alone would also select MeshComposer’s create-mode groups, which carry the same marker. These are deliberately not the older AD Group Export template, which has no precondition and would export every mesh group in the estate, imported ones included.

Applying a template creates an independent rule you can edit. Two things to know:

2a. Verify the managedBy schema row

The engine only resolves a mesh id to a directory DN for attributes declared as a single-valued reference for that object type. The seed adds that row for managedBy on Group, but it never overwrites a row you already had. Check Schema → Attributes:

FieldValue
Attribute namemanagedBy
Object typeGroup
Data typereference
Multi-valuedno

If the data type reads anything else, change it by hand, or AD receives the owner’s raw mesh object id instead of a DN. Declaring it a reference is safe on an existing estate: an imported group whose managedBy already holds a DN string keeps that value.

2b. Set the AD connector’s delete mode

Set the AD connector’s Delete mode to disable-or-delete (new in this release) or delete. Under disable a group’s delete fails loudly and the queue item stays red: a group has no userAccountControl, so there is nothing to disable, and the connector refuses rather than reporting a delete it did not perform. Self-service deletes never reach the directory until the mode is changed.

3. Grant the Entra app registration its Graph permissions

Group export needs these Application permissions with admin consent, on the app registration the Entra connector already uses:

PermissionNeeded for
Group.ReadWrite.Allcreate, patch and delete the group; read and write its owners
GroupMember.ReadWrite.Alladd and remove members
User.Read.Allresolve mesh persons to Entra user ids (the import already needs this)

Members and owners are written as Entra object ids, so the Entra user import must already be running and joined to mesh persons. Membership convergence is one Graph call per member added or removed, and the run log warns above 500 changes. Plan on groups of a few hundred members. See the Entra connector runbook.

4. Configure the policy

Configure → Groups writes the single install-wide policy row.

SettingDefaultNotes
Creator groupsemptymesh groups whose members may create; see below
Allowed group typesemptywhich of Security / Distribution / MailEnabledSecurity / Microsoft365 users may pick
Naming regex, name prefixnoneon top of the 20-character sAMAccountName limit
Max expiry days365caps what an owner may set, including on renewal
Default renewal period days365
Default target OU by typenoneOU per group type
AD connector, Entra connectornonewhich connectors receive the exports
Mail domainnonebuilds mail for mail-enabled types
Expiry warning days30, 7, 1days before expiry that owners are warned
Retain expired days30how long an expired group is restorable

The two empty lists mean opposite things. An empty creator list means only groups.admin holders can create. That is the shipped default and is restrictive on purpose. An empty allowed-type list means all four types are allowed.

A user may create a group only if they hold groups.own and the mesh holds a membership edge from one of the creator groups to their person object.

The policy is one row, replaced whole. Sending the policy over the API (PUT /api/groups/policies) must include every field; an omitted field is written as its default. An empty expiry-warning list is a real value meaning never warn.

5. Configure email

Notifications always write an in-portal inbox row. Email is additional and off until enabled under Settings → Email, where you choose SMTP, Mailgun or Microsoft 365. Configure → Groups shows a link to that page. Installs that already had the SMTP relay set up keep sending, and their settings now appear under Settings → Email with SMTP selected. See the Email runbook.

A recipient’s address is their person object’s mail attribute. No mail means inbox only, and the notification row records the reason. A send failure is recorded on the row and never fails the request that triggered it.

Roles and permissions

PermissionAdminOperatorViewerUserGrants
groups.read✓✓✓✓browse joinable groups, own memberships and requests
groups.request✓✓✓join, leave, cancel own requests
groups.own✓✓✓create within policy, manage owned groups, decide their requests
groups.admin✓any group, policies, high-impact decisions, restore, delete, override
groups.audit.read✓✓✓audit query and CSV export
groups.notify.read✓✓✓✓own inbox

Ownership is a fact, not a role. Every owner endpoint checks the owners table (or groups.admin). Granting someone groups.own does not give them any existing group.

A group can be an owner. An owner row may name a mesh group instead of a person. Every direct member of that group then holds owner standing: they see the group under My Groups, get its join requests in their owner queue, and may approve, add, remove, edit and renew exactly as a named owner would. Membership is read from the mesh the same way creator groups are (relationship types member / member_of, group → person, any source), so an imported AD or Entra group works as soon as its membership has been imported; nested groups are not followed. See Groups as owners.

Sign-ins are resolved to mesh persons by UPN, mail or sAMAccountName. An ambiguous match is refused, not guessed.

Workflows

Join policies

Join policyA join request does
Openadds the member immediately, queues the export, audits MemberAdded
OwnerApprovalcreates a pending request and notifies the owners
Closedis refused

High impact needs two decisions

A group flagged high impact requires a second decision from a groups.admin holder. After owner approval (or immediately, on an Open group) the request waits for an administrator. The membership edge is not written until the admin approves, so an open high-impact group is not a back door.

The owner decides first, then a different administrator decides. The same person cannot satisfy both arms, and the admin arm is not a shortcut past the owner arm. Denials are exempt: an administrator may deny at any stage. Groups that are not high impact keep the single-administrator override. High impact and group type are admin-only fields.

Separation of duty

The owner deciding a request must not be the person who raised it. A sole owner therefore cannot approve their own join: the group needs a co-owner, or an administrator has to decide. A single-owner group is a dead end for self-approval by design.

Creating a group without a mesh person

“Your sign-in could not be matched to one person in the mesh” on New group means the signed-in account is not a person object in the mesh (a demo mesh, or an admin account from a domain the mesh does not import). A groups.admin holder can still create the group by naming at least one owner on the form: the first named becomes the primary owner, the rest co-owners, and the audit actor stays the administrator. Anyone else needs their account imported first.

Name reservation

A deleted or expired group’s name stays reserved until the retention purge frees it, and the refusal names that date. This mirrors Entra’s hold on a deleted group’s mail nickname. See the arithmetic under Expiry, retention and deletion.

Other actions

Every one of these writes an audit row.

Groups as owners

The owner picker on the New-group form and on Add a co-owner offers mesh groups as well as people (the API call is GET /api/groups/people?includeGroups=true; group rows carry kind: "Group"). Naming a group, for example Helpdesk, means:

Onboarding groups you already have

Everything above describes groups the portal created. Every real estate is brownfield: the groups people actually want to govern already exist in Active Directory and arrived in the mesh through an import. Configure → Groups → Onboard existing brings those groups under self-service management without recreating them. The group’s mesh object, its anchor and its connector link are reused; what onboarding adds is the IM_Groups row (status Active, Origin = Adopted), its owners, a self-service membership edge for every current member, and the three markers, all written in one save.

From that moment the group behaves like a created one everywhere: Browse and Join, My Groups, Approvals, expiry, renewal, high impact, audit and notifications. The mesh becomes authoritative for its membership. Joins and leaves in the portal reach AD, and a member added directly in AD is reverted the next time the group is exported. Onboard a group only when that is what you want.

What stays the import’s

Onboarding writes only the markers and the owner reference. Name, description, mail, group type and the anchor stay exactly what the import brought in, because a second copy of any of them would be exported beside the first (Finance;Finance). Three consequences:

How membership is resolved

The source of truth is the group’s own Management Space row on the AD connector: its member attribute is the list of DNs as AD holds them now. Each DN is looked up against the user rows imported on the same connector, and the preview reports one outcome per member:

OutcomeMeaningAfter onboarding
CarriedA live, linked person row with that DNstays a member; the mesh now owns the edge
Nested groupThe DN is a groupremoved on first export; membership is flat
Not importedNo row: outside the import scope, a foreign security principal, a contact, a computerremoved on first export
UnlinkedA row that is not joined to a mesh objectremoved on first export
DeletedA soft-deleted rowremoved on first export

“Removed on first export” is a real change to a production directory. The preview lists every such DN with its reason, the request must carry an accepted count per group that is at least the live count, and the confirm dialog makes you type ONBOARD when any group would lose members. A group whose every member would be dropped is refused outright: an onboarding that would empty a populated group is never right. Link the members first, or leave the group alone.

An empty AD group has no member value at all, so the connector cannot tell “empty” from “attribute not loaded”. The preview flags such a group as will be onboarded as an EMPTY group, and it needs its own explicit acceptance plus the typed confirm. If no group on the connector carries member, the attribute is missing from the connector’s load list and onboarding refuses with that reason.

A group with more than 1,500 members is refused: AD returns such memberships in ranges the connector does not retrieve, so the mesh would see a partial list and treat it as the whole.

Refusals

Each group in a batch is decided on its own, so one bad group does not fail the other forty-nine. A group is refused when it:

Owners are required because the orphaned-owner sweep would otherwise close a freshly onboarded group within a minute. The owner reference (managedBy) is written only when the group has no managedBy from any source; an imported value is left alone, and the preview says keeps its AD managedBy. Approvals read the owners table, so owners work in the portal either way.

Hybrid groups

If the group’s mesh object is also linked to an Entra row with onPremisesSyncEnabled true, Entra Connect owns the cloud copy. Such a group shows a Hybrid badge, Entra sync is forced off whatever the batch setting says, and later attempts to turn it on are refused.

Undo

POST /api/groups/{id}/release (groups.admin, API only in this release) reverses an onboarding: it removes the IM_Groups row, the owners, every self-service edge and attribute on the group, the group’s pending export-queue rows and its export snapshots, and cancels pending join requests with a notification. AD membership is not touched; with the marker gone the next export leaves member alone, exactly as before onboarding. Release is refused for groups the portal created, which have Delete. A failure part-way through an onboarding runs the same release automatically and audits GroupOnboardFailed.

Audit

One hash-chained GroupOnboarded entry per group records the connector, the carried count, every dropped DN with its reason (capped at 500), the accepted counts, the owners and the settings. GroupReleased records the same shape on undo. There are no per-member events; the member list is the entry.

Known issue: imported groups and Delete if unlinked

The shipped AD group template carries Delete if unlinked so that deleting a self-service group deletes the directory group. The engine evaluates that flag per rule for any queued export of a mesh Group that names no connector, and an imported AD group that carries none of the markers fails the rule’s precondition, which the engine currently reads as “no longer eligible, delete”. On an estate where the AD connector imports groups, an imported group that is queued that way can be deleted from AD. Until the engine refuses to delete objects it never provisioned, apply the AD template with Delete if unlinked off on any connector that also imports groups, and delete retired self-service groups in AD by hand. The runbook will say when this restriction is lifted.

Acceptance groups

Run these on the test directory before onboarding anything real:

  1. A fully carried group changes nothing. Onboard a group whose members are all imported, linked users. Trigger the export and compare AD’s member before and after: identical, and the export confirmation shows no membership delta.
  2. A mixed group drops exactly what the preview said. Onboard a group holding a nested group and a foreign security principal. The preview must list both; the onboarding must refuse without the accepted count; with it, the first export removes exactly those two entries and nothing else.

Expiry, retention and deletion

A group’s life after activation runs on a schedule:

  1. Warning. At each warning threshold (default 30, 7 and 1 days out) the group is marked pending expiry and its owners are notified, once per threshold.
  2. Expiry. At the expiry date the group becomes expired, all membership edges are removed, and the resulting export empties the directory group. The object stays and the owners are kept.
  3. Restore window. For the retain-expired window (default 30 days) an owner can restore the group with a new expiry. Members are not restored; re-adding them is a deliberate act.
  4. Deletion. After the window the group is deleted: Delete exports go to every linked target and the mesh object is erased. The group row itself stays, holding its name.
  5. Purge. The group row is removed a further retain-expired window later. This is the step that frees the name.

A name can therefore be reserved for two windows, 60 days at the defaults. A group that expired naturally spends one window reaching deletion and one more reaching purge. A group deleted outright by an administrator waits one window. This is the first thing to check when a creation fails with a duplicate-name error for a group nobody can see.

Sweeps

Three background passes on the API host, configured in the Admin API’s appsettings.json. Each is disabled by setting its interval to 0 or less.

SweepCadence keyDefaultDoes
ExpiryIdentityMesh:Groups:ExpirySweepHours24warnings, expiry, deletion and purge after the retain window
Orphaned ownerIdentityMesh:Groups:OwnerSweepHours24drops owners whose person is gone; promotes their manager
Provisioning statusIdentityMesh:Groups:StatusSweepMinutes1marks a pending group active when its AD link appears; records error targets

The orphaned-owner sweep escalates rather than leaving a group unowned. When the last owner goes, it resolves that owner’s manager and makes them the owner, notifying them. With no manager, the group is flagged as needing an owner, its join policy is forced to Closed, and every groups.admin holder is notified.

The status sweep is why a new group shows Pending for a minute or two. If the export drain is not running it shows Pending forever: a paused engine looks identical to a slow directory from the portal. Check the export queue before assuming the group failed. Consider a status cadence of 5 minutes on a first rollout rather than the default 1; the sweep queries every non-active group each pass.

Provisioning-error notices are throttled to one per connector per group per 24 hours. The queue item is the live signal; the notification is the prompt to go and look.

Notifications

Each notification is written to the recipient’s in-portal inbox (the bell in the navbar), then attempted as email if email is set up under Settings → Email. The delivery outcome is recorded on the row.

Inbox rows are purged after IdentityMesh:Retention:NotificationsDays in the Sync Engine’s appsettings.json, default 90 days, 0 or negative to disable. Every row past the window goes, read or unread. The inbox is not evidence; the audit trail is the group audit table below. Expiry warnings fan out to every owner at three thresholds, so this is the fastest-growing table the feature adds. There is no UI for this setting.

Audit and its retention

Every state change writes one hash-chained row to the group audit table, stamped by the same chain the admin and vault audit use. Query and export it from Groups → Group Audit, or at GET /api/groups/audit and …/audit/export.csv, both gated on groups.audit.read. Filters: group, user, from, to.

The CSV cannot report a failure that happens after streaming began. A read that fails part-way through produces a truncated file under a success status. Check the last line of any export you intend to keep as evidence, and fetch it again if it stops mid-row.

Group audit is purged under the same window as the rest of the audit evidence, Retention.AuditDays (default 30 days, 0 or negative to keep forever). Purging removes the oldest rows, so the surviving chain stays verifiable; a full-history verification on a purged deployment reports exactly one broken link at the retention boundary, and that is expected. See the audit retention runbook.

Known standoffs

These look like faults and are not. Each is a deliberate refusal to record a convergence that did not happen.

Troubleshooting

The people picker finds nobody

Searching for a known account on the New group owners field, the co-owner picker or the members picker returns nothing, and onboarding previews report every member dropped. The search only looks at mesh objects of the person types (User and Person by default), matched on the anchor, displayName, userPrincipalName, mail, sAMAccountName, accountName (the metaverse name the shipped templates use) and whatever Vault:RequesterMatchAttributes lists. Check the object type your projection rules produce on the Mesh Objects page; if it is something else, set Groups:PersonObjectTypes. If the sign-in lives in an attribute with another name, add that name to Vault:RequesterMatchAttributes, which makes it both searchable and the name the group pages show. Earlier releases only recognised Person and did not search accountName; on a template-shaped mesh only the anchor matched.

Ownership is not being managed

Work these in order; the first two are configuration, the rest are data.

  1. The rule does not flow owners (Entra) or managedBy (AD). The shipped templates have it; a hand-built or edited rule may not. On Entra the target attribute must be spelled exactly owners.
  2. managedBy is not declared as a single-valued reference (AD only). The symptom is a GUID in AD’s managedBy, not an empty one. See step 2a.
  3. The group has no owners. The portal shows the status; assign an owner.
  4. The owners exist but are not provisioned on that connector. The engine drops the key and defers the export. If they are merely late it clears itself; if their target object was deleted it never clears (see below).
  5. Entra refuses to remove the last owner. The export fails every cycle rather than succeeding with no owners.

The export queue never empties, and nothing is failing

A group can sit in the queue indefinitely with no failed items. The queue item’s reason ReferenceDeferred is this case: a member, owner or managedBy reference could not be resolved to that person’s identifier on the target, so the engine wrote what it could and re-queued the rest. The Sync Engine log (not the per-connector run log) records how many references did not resolve.

A late member resolves on its own. A member whose target object was deleted never does, and the same group reappears on every drain. Either remove the member from the group, or re-provision their target object. Deleting the group also ends it.

Group membership shrank after an import

Narrowing an AD connector’s OU or filter marks the excluded population deleted, and every group containing those people is re-exported with a shortened membership. After narrowing scope, run a full import and read the drift queue before you let exports drain. When more than Export:MaxDeletedMemberFraction of a group’s members (a system setting, default 0.5) resolve only to deleted rows, the export omits member entirely and defers instead of stripping the group. A warning names the group and the counts.

Exports for one connector type stop, with nothing failing

IdentityMesh:Export:PerExportTimeoutSeconds (new in this release, default 120, 0 disables) bounds one queue item’s initialize plus export. On expiry the item fails and the message names which phase ran out: during initialize means the directory would not let the connector bind; during export means the bind worked and the write did not. Raise it for a slow target; do not disable it on a connector that has ever hung.

Nothing exports at all

Check the markers before the rule. Both preconditions require imMembershipAuthoritative in addition to imProvision or imSyncToEntra, and a precondition that evaluates false, or fails to parse, is treated as “do not project” with no error surfaced. Confirm all the markers are present on the mesh object.

Acceptance round trips

Run these in order on a test directory and tenant before opening the feature to users.

  1. Create reaches both directories. Create a Security group with Entra sync on. Expect the AD group within five minutes and the Entra group within one cycle, the portal flipping to Active, and managedBy in AD holding a DN, not a GUID.
  2. Approved join reaches AD, with audit. With owner approval, a different user requests to join; the owner approves. Expect the member in AD (and Entra if synced) and the audit chain RequestSubmitted, RequestApproved, MemberAdded, matched by the CSV export.
  3. Delete leaves both directories. Delete the group as an administrator. Expect both directory groups gone, the Management Space rows marked deleted, a single Delete in the queue, and a same-name create refused with the date retention frees the name.

Record the actual times and any Graph error text verbatim. Then run the two onboarding acceptance groups before onboarding a real one.