> 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/lifecycle/policy-map.md).

# Build a policy map

Map the attributes a managed system needs, test one against a disposable user, and save.

## What this is, and when you need it

A **policy map** turns an OpenIAM user or group into the attribute set a connector sends to a managed system. One row per target attribute: you name the attribute the target expects, and you say where its value comes from — a Groovy script, an OpenIAM field, or a literal. Nothing reaches a managed system that is not on a policy-map row. Build it once the managed system has a proven connection.

Do not confuse this with a **reconciliation** attribute map. Reconciliation configures its own rows and evaluates them in the opposite direction, reading the target's current value rather than computing the desired one — that is [Configure reconciliation](/administration-guide/lifecycle/configure-reconciliation.md).

The specific risk: a row whose source is a Groovy script and whose script throws is **silently skipped**. The failure is logged at debug level in the provisioning service log, and the connector object is built without that attribute. A broken `unicodePwd` row therefore looks like "AD created the account but the password is wrong", not like an error. Read [Change configuration safely](/administration-guide/start/change-safely.md) before editing a policy map on a live managed system.

## At a glance

|                          |                                                                                                                                                                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | One policy map and its rows on one managed system, and a `provisionconnectorrequest` document carrying exactly the attributes those rows should have produced and nothing else. Nothing reaches a user or a target until you provision someone |
| **Where**                | **Provisioning → Managed System**, then the system's policy maps — `/webconsole/policyMap?id=<MANAGED_SYSTEM_ID>`                                                                                                                              |
| **You need first**       | A managed system with a connector assigned and a proven heartbeat, the target's exact attribute names, the Groovy scripts registered, and the right to save a managed system — confirm which menu grant carries them                           |
| **Takes**                | 30–60 minutes for a minimum map; a full Active Directory map is nearer half a day                                                                                                                                                              |
| **Reversible**           | Partly — restoring the previous rows restores the previous behaviour on the very next operation, but not a target value a row has already overwritten                                                                                          |

## Before you begin

* A managed system with a connector assigned; the **Create Default Policies** shortcut also needs that connector to have policy maps of its own. [Verify connectivity to the target](/administration-guide/lifecycle/verify-connection.md).
* The target's real attribute names. OpenIAM does not translate them — the attribute name goes on the wire as written, so `sAMAccountName` and `samaccountname` are two different requests.
* The Groovy script IDs you intend to reference, already registered — the editor picks scripts from the registry, not from a free-text path. See [Shipped Groovy scripts](/administration-guide/reference/shipped-groovy-scripts.md).
* The object type user provisioning looks for. `org.openiam.idm.managed.sys.policy.user.type` ships as `USER_OBJECT`, and a map created under any other type is invisible to provisioning, with no warning. **It has no row on Administration → System Configuration**: assume `USER_OBJECT` unless the deployment owner says otherwise.
* `<MANAGED_SYSTEM_ID>` and a `<TEST_USER>` you are willing to provision repeatedly.

## Build the map

{% stepper %}
{% step %}

### Open the policy-map list for the managed system

Go to **Provisioning → Managed System**, open your system, then follow its policy maps to `/webconsole/policyMap?id=<MANAGED_SYSTEM_ID>`. The list shows Name, policy type and Primary.
{% endstep %}

{% step %}

### Create the map, or copy the connector's

If the system has no maps and its connector ships policy maps of its own, a **Create Default Policies** button appears. Use it: it copies every template row onto a new map of your own, which is the fastest way to a working starting point, and editing the copy never touches the template.

Otherwise **Add** opens a dialog listing the object types. Choose the user type and the editor opens at `/webconsole/policyMapEdit?managedSysId=<MANAGED_SYSTEM_ID>&type=USER_OBJECT`. Give the map a name and decide **Mark this policy map as primary**.

Create **one** map of the user type. OpenIAM builds one connector object per matching map, so two maps of the same type means two objects sent for the same user. The third route, cloning a whole managed system with its maps, is in [Creating a map: the three routes](/administration-guide/lifecycle/policy-map/policy-map-reference.md#creating-a-map-the-three-routes).
{% endstep %}

{% step %}

### Add the minimum rows

Each **Add** in the Policy Maps table appends a row with Status pre-checked. Fill four fields:

| Field           | What to enter                                                                                                                                                                                  |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Object type     | `PRINCIPAL` for the target's naming attribute, `PASSWORD` for its password attribute, `USER` for everything else.                                                                              |
| Attribute name  | The target's attribute name, exactly as the target spells it. Free text, unless OpenIAM could read a schema from the connector, in which case it becomes a dropdown of real target attributes. |
| Type            | **Groovy** to compute the value, **IDM value** to copy an OpenIAM field straight off the change, **Static value** for a literal.                                                               |
| Attribute Value | The registry script, the IDM field name, or the literal — whichever the Type calls for. Saving a row clears the other two, so a row can only ever have one source.                             |

The minimum map is three kinds of row: **exactly one** `PRINCIPAL` row carrying the naming attribute (`cn` for Active Directory), one `PASSWORD` row named for the target's password attribute (`unicodePwd` for Active Directory) if the target needs a password at creation, and one `USER` row for each attribute the target requires. Add the required attributes only for now — every extra row is another thing that can fail silently on the first test.

A row with no Attribute Value, and a row whose Status is unchecked, are both skipped in silence. A worked map with all thirty-three rows an Active Directory system typically carries is in [A complete Active Directory user policy map](/administration-guide/lifecycle/policy-map/policy-map-reference.md#a-complete-active-directory-user-policy-map).
{% endstep %}

{% step %}

### Save

**Save** audits `MODIFY_MANAGED_SYSTEM_POLICY`, which carries the full request body of the save. Rows you never touched are submitted as `NO_CHANGE` and are not rewritten.

The save is also the first validation: it refuses `Some policy records are empty`, `2 attributes with same name found`, `Principal should be unique attribute`, `One or more attribute names are empty` and `Policy Map Name cannot be blank`. Each is in [When it goes wrong](#when-it-goes-wrong).
{% endstep %}

{% step %}

### Provision one test user and read the connector request

Put the managed system into simulation for `Add` and `Save` first — [Simulation mode](/administration-guide/lifecycle/simulation-mode.md) holds the generated object for review before it is applied, and audits `SIMULATE_PROVISIONING` then `APPLY_SIMULATION_REQ`. Then provision `<TEST_USER>` and read the captured attribute set against your rows, one row at a time. A skipped row shows up as an absence, so check for what is missing before you check the values. Use a disposable `<TEST_USER>` and a disposable target container for this.
{% endstep %}
{% endstepper %}

## Verification

* **Connector request.** The generated object is persisted to the `provisionconnectorrequest` index (`PROVISION_CONNECTOR_REQUEST_DOC`) and the reply to `connectorreply` (`CONNECTOR_REPLY`); the streamed request lands in `provisionstream`. See [Search indexes and the authorization graph](/administration-guide/reference/search-indexes.md). Confirm that every attribute you expect is present *and* that nothing extra is — a silently skipped row shows up as an absence.
* **Audit.** `PROVISIONING_ADD` for a create, `PROVISIONING_MODIFY` for a modify. The diff is attached to the audit event, so the audit record tells you what OpenIAM thought changed, independently of what the policy map emitted.
* **Managed-system logs.** Every shipped script logs its computed value at info under a logger named after itself — `sAMAccountName`, `GivenName`, `MemberOf`, `AccountExpires`, `UserAccountControl`. Raising those to info/debug is the fastest way to see per-attribute output without simulation.
* **Target object.** For AD, read the attributes back with the same names you put in the Attribute name column. A value in OpenIAM and no value in the target, with no error anywhere, is the signature of a skipped row.
* **Policy-map change itself.** `MODIFY_MANAGED_SYSTEM_POLICY` carries the full request body of the save.

## When it goes wrong

| Symptom                                                                                                                   | Cause                                                                                                                                                                                                                                                                                                                                        | Fix                                                                                                                                                                                                |
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| :---                                                                                                                      | :---                                                                                                                                                                                                                                                                                                                                         | :---                                                                                                                                                                                               |
| Attribute never appears in the connector request, no error                                                                | Row's **Status** unchecked, or **Attribute name** empty. `validateAttributeMapConfiguration` returns `-1` and the row is skipped with "Attribute Map is inactive or name is empty!"                                                                                                                                                          | Tick Status; confirm `ATTRIBUTE_MAP.IS_ACTIVE = 'Y'` and `ATTRIBUTE_NAME` non-blank.                                                                                                               |
| Attribute missing only on modify, present on create                                                                       | The script's `isPerform` returned false because the diff field it tests was not dirty.                                                                                                                                                                                                                                                       | Check which diff the script reads; widen `isPerform` or switch the row's source.                                                                                                                   |
| Attribute missing and log shows `Script not found:<id>`                                                                   | **Attribute Value** holds a script ID that is not in the registry (typo, or the script was deleted after the row was saved).                                                                                                                                                                                                                 | Re-select the script in the Attribute Value autocomplete.                                                                                                                                          |
| Attribute missing and log shows `Could not put value for attribute map <name>`                                            | Script threw, or an IDM-value row resolved to blank.                                                                                                                                                                                                                                                                                         | Read the cause on that log line. For IDM-value rows, a blank source is expected behaviour — move to Groovy if you need to send blanks.                                                             |
| Cleared field in OpenIAM leaves the old value in the target                                                               | Row's Type is **IDM value**, which never emits blanks.                                                                                                                                                                                                                                                                                       | Convert the row to **Groovy** with an else branch that adds `""`, as `title_1.0.groovy` does.                                                                                                      |
| Password wrong or absent in the target on create                                                                          | `PASSWORD` row missing, inactive, or its script's `isPerform` too narrow. Also check `isChangePasswordOperation` — on an already-provisioned identity a modify sends no password at all.                                                                                                                                                     | Verify there is exactly one `PASSWORD` row and that its attribute name is the target's password attribute (`unicodePwd` for AD).                                                                   |
| Login created in OpenIAM but target object has the wrong naming attribute                                                 | The naming attribute was placed on a `USER` row instead of the `PRINCIPAL` row. `mergeIdentity` sets `identityName` from the `PRINCIPAL` row's attribute name and `identityValue` from the login.                                                                                                                                            | Move the naming attribute (`cn` for AD) onto the `PRINCIPAL` row.                                                                                                                                  |
| `memberOf` empty although OpenIAM shows group membership                                                                  | The group has no attribute named `dn`, or `GRP.MANAGED_SYS_ID` does not match this managed system. Both are silent skips in `memberOf_1.0.groovy`.                                                                                                                                                                                           | Add a `dn` group attribute; confirm the group is bound to this managed system.                                                                                                                     |
| Two connector objects sent per user                                                                                       | Two policy maps of type `USER_OBJECT` on the same managed system, and one object is built per map.                                                                                                                                                                                                                                           | Delete the duplicate map, or mark only one **primary** and remove the other.                                                                                                                       |
| `Some policy records are empty` on save                                                                                   | A `DEFAULT_IDM` row with no IDM field, a `POLICY` row with no Groovy script, or a `STATIC_VALUE` row with no static value.                                                                                                                                                                                                                   | Fill the Attribute Value for the Type you chose.                                                                                                                                                   |
| `2 attributes with same name found` on save                                                                               | Two non-`PRINCIPAL` rows share an Attribute name. Validation merges the rows you submitted with the ones already stored, so the duplicate may be a row you never opened.                                                                                                                                                                     | Rename or delete one. Use multiple values on one row instead of two rows.                                                                                                                          |
| `Principal should be unique attribute` on save                                                                            | More than one row whose Object type contains `PRINCIPAL` (`PRINCIPAL`, `GROUP_PRINCIPAL`, `COMPUTER_PRINCIPAL` all count).                                                                                                                                                                                                                   | Keep exactly one.                                                                                                                                                                                  |
| `One or more attribute names are empty` / `Policy Map Name cannot be blank` on save                                       | An added row was never filled, or the map has no name.                                                                                                                                                                                                                                                                                       | Fill or delete the row; name the map.                                                                                                                                                              |
| An edited script keeps its old behaviour, then starts working a few minutes later with no restart                         | The compiled script instance is cached and evicted only on a timer, `org.openiam.idm.cache.evict.rate`, which ships as `180000` ms.                                                                                                                                                                                                          | Wait three minutes per IDM node before re-testing. See [Instance lifecycle](/administration-guide/lifecycle/policy-map/policy-map-reference.md#instance-lifecycle-one-cached-instance-per-script). |
| Create fails with `An attempt was made to add an object to the directory with a name that is already in use.`             | An object with the same RDN already exists in the target container, and it need not be a user — a `contact`, `group` or `computer` blocks the name. OpenIAM cannot see it: every user lookup the AD connector makes uses `Get-ADUser`, which returns user objects only, so a user synchronization never imports the colliding object either. | Search the container with `Get-ADObject`, then rename or remove the colliding object, or change the `PRINCIPAL` row's generation rule.                                                             |
| Create fails with `LdapErr: DSID-0C091211, comment: Error in attribute conversion operation, data 0, v3839`               | A value was sent in the wrong form for a binary target attribute — usually a password the script encoded before handing it over.                                                                                                                                                                                                             | Send the plain string. See [Every value on the wire is a string](/administration-guide/lifecycle/policy-map/policy-map-reference.md#every-value-on-the-wire-is-a-string).                          |
| Adding an `Organization` policy map, or a `distinguishedName` row, does not make organizational-unit synchronization work | A policy map is read only when OpenIAM provisions **out** to a managed system. A synchronization pull is driven by its own synchronization configuration and its attribute script, and never consults a policy map at all.                                                                                                                   | Configure the synchronization, not the policy map.                                                                                                                                                 |

Symptoms that come from the engine's own behaviour rather than the map's configuration — a value appearing on the wrong user under load, an edited script that behaves as if it were not edited, an account the Disable operation will not disable, an attribute the Active Directory connector drops — are in [Advanced symptoms](/administration-guide/lifecycle/policy-map/policy-map-reference.md#advanced-symptoms).

## Rollback

A policy-map change is reversible up to the point where something has been sent. Delete a single row in the editor and save — audited as `DELETE_MANAGED_SYSTEM_POLICY` — or delete the whole map, audited as `DELETE_MANAGED_SYSTEM_POLICY_MAP`. Restoring the previous rows restores the previous behaviour on the very next operation.

What is not reversible is what the target already received. Removing a row stops OpenIAM sending that attribute; it does not put the target's old value back, and for an attribute a row overwrote you need the target's own history. Untick **Active** on the managed system if you need everything to stop while you work out what was sent.

## Advanced behaviour and implementation notes

**A failing row does not fail the operation.** A row that throws is caught, logged at debug level, and skipped; any other failure is logged at error level as `Internal error`. Neither aborts the object, so an operation with every row failing still reports success — and sends an object with no attributes, which then sends nothing at all. See [Configure provisioning](/administration-guide/lifecycle/configure-provisioning.md).

**One connector object is built per matching map**, and whether all fields are processed is decided once per object: while an identity has never been successfully provisioned every row is forced to evaluate, and after that only the rows whose source says the value changed.

The rest of the behaviour behind a row lives in [Policy map reference](/administration-guide/lifecycle/policy-map/policy-map-reference.md): [how a row is stored and dispatched](/administration-guide/lifecycle/policy-map/policy-map-reference.md#evaluation-order), [the script contract and what a script may assume](/administration-guide/lifecycle/policy-map/policy-map-reference.md#the-policy-map-script-contract), [group membership](/administration-guide/lifecycle/policy-map/policy-map-reference.md#groups-memberof-and-membershipdiff), [status changes and account enablement](/administration-guide/lifecycle/policy-map/policy-map-reference.md#status-changes-and-account-enablement), and [null and multi-value semantics](/administration-guide/lifecycle/policy-map/policy-map-reference.md#null-and-multi-value-semantics).

## Related

* [Policy map reference](/administration-guide/lifecycle/policy-map/policy-map-reference.md) — the evaluation rules, the script contract, the worked Active Directory map and the connector-specific behaviour.
* [Attach a provisioning processor](/administration-guide/lifecycle/provisioning-processors.md) — the hooks that run around the same provisioning operations and can mutate the user the policy map then reads.
* [Configure provisioning](/administration-guide/lifecycle/configure-provisioning.md) — the managed-system settings that decide whether the object built here is ever sent.
* [Configure reconciliation](/administration-guide/lifecycle/configure-reconciliation.md) — reading the target's current values instead of computing the desired ones.
* [Shipped Groovy scripts](/administration-guide/reference/shipped-groovy-scripts.md) — the full inventory of scripts to start from.
* [Change configuration safely](/administration-guide/start/change-safely.md) — how to stage a policy-map change.

*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/lifecycle/policy-map.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.
