> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/access/manage-roles-and-groups.md).

# Manage roles and groups

Add and remove members, nest roles and groups, set effective dates that actually take effect, and compare two roles before you change one.

## What this is, and when you need it

A membership is the row that says one user holds one role or group — and in a live deployment that row has several possible authors. An administrator can create it by hand, a business rule can create it from identity data, an approved request can create it, a synchronization or reconciliation can create it from what a managed system already contains, and a certification revocation can take it away. None of those mechanisms consults the others, which is what makes membership something to manage rather than merely edit: before you change a row you need to know what is asserting it, or your change will not survive. Come here to add, remove, nest, end-date or compare memberships. If you need the objects themselves first, read [The access model](/administration-guide/access/access-model.md).

This page owns membership: who is in a role or group, what a role contains, how the relationships nest, and how to take a relationship away without breaking something that still needs it. The role form itself is in [Create a role](/administration-guide/access/create-a-role.md).

One thing to settle before you touch a membership: **which mechanism owns it**. A direct grant on a role whose population is decided by a business rule is removed again the next time that user is saved, and across the whole population at the next recalculation. Nothing re-applies your grant for you, and the audit record will show the rule taking it away, not an administrator. Correct the authoritative path, not the symptom.

## At a glance

|                          |                                                                                                                                                        |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **You will end up with** | A membership set you can defend: every row created by the mechanism that owns it, visible from both the role and the user, and reflected in the target |
| **Where**                | `/webconsole/roleEntitlements?id=<ROLE_ID>` and `/webconsole/groupEntitlements?id=<GROUP_ID>`                                                          |
| **You need first**       | The exact role or group (names repeat across managed systems), and the answer to "what is asserting this membership today"                             |
| **Takes**                | 5 minutes for one membership; an hour if you have to trace every path granting it before you remove one                                                |
| **Reversible**           | Partly — re-adding a membership row is trivial, but a removal that deprovisioned a target account is reversible only with consequences                 |

## The routes

| Route                                         | What it does                                                                   |
| --------------------------------------------- | ------------------------------------------------------------------------------ |
| `/webconsole/roles`                           | Role search                                                                    |
| `/webconsole/editRole?id=<ROLE_ID>`           | The role's own fields                                                          |
| `/webconsole/roleEntitlements?id=<ROLE_ID>`   | Users, child roles, parent roles, groups, resources, organizations, attributes |
| `/webconsole/roleMenuTree?id=<ROLE_ID>`       | The role's navigation tree (404s with `Role with ID '<id>' not found`)         |
| `/webconsole/compare-roles?id=<ROLE_ID>`      | Side-by-side content comparison against a second role                          |
| `/webconsole/groups`                          | Group search                                                                   |
| `/webconsole/editGroup?id=<GROUP_ID>`         | The group's own fields                                                         |
| `/webconsole/groupEntitlements?id=<GROUP_ID>` | The group's members and contents                                               |
| `/webconsole/entitlements-bulk-operations`    | Bulk add or remove across many entitlements at once                            |

Both `editRole` and `editGroup` accept a missing `id` and open in create mode, so a mistyped or truncated link creates a new object rather than reporting an error.

## Add a user to a role

{% stepper %}
{% step %}

### Confirm you have the right role

Search by exact name at **Access Control → Role**. Role names are unique only within a managed system, so two roles can share a name across systems. Check the managed system, status and owner before you open the entitlements screen.
{% endstep %}

{% step %}

### Confirm this is the authoritative path

If a business rule, a synchronization source or a request flow decides this role's population, stop and change that instead. Check the role's business-rule targets and the user's existing memberships' tags — a membership tagged `BUSINESS_RULE_ASSIGNMENT` or `SYSTEM_ASSIGNMENT` tells you a machine owns it. See [Membership tags](/administration-guide/governance/certification/membership-tags.md).
{% endstep %}

{% step %}

### Add the member with dates and rights

Open `/webconsole/roleEntitlements?id=<ROLE_ID>`, go to the users list, add the user, and set the start and end dates and any access rights the grant needs. The four shipped access rights are `READ`, `WRITE`, `ADMIN` and `OWNER`. Set an end date if the grant is temporary — but read [Effective dates, honestly](#effective-dates-honestly) before you promise anyone it will lapse.
{% endstep %}

{% step %}

### Read it back from both sides

Reopen the role's user list *and* the user's role list. They are the same row; if only one shows it, the search index behind the other screen is stale — see [Search indexes and the authorization graph](/administration-guide/reference/search-indexes.md).
{% endstep %}

{% step %}

### Verify the outcome, not the save

Check the user's *effective* access, and if the role carries a managed-system entitlement, check the provisioning request and the account in the target. Audit records `ADD_USER_TO_ROLE` (targets `USER` and `ROLE`).
{% endstep %}
{% endstepper %}

## What a membership carries

Every membership — user-to-role, role-to-role, group-to-role, resource-to-role, user-to-group, user-to-organization — is the same shape: a start date, an end date, a set of access rights, and a set of membership tags. When you add a member you are filling in those four things whether the screen asks you to or not. An **owner** is not one of them — it is a single field naming one user or one group, and an owner is not a member unless separately added as one.

### Effective dates, honestly

`startDate` and `endDate` are recorded and displayed, and a role's **Default Membership Duration** pre-fills the end date. But the thing that acts on an expired end date is a batch task:

| Task ID           | Name                                              | Cron           | Ships        |
| ----------------- | ------------------------------------------------- | -------------- | ------------ |
| `AUTO_REVOCATION` | Notification of expiring/revoke of expired access | `0 0 20 * * ?` | **Disabled** |

It appears in **Administration → Batch Tasks** as `AUTO_REVOCATION` / **Notification of expiring/revoke of expired access**, and has shipped, disabled, since 4.1.3.0 — it was called *Revoke expired access* until 4.2.1.1, which is the name older runbooks use. Check its enabled flag there: if it is off on your system, no end date has ever been acted on.

{% hint style="warning" %}
On a system where `AUTO_REVOCATION` has never been enabled, every end date ever set is decorative. Time-bound access is one of the easiest governance findings to fail an audit on, precisely because the UI shows the date and implies enforcement. Enable the task — see [Create a batch task](/administration-guide/automation/batch-tasks.md) — or revoke through a business rule or a certification campaign instead, and do not tell requesters access will lapse if nothing is going to lapse it.
{% endhint %}

Two related tasks ship **enabled** and do act on dates, on the *user* rather than the membership: `PROV_BY_START_DATE_FUT` ("Activate by Start Date", `0 0 0 1/1 * ?`) and `LEAVER_PROCESS` ("Disable by last date", `0 0 17 * * ?`).

## Stop an operator granting themselves access

Anyone who can edit user entitlements can otherwise add themselves to any role, including the privileged built-in ones. `org.openiam.ui.webconsole.allow.change.own.entitlements` (**Administration → System Configuration**, category `System`) ships `false`, and while it is `false` OpenIAM refuses any entitlement change whose target is the signed-in operator — roles, groups, resources and organizations alike — with **Not allowed to change your own entitlement**. The test is "requester is the target user", not "the role is privileged", so the flag blocks every self-grant or none, and it does nothing about two operators granting each other what neither may grant alone. Where that matters, take the entitlement screens away from the population and route the access through a request with an approval flow.

## Add contents to a role

A role can contain child roles, groups, resources and organizations. Each is a separate list on `/webconsole/roleEntitlements`, and each has its own audit action:

| Relationship         | Audit on add           | Audit on remove                              |
| -------------------- | ---------------------- | -------------------------------------------- |
| User in role         | `ADD_USER_TO_ROLE`     | `DELETE_USER_FROM_ROLE`                      |
| Child role in role   | `ADD_CHILD_ROLE`       | `DELETE_CHILD_ROLE`                          |
| Parent role of role  | `ADD_PARENT_ROLE`      | —                                            |
| Group in role        | `ADD_GROUP_TO_ROLE`    | `DELETE_GROUP_FROM_ROLE`                     |
| Role in group        | `ADD_ROLE_TO_GROUP`    | `DELETE_ROLE_FROM_GROUP`                     |
| Role on resource     | `ADD_ROLE_TO_RESOURCE` | `DELETE_ROLE_FROM_RESOURCE`                  |
| User in group        | `ADD_USER_TO_GROUP`    | `DELETE_USER_FROM_GROUP`                     |
| Child group in group | `ADD_CHILD_GROUP`      | `DELETE_CHILD_GROUP` / `DELETE_PARENT_GROUP` |
| User in organization | `ADD_USER_TO_ORG`      | `DELETE_USER_FROM_ORG`                       |
| Role attribute       | `ADD_ATTRIBUTE`        | `DELETE_ATTRIBUTE`                           |
| Approver on role     | `ADD_APPROVER_TO_ROLE` | `DELETE_APPROVER_FROM_ROLE`                  |

Each of these appears as the **Action** on the audit record; search for them at the audit log screen to confirm which mechanism made a change. Retention for each is in [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md) — and every `org.openiam.audit.log.prune.days.*` property ships `-1`, so these events are kept forever by default.

Adding contents to a populated role changes access for every current member, and every member of every child role, at once. Count the members before you save.

## Nesting

A role's **Role Parent** field only appears while the role has no ID, so on an existing role you manage parents from `/webconsole/roleEntitlements?id=<ROLE_ID>` instead. Before you save a parent:

* confirm the child genuinely needs everything the parent contains — inheritance is all-or-nothing;
* walk the parent's ancestors and the child's descendants yourself. OpenIAM's only cycle check rejects a role added as its own child, with `CANT_ADD_YOURSELF_AS_CHILD`; it does not detect a longer loop such as A→B→A, which will save and then behave unpredictably. Prove it if you like: on two disposable roles, make A the parent of B and then B the parent of A — both saves succeed;
* test a member of the child and a user who holds only the parent, so you prove inheritance runs in the direction you think it does;
* record who owns changes to the parent, because from now on they own changes to every child.

Resources are stricter than roles here. OpenIAM rejects a resource parent-child relationship when either resource's type has `SUPPORTS_HIERARCHY='N'`, returning `RESOURCE_TYPE_NOT_SUPPORTS_HIERARCHY` and naming the offending resource type in the message. Four shipped types are affected — `ADMIN_RESOURCE`, `OAUTH_SCOPE`, `REPORT` and `SOD_ENTITLEMENT`. See [Resources and organizations](/administration-guide/access/resources-and-organizations.md).

### What nesting does not reach

Nesting decides what a user is entitled to inside OpenIAM, and it decides which managed-system accounts are created — provisioning walks a role's whole hierarchy to collect the resources it carries. It does **not** reach the membership attribute a connector writes on the account. That is built from the user's own **direct** group memberships, so a group reached only through a role never appears in it and the directory shows the account with no group. Worse, adding a group to a role a user already holds changes nothing on that user's own memberships, so the next save sends no membership attribute at all and the connector reports it unchanged.

The shipped directory membership scripts name each group they did send as `[userGroups]: <dn>` in the provisioning service log, log `there is no groups` when there were no direct memberships to send, and skip in silence any group belonging to another managed system or carrying no `dn` attribute — the last logging `there is no DN for group`.

If a role must drive target group membership, grant the groups to the user directly as well as the role; a business rule can carry both as separate on-match targets. The alternative is to copy the shipped membership script in **Groovy Manager**, expand the role's contents inside your copy, and select it on the managed system's policy map — see [Policy map](/administration-guide/lifecycle/policy-map.md).

## Groups

A group's form is rendered from `DEFAULT_GRP_TEMPLATE` (template type `GROUP_TEMPLATE`), which after all migrations carries sixteen fields. Most mirror the role form; the five that differ — **Classification**, **AD Group Type**, **AD Group Scope**, **Organizations** and **Participate in Access Certification** — are listed with their shipped flags in [Role and group field reference](/administration-guide/access/manage-roles-and-groups/role-and-group-reference.md#the-shipped-default-group-template), along with the shipped **DistinguishedName** custom field.

Two group facts are worth carrying in your head rather than looking up. **Classification** is a `GROUP_TYPE` metadata type, and only `AD_GROUP` and `GENERAL_GROUP` ship; the AD group type and scope fields appear only when the classification is `AD_GROUP`. And `GROUP_OWNER`, `GROUP_ADMIN`, `GROUP_ADMIN_RESOURCE` and `GROUP_ORGANIZATION` all ship `IS_EDITABLE='N'`, which for the owner and admin pickers is not enforced — the same trap as on the role form, described in [the three non-editable rows](/administration-guide/access/manage-roles-and-groups/role-and-group-reference.md#the-three-non-editable-rows-and-what-really-happens).

Practical guidance: a group imported from a target is **target-owned**. Renaming or deleting it in OpenIAM does not rename it in the directory, and the next reconciliation will disagree with you. Put the group inside a role and manage the role. Renaming it *in the directory* is worse still: incoming group records are matched by name within the managed system, so the next synchronization matches nothing, creates a **second** OpenIAM group under the new name and leaves the original behind. Nothing removes the stale row. Match on an attribute the directory does not change, or reconcile, which compares both sides.

## Compare two roles before you change one

OpenIAM ships a role diff tool, and it is the single most useful thing on this page for catalog work.

**Compare with...** on a role's entitlements screen opens **Role Comparison**, `/webconsole/compare-roles?id=<ROLE_ID>`, for that role as the primary; the write lands on the **primary** role and records audit action `COMPARE_ROLE_ACCESS`. Working through a set of candidate pairs is [Role engineering and maintenance](/administration-guide/access/role-engineering.md#converge-collapse-the-duplicates).

Two facts about its scope, both of which you can see by running one comparison against a role you know well:

* It compares **contents only** — child roles, groups and resources. It does not compare users, attributes, owner, risk, or any single-value field, so two roles reported identical can still differ in owner, risk rating and status.
* It excludes resources of type `MENU_ITEM` and `WORKFLOW`, so navigation entries and approval-workflow resources never show up as differences. A role that reports no differences may still have a different menu.

Comparing the same role against itself returns `DUPLICATE_IDENTIFIER` rather than an empty diff.

{% hint style="info" %}
Applying a comparison writes to the primary role. It is the fastest way to make two roles identical and the fastest way to grant a hundred people something by accident. Read the diff first, and set operations only on the rows you meant.
{% endhint %}

## Bulk membership changes

`/webconsole/entitlements-bulk-operations` runs one operation across many entitlements, records a single `ENTITLEMENT_BULK_OPERATION` audit event with targets `GROUP` and `ROLE`, and offers 25 operations in its **Operation** drop-down — `ADD_ENTITLEMENT` and `DELETE_ENTITLEMENT` are the membership ones. The full list is in [Enumerated values](/administration-guide/reference/enums.md#bulk-operations), and which of the others are worth using on a catalog is in [Retrofit: fix the whole catalog at once](/administration-guide/access/role-engineering.md#retrofit-fix-the-whole-catalog-at-once) — including `CHANGE_ASSIGN_TO_ALL`, which can set "assign to all users" on many roles in one submission and should never be part of a batch.

## Remove a membership without breaking something

Removal is where dependency awareness matters, because the visible relationship is often not the one granting the access.

{% stepper %}
{% step %}

### Establish every path first

For the user and the entitlement in question, enumerate: the direct membership; each child role whose parent is this role; each group in the role that the user is also in; the organization affiliations; any business rule whose on-match target adds it; any open or approved request. Read the membership tags — they tell you which mechanism created each row.
{% endstep %}

{% step %}

### Remove or end-date at the source

If a business rule grants it, change the rule or its condition. If synchronization grants it, change the source scope. If a request granted it, revoke through the request path so the request history matches reality. Only remove the row directly when direct administration is genuinely the owner.
{% endstep %}

{% step %}

### Check the object still has no dependants before deleting it

Deleting a role or group is different from removing a membership. OpenIAM will refuse a delete while another object still points at the one you are deleting, so work through the dependants yourself first: child resources, groups, roles and users. It will not, however, warn you about references it does not hold as memberships — a role named by a business-rule target, by a certification campaign in flight, by a request catalog entry or by an SoD rule set should be cleared from those before you delete it, and nothing in the delete confirmation mentions them.
{% endstep %}

{% step %}

### Verify the far end

The OpenIAM row being gone is not the outcome. Confirm no second path still grants it, that the deprovisioning request completed, and that the account or group membership in the target changed. Reversing a deprovisioning is "reversible with consequences" — see [Change configuration safely](/administration-guide/start/change-safely.md#reversibility-honestly).
{% endstep %}
{% endstepper %}

## Verification

| What to check                          | Where                                                                                                                                                                                                      |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The membership row exists              | Both the role's user list and the user's role list                                                                                                                                                         |
| The correct mechanism owns it          | The membership's tag: `ADMIN_ASSIGNMENT`, `BUSINESS_RULE_ASSIGNMENT`, `ACCESS_REQUEST_ASSIGNMENT`, `SYSTEM_ASSIGNMENT`, `PENDING_MANUAL_ASSIGNMENT`                                                        |
| The change was recorded                | The matching `ADD_*` / `DELETE_*` audit action from the table above                                                                                                                                        |
| Effective access changed               | The user's effective access. Have the graph rebuilt only after confirming the membership is right — [Rebuild the authorization graph](/administration-guide/operations/rebuild-the-authorization-graph.md) |
| The target changed                     | The provisioning request and the connector reply                                                                                                                                                           |
| A comparison applied what you intended | Audit action `COMPARE_ROLE_ACCESS`, then reopen **Role Comparison** on the same pair and confirm the rows now agree                                                                                        |

## When it goes wrong

| Symptom                                                                                                                                                             | Cause                                                                                                                                        | Fix                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| A manual membership disappears                                                                                                                                      | A business rule's no-match group removes it — at the next save of that user, or population-wide if `PERFORM_BR_REPROVISION` has been enabled | Change the rule, or stop granting manually — see [Build a business rule](/administration-guide/access/business-rules.md) |
| Access remains after the membership is removed                                                                                                                      | Another path grants it: child role, group in the role, organization affiliation, or a second direct row                                      | Enumerate paths before removing; check membership tags                                                                   |
| End date passed and access is still live                                                                                                                            | `AUTO_REVOCATION` ships disabled                                                                                                             | Enable that batch task, or revoke explicitly                                                                             |
| A new membership silently does not appear                                                                                                                           | The role's **Max. User Number** is reached; OpenIAM drops the membership from the save rather than reporting an error                        | Raise the limit or remove a member; existing over-limit memberships are never trimmed                                    |
| Cannot set a resource parent                                                                                                                                        | The resource type has `SUPPORTS_HIERARCHY='N'`; error `RESOURCE_TYPE_NOT_SUPPORTS_HIERARCHY`                                                 | Check the type in [Resources and organizations](/administration-guide/access/resources-and-organizations.md)             |
| Compare shows no difference but the roles behave differently                                                                                                        | Compare covers child roles, groups and resources only, and excludes `MENU_ITEM` and `WORKFLOW` resources                                     | Compare users, attributes and scalar fields by hand                                                                      |
| A role grants a directory group and the account's group attribute is empty, or a group added to a role never reaches the target and the connector reports no change | Only the user's own direct group memberships are sent to a managed system, and only when one of them changed                                 | Grant the group directly as well — see [What nesting does not reach](#what-nesting-does-not-reach)                       |
| A group renamed in the directory appears twice in OpenIAM                                                                                                           | Incoming group records are matched by name within the managed system, so a renamed group is created afresh and the old row stays             | Match on an attribute the directory does not change, or reconcile                                                        |
| An operator added themselves to a privileged role                                                                                                                   | `org.openiam.ui.webconsole.allow.change.own.entitlements` is `true`; it ships `false`                                                        | Set it `false` — see [Stop an operator granting themselves access](#stop-an-operator-granting-themselves-access)         |
| Membership visible in OpenIAM, absent in the target                                                                                                                 | Managed-system association, policy map, or the provisioning request                                                                          | [Configure provisioning](/administration-guide/lifecycle/configure-provisioning.md)                                      |
| Target membership exists, OpenIAM does not show it                                                                                                                  | Reconciliation situation response or correlation rule                                                                                        | [Configure reconciliation](/administration-guide/lifecycle/configure-reconciliation.md)                                  |
| A role rename did not reach Active Directory                                                                                                                        | The group is target-owned; OpenIAM does not rename it                                                                                        | Rename in the directory and reconcile                                                                                    |

## Related

* [Create a role](/administration-guide/access/create-a-role.md) — the form and its fields
* [Role and group field reference](/administration-guide/access/manage-roles-and-groups/role-and-group-reference.md) — every field on both forms
* [Role engineering and maintenance](/administration-guide/access/role-engineering.md) — using compare and bulk operations as a method
* [Build a business rule](/administration-guide/access/business-rules.md) — the mechanism that will overwrite your manual change
* [Membership tags](/administration-guide/governance/certification/membership-tags.md) — reading how a grant happened

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/access/manage-roles-and-groups.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
