> 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/developer-guide/reconciliation-scripts.md).

# Reconciliation scripts

Act on the differences reconciliation finds between OpenIAM and a managed system — situations, command scripts, and an annotated shipped example.

Reconciliation compares what OpenIAM holds against what a managed system holds, classifies each difference as a **situation**, and runs the command script bound to that situation.

**You write the command, not the comparison.** The platform decides that an account exists in the target and not in OpenIAM; your script decides what to do about it.

## What this builds, and how long it takes

|                |                                                                                             |
| -------------- | ------------------------------------------------------------------------------------------- |
| **You get**    | A script that acts on one class of difference between OpenIAM and a managed system          |
| **Time**       | Two hours, including a reporting run before you let it act                                  |
| **You need**   | A deployment, a reconciliation configuration, and a **small** managed system to run against |
| **Reversible** | Unbinding the script is immediate. **What it already did is not** — see the warning below   |

## Before you start

* [*Set up your environment*](/developer-guide/set-up-your-environment.md) and [*How scripting works*](/developer-guide/how-scripting-works.md).
* **A small managed system.** A command bound to the wrong situation and run against a full directory can create or disable a large number of objects quickly.
* **A reporting run, or a no-op command, before anything that acts.** Step 5 covers this.

***

## Build it

### 1. Understand situations

A situation is a classification of one difference:

| Situation                     | Means                                                            |
| ----------------------------- | ---------------------------------------------------------------- |
| **Match found**               | The account exists in both. Attributes may still differ          |
| **In target, not in OpenIAM** | An account exists in the managed system with no OpenIAM identity |
| **In OpenIAM, not in target** | An identity that should have an account and does not             |
| **Attribute mismatch**        | Both exist; a value differs                                      |

Each is bound to a command script in the reconciliation configuration. **Binding no script is a valid choice** — it means observe and report without acting.

### 2. Understand what a command receives

```groovy
void execute(AbstractPopulationScript<ProvisionGroup> populationScript,
             String principal,
             String mSysID,
             ProvisionGroup pGroup,
             Map<String, Attribute> attributes,
             String parentAuditLogId,
             Map<String, Object> dataMap) throws Exception
```

| Argument           | Is                                                                      |
| ------------------ | ----------------------------------------------------------------------- |
| `populationScript` | An optional script that fills the OpenIAM object from target attributes |
| `principal`        | The identifier in the managed system                                    |
| `mSysID`           | The managed system                                                      |
| `pGroup` / `pUser` | The OpenIAM object, to populate or act on                               |
| `attributes`       | What the target holds, keyed by attribute name                          |
| `parentAuditLogId` | The audit entry this run belongs to                                     |
| `dataMap`          | Values passed through from the configuration                            |

**It throws.** Unlike transformation, which returns a signal, a reconciliation command reports failure by throwing — and the platform records it against `parentAuditLogId`.

### 3. Read a shipped example

`conf/iamscripts/recon/DoNothingGroupCommand_1.0.groovy`, abridged:

```groovy
import org.openiam.common.service.AbstractProvisioningService
import org.openiam.provision.dto.ProvisionGroup
import org.openiam.provision.type.Attribute
import org.openiam.recon.command.grp.BaseReconciliationGroupCommand
import org.openiam.recon.service.impl.service.AbstractPopulationScript

class DoNothingGroupCommand extends BaseReconciliationGroupCommand {

    static final Log log = LogFactory.getLog("DoNothingGroupCommand")

    @Override
    void execute(AbstractPopulationScript<ProvisionGroup> populationScript, String principal,
                 String mSysID, ProvisionGroup pGroup, Map<String, Attribute> attributes,
                 String parentAuditLogId, Map<String, Object> dataMap) throws Exception {

        log.debug("Do nothing for Group :" + principal)
        pGroup.setSrcSystemId(mSysID)

        if (populationScript) {
            try {
                executeScript(populationScript, attributes, pGroup, dataMap)
            } catch (Exception e) {
                throw new Exception("Couldn't populate ProvisionGroup")
            }
        }
    }
}
```

**"Do nothing" still does two things**, and both matter:

* **`pGroup.setSrcSystemId(mSysID)`** records where the object came from. Even a command that takes no action records provenance.
* **It runs the population script if one is configured.** `executeScript(...)` comes from the base class, and running it means the OpenIAM object reflects the target's attributes even when the command does not act.

**Start from this script.** A no-op that records provenance is the right first command for a new reconciliation — run it, read the audit log, and only then decide what should actually happen.

### 4. Write a command that acts

The shape is the same; the body changes:

```groovy
@Override
void execute(AbstractPopulationScript<ProvisionGroup> populationScript, String principal,
             String mSysID, ProvisionGroup pGroup, Map<String, Attribute> attributes,
             String parentAuditLogId, Map<String, Object> dataMap) throws Exception {

    pGroup.setSrcSystemId(mSysID)

    if (populationScript) {
        executeScript(populationScript, attributes, pGroup, dataMap)
    }

    // then call the provisioning service to create it
}
```

**Read the shipped commands under `conf/iamscripts/recon/` first.** `CreateIdmGroupCommand` and `UpdateIdmGroupCommand` show the full form, including how they reach the provisioning service.

### 5. Deploy it — carefully

1. Groovy Manager → the `recon/` folder.
2. **New Script**, named for what it does. The shipped set uses names like `DoNothingGroupCommand`.
3. **Save.**
4. **Bind a no-op command first**, or run reconciliation in a reporting mode if your configuration supports one.
5. Read the report. Confirm the situations are classified as you expect and the counts are what you expect.
6. **Only then** bind the command that acts.

***

## Verify it

| Check                                | Where                                                        |
| ------------------------------------ | ------------------------------------------------------------ |
| The command ran                      | Its log output, and the audit entry under `parentAuditLogId` |
| The situation classified as expected | The reconciliation report                                    |
| The object was populated             | The object in the console                                    |
| Failures were recorded               | The audit log — a thrown exception appears there             |

**The count is the check that matters.** If a situation you expected to match a handful of accounts matched hundreds, stop before binding anything that acts.

## If it goes wrong

| What you see                            | What it means                                                  | What to do                                                                                      |
| --------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| The command never runs                  | Not bound to that situation, or the situation is not occurring | Check the binding, then whether the report shows that situation at all                          |
| `Couldn't populate ProvisionGroup`      | The population script failed                                   | Read the cause. The shipped example wraps it and loses the original — log `e` before rethrowing |
| Objects created without a source system | `setSrcSystemId` was not called                                | Add it. It is how later runs know where the object came from                                    |
| Too many objects affected               | Bound to a broader situation than intended                     | Unbind, re-run in reporting mode, read the report before rebinding                              |
| Nothing in the audit log                | The command threw before doing anything, or is not bound       | Check the service log for a compilation or binding error                                        |

## What this procedure does not cover

Configuring reconciliation — the managed system, the schedule, and which situations are evaluated — which is the [Administration Guide](/administration-guide/admin.md)'s subject. Undoing what a command already did; that is a restore, covered in the [Installation Guide](/installation/installation.md).

## Related

* [*Batch scripts*](/developer-guide/batch-scripts.md) — reconciliation is commonly run as a batch task
* [*How scripting works*](/developer-guide/how-scripting-works.md)

*Applies to OpenIAM 2026.8.1.*


---

# 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/developer-guide/reconciliation-scripts.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.
