> 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/reference/groovy-script-reference.md).

# Groovy script reference

Where a Groovy script is stored, every column a reference to one can live in, which OpenIAM services each script family may inject, and the sandbox allow-list.

## What this is, and when you need it

Three things about Groovy scripts are invisible from the Groovy Manager screen and decide whether a change behaves: where the script actually lives, which of two dozen columns holds the reference that will run it, and whether the process that runs it can reach the services the script injects.

Creating, versioning, testing and rolling a script back is [Create a Groovy script](/administration-guide/automation/manage-groovy-scripts.md). Use this page when you are deleting a script, promoting one, chasing a reference, or working out why a script that saved cleanly fails where it runs.

## Where a script actually lives

Two places at once, and you need both facts to reason about promotion and rollback.

**Metadata in the `groovy` schema.** `GROOVY_FILE` is the only table in that schema, and these are its columns — worth knowing because it is the one place you can see every version of every script in a single query:

| Column             | Holds                                                       |
| ------------------ | ----------------------------------------------------------- |
| `GROOVY_SCRIPT_ID` | The 32-character id that almost every reference stores      |
| `FILE_NAME`        | The script name, without the `.groovy` suffix               |
| `PATH`             | The folder shown in the tree                                |
| `VERSION`          | `1.0`, `1.1` and so on, to two decimal places               |
| `DESCRIPTION`      | The only field you can change on an existing version        |
| `LAST_MODIFIED`    | Advances on every save; the check that a save really landed |
| `GROOVY_FILE_NAME` | Unused in this release, always empty                        |

{% hint style="warning" %}
**Break-glass, read-only.** Groovy Manager shows you one script at a time, so a `SELECT` against `GROOVY_FILE` is the only way to answer questions about the catalogue as a whole — every version of one script, everything saved since a date, two scripts that collide on path, name and version. Reading it is outside the supported path only in the sense that it is unsupported to *rely* on: the column set can change between releases, and a query is not audited, so nothing records that you looked. Keep it to `SELECT`. Never `INSERT`, `UPDATE` or `DELETE` here — the row and the script file on disk have to agree, no constraint enforces that they do, and a row you write by hand has no file behind it and will open with an empty body.
{% endhint %}

The `groovy` schema is a **separate database with its own connection**, not a set of tables inside `openiam`. A backup that covers only the `openiam` schema does not contain your scripts' metadata. Nothing in the Webconsole shows you which schemas your deployment backs up, so confirm that with whoever owns it — see the [Installation Guide](/installation/installation.md).

**Body on the filesystem.** The script body is not stored in the database. It is written to a file under the script root named by `org.openiam.groovy.script.root`, at `<PATH>/<FILE_NAME>_<VERSION>.groovy`, and a save that tried to write outside that root is refused. Reads come from an in-memory copy of that directory, refreshed every `org.openiam.groovy.script.file.local.cache.sweep` ms — shipped **30000**.

Two operational consequences:

* **A restore must cover both**, the `groovy` schema and the script files. A row whose file is missing opens with an empty body; a file with no row is invisible to every caller and to the tree.
* **Editing a file on disk is not supported, but it does take effect.** The next 30-second sweep picks the new body up, so the change goes live with no save and **no audit record** — which means nothing in the audit trail will ever explain the behaviour change. Treat a direct file edit as an incident to investigate, not a technique.

## The one-time path-to-ID migration

Once per service, on the first start after upgrade, OpenIAM converted script references that were stored as paths into ids. Any such value ending `.groovy` was looked up by path and name **with the version forced to `1.0`** and rewritten to that script's id — which is how a batch task that shipped with a path came to hold an id.

Two outcomes matter to you. Where the lookup found nothing, the column was set to **null** and the log records `Could not find groovy script: <name>. Path <path>:<file>` — so a script renamed before the upgrade left its owner with no reference at all, silently and permanently. Where it found more than one match the migration failed rather than guessing. The migration is audited as `MIGRATE_GROOVY_SCRIPTS`; that event, and the log line above, are the only records that it ran, so check both after an upgrade rather than assuming references survived.

## Every place a script can be attached

**Show References** — the network icon on the editor tab — asks every service which of its own objects point at this script, and it is the first thing to open before you branch, re-point or delete anything. It answers "where else is this script used" by inspecting exactly the columns below and nothing else, which is why the two exceptions after the table matter as much as the table does. These are also the columns to query directly if you need the answer for every script at once rather than one at a time:

| Table                                    | Column                                                                                                                                                                                       | Reported as                                                    |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `AUTH_PROVIDER`                          | `GROOVY_SCRIPT_ID`, `CERT_AUTH_REGEX_SCRIPT_ID`, `SMS_OTP_GROOVY_SCRIPT_ID`, `CERT_AUTH_CA_VALIDATE_SCRIPT_ID`                                                                               | `AUTH_PROVIDER`                                                |
| `AUTH_PROVIDER_ATTRIBUTE`                | `VALUE`                                                                                                                                                                                      | `AUTH_PROVIDER`                                                |
| `URI_PATTERN`                            | `REDIRECT_TO_GROOVY_SCRIPT_ID`                                                                                                                                                               | `URI_PATTERN`                                                  |
| `URI_PATTERN_META_VALUE`                 | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `URI_PATTERN`                                                  |
| `PATTERN_METHOD_META_VALUE`              | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `URI_PATTERN`                                                  |
| `QUESTIONNAIRE`                          | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `QUESTIONNAIRE`                                                |
| `RECON_RES_ATTR_MAP`                     | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `RECON_RES_ATTR_MAP` (this is the policy-map attribute script) |
| `RES`                                    | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `RES`                                                          |
| `RESOURCE_PROP` / `RES_ATTRIBUTE_VALUES` | `ATTR_VALUE`                                                                                                                                                                                 | `RES`                                                          |
| `POLICY`                                 | `GROOVY_SCRIPT_ID`                                                                                                                                                                           | `POLICY`                                                       |
| `SYNCH_CONFIG`                           | `PREPROCESSOR_SCRIPT_ID`, `POSTPROCESSOR_SCRIPT_ID`, `VALIDATION_SCRIPT_ID`, `TRANSFORMATION_SCRIPT_ID`, `CUSTOM_MATCH_SCRIPT_ID`, `CUSTOM_ADAPTER_SCRIPT_ID`, `ATTR_NAMES_LOOKUP_SCRIPT_ID` | `SYNCH_CONFIG`                                                 |
| `RECONCILIATION_SITUATION`               | `GROOVY_SCRIPT_ID`, `CUSTOM_COMMAND_SCRIPT_ID`                                                                                                                                               | `RECONCILIATION_CONFIG`                                        |
| `RECONCILIATION_CONFIG`                  | `TARGET_SYS_MATCH_SCRIPT_ID`, `MATCH_SCRIPT_ID`, `CUSTOM_IDENT_MATCH_SCRIPT_ID`, `RECON_CUSTOM_PROCESSOR_ID`, `POST_PROCESSOR_ID`, `PRE_PROCESSOR_ID`                                        | `RECONCILIATION_CONFIG`                                        |
| `AUTH_RESOURCE_ATTRIBUTE_MAP`            | `AM_POLICY_SCRIPT_ID`                                                                                                                                                                        | `AUTH_RESOURCE_ATTRIBUTE_MAP`                                  |
| `METADATA_ELEMENT`                       | `DATA_MODEL_SCRIPT_ID`                                                                                                                                                                       | `METADATA_ELEMENT`                                             |
| `METADATA_ELEMENT_PAGE_TEMPLATE`         | `DATA_MODEL_SCRIPT_ID`, `CUSTOM_VALIDATION_SCRIPT_ID`                                                                                                                                        | `METADATA_ELEMENT_PAGE_TEMPLATE`                               |
| `REPORT_INFO`                            | `DATASOURCE_SCRIPT_ID`                                                                                                                                                                       | `REPORT_INFO`                                                  |
| `PROPERTY_FILE_VALUES`                   | `PROPERTY_VALUE`                                                                                                                                                                             | `PROPERTY_FILE_VALUES`                                         |
| `OTP_PROVIDER_ATTR`                      | `VALUE`                                                                                                                                                                                      | `OTP_PROVIDER` — **see the warning below**                     |
| `ADAPTIVE_RISK`                          | `RISK_VALUE`                                                                                                                                                                                 | `ADAPTIVE_RISK`                                                |
| `POLICY_DEF_PARAM`                       | `POLICY_PARAM_HANDLER`                                                                                                                                                                       | `POLICY_DEF_PARAM`                                             |

The batch-task service adds `BATCH_CONFIG.GROOVY_SCRIPT_ID` to the same list, so batch references appear in the popover too. Most entries are clickable: `SYNCH_CONFIG` opens `/webconsole/synchronization?id=…`, `BATCH_CONFIG` opens `/webconsole/batchTask?id=…` and `RECONCILIATION_CONFIG` opens `/webconsole/reconciliationEdit?id=…`. Four are deliberately not — `RECON_RES_ATTR_MAP`, `AUTH_RESOURCE_ATTRIBUTE_MAP`, `ADAPTIVE_RISK` and `POLICY_DEF_PARAM` show a name with no link, so you have to find the owning object yourself. A name with no link is still a reference and still blocks deletion.

{% hint style="danger" %}
**A script referenced by an OTP provider breaks the reference lookup for the whole catalogue.** An OTP provider attribute holding a script id produces a reference OpenIAM cannot classify, and the failure is not contained: **Show References** and **Compile All Groovy Scripts** both stop working for *every* script, not just that one, as soon as any `OTP_PROVIDER_ATTR.VALUE` matches a script id. If those two buttons started erroring right after you configured a Groovy handler on an OTP provider, that is the cause and the timing is the evidence. **Verify it on your release** — configure one on a test provider and click **Show References** on any unrelated script — and raise it with support rather than working around it; there is no setting that disables the offending lookup.
{% endhint %}

{% hint style="danger" %}
**Two families of reference are invisible to Show References, and both hold paths rather than ids.** The twelve `MANAGED_SYS.*_HNDLR` columns used by the Groovy Script connector hold script paths — managed system `104` has shipped with `example/AddScriptConnector.groovy` in them since 3.1.0.0 — and the `Workflow`-category script properties hold paths too. The reference lookup compares `PROPERTY_FILE_VALUES.PROPERTY_VALUE` to the script **id** for exact equality, so a property holding a path matches nothing and the script looks unreferenced. **Show References returning nothing is therefore not proof that a script is unused.** Before deleting one, open the managed systems that use the Groovy connector and read their handler columns, and read the `Workflow`-category property values on **Administration → System Configuration**.
{% endhint %}

## Which services a script can inject, by script type

A script can inject an OpenIAM service to call it — but only if the process that runs that kind of script has the service available, and one family of scripts does not. The symptom is a compile error, and it looks like this:

{% code overflow="wrap" %}

```
unable to resolve class org.openiam.common.beans.mq.GroupRabbitMQService
```

{% endcode %}

It is not a sandbox rejection — the sandbox allows the whole of `org.openiam`, and [Adding a package to the sandbox](#adding-a-package-to-the-sandbox) covers what a rejection looks like instead. It means the class was not available to **the process that compiled the script**, and that depends entirely on where the script type runs.

`GroupRabbitMQService` is one of the 111 interfaces a script can inject to call another OpenIAM service: groups, managed systems, users and so on. Two things have to be true for an injection to work — the interface has to be available to the process that compiles and runs the script, and that process has to be one that publishes those beans. Both are true in every OpenIAM service and false in a remote connector.

| Script family                                                     | Shipped under                  | Compiled and executed by             | `org.openiam.common.beans.mq` reachable | Shipped scripts that inject it                 |
| ----------------------------------------------------------------- | ------------------------------ | ------------------------------------ | --------------------------------------- | ---------------------------------------------- |
| Policy map and provisioning attribute                             | `provision/`                   | `idm`                                | Yes                                     | 80 of 397                                      |
| Synchronization pre/post, transform, match                        | `sync/`                        | `synchronization`                    | Yes                                     | 25 of 206                                      |
| Reconciliation situation and custom command                       | `recon/`                       | `reconciliation`                     | Yes                                     | 14 of 41                                       |
| Batch task                                                        | `batch/`                       | `batch-task-manager`                 | Yes                                     | 11 of 14                                       |
| Authentication and authorization (auth provider, OTP, cert regex) | `AM/`, `idp/`, `oauth/`        | `esb` and the `idp` UI service       | Yes                                     | 2 of 16 under `AM/`                            |
| URI pattern redirect and metadata                                 | `uri/`                         | the UI service that owns the request | Yes                                     | 1 of 1                                         |
| Workflow and approver association                                 | `bpm/`, `workflow/`            | `workflow`                           | Yes                                     | 1 of 2 under `workflow/`; 0 of 12 under `bpm/` |
| Report data source                                                | `reports/`                     | `esb`                                | Yes                                     | 1 of 1                                         |
| Self-service and page template                                    | `selfservice/`, `ui-template/` | `selfservice`, `webconsole`          | Yes                                     | 2 of 7 under `selfservice/`                    |
| Email template and custom notification                            | `mail/`                        | `email-manager`                      | Yes                                     | 0 of 2                                         |
| **Groovy Script connector handler**                               | `connector/groovy/`            | **the connector process**            | **No**                                  | **0 of 11**                                    |

Every "Yes" row is an OpenIAM service — `idm`, `synchronization`, `reconciliation`, `batch-task-manager`, `workflow`, `email-manager`, `esb`, `groovy-manager`, and the `webconsole`, `idp`, `selfservice` and `reportviewer` UI services. All of them carry the MQ client interfaces and publish them as beans, so an injection in a script that runs there works. **This cannot be confirmed from the Webconsole**: no screen lists what a service can inject. The observable check is to write a trial script of the family in question that injects the interface, save it, and see whether it compiles.

The "No" row is the exception, and it is the one that produces the error. A Groovy Script connector handler — the scripts named in the twelve `MANAGED_SYS.*_HNDLR` columns — extends `AbstractCommandExecutor` and runs inside the **connector**, which is a separate process with a much smaller API available to it and no MQ client layer at all. The shipped catalogue reflects this: **not one** of the eleven shipped connector scripts injects an MQ service, while 80 of the provisioning scripts do. If you are writing a connector handler, treat any MQ injection as unavailable rather than untested.

{% hint style="danger" %}
**Compile All will not catch this, and neither will Save.** Both compile the script inside an OpenIAM service, which *does* have the MQ client layer available to it. A connector-bound script that injects an MQ service therefore compiles cleanly, saves cleanly and passes [Compile the whole catalog](/administration-guide/automation/manage-groovy-scripts.md#compile-the-whole-catalog) — then fails with `unable to resolve class org.openiam.common.beans.mq.GroupRabbitMQService` the first time the connector compiles it, in the **connector's own log** rather than any OpenIAM service log. Nothing in the Webconsole will show you that failure. If a script that saved without complaint is failing on the connector, read the connector log first; getting to that log is deployment-level access, so if you do not have it, ask for the log rather than editing the script on a guess.
{% endhint %}

### The workaround: move the lookup upstream

Do not try to reach OpenIAM data from inside a connector-side script. Resolve it in a script type that runs inside an OpenIAM service and pass the answer to the connector as an attribute value:

* **A policy-map script** is the natural place. An `AbstractIPolicyMapGroovy` implementation runs in `idm` and can inject `GroupRabbitMQService` and `ManagedSystemRabbitMQService` freely — the shipped `provision/ad/memberOf_1.0.groovy` does exactly that — and its return value becomes an attribute the connector receives. See [Build a policy map](/administration-guide/lifecycle/policy-map.md), and in particular [Groups: `memberOf` and `MembershipDiff`](/administration-guide/lifecycle/policy-map/policy-map-reference.md#groups-memberof-and-membershipdiff) for the group-membership case that drives most of these requests.
* **A synchronization pre-processor** works when the data is per-record rather than per-attribute; those run in the `synchronization` service. See [Synchronization script contracts](/administration-guide/reference/synchronization-script-contracts.md).

Neither is a workaround in the grudging sense. The connector's job is to talk to the managed system; deciding *what* to send it is the policy map's job, and a connector script that needs to query OpenIAM's own group graph is usually a sign the logic is in the wrong place.

## Adding a package to the sandbox

Scripts are compiled statically inside a sandbox, and only allow-listed packages may be referenced. A reference outside the list is a compile failure — both when you save the script and when a stored script is compiled to run — with `Method calls not allowed on` or `You tried to call a method which is not allowed` in the message. That wording is how you tell a sandbox rejection from an ordinary syntax error. The allow-list is 26 prefixes, fixed in the release:

`org.openiam`, `org.apache.commons.logging`, `org.apache.commons.collections4`, `org.apache.commons.codec`, `org.apache.commons.lang3`, `org.apache.commons.httpclient`, `org.apache.hc`, `org.apache.http`, `org.bouncycastle.cert`, `jakarta.servlet`, `org.opensaml`, `org.joda.time`, `groovy.json`, `org.codehaus`, `javax.imageio`, `java.text`, `java.math`, `java.util`, `java.security.cert`, `org.springframework.transaction`, `java.io.ByteArrayInputStream`, `com.fasterxml`, `java.nio.charset`, `java.lang.Class`, `groovy.lang.Closure`, `org.slf4j` — plus individually allowed classes including `java.lang.Integer`, `String`, `Math`, `StringBuilder`, `Object`, `java.net.URL`, `java.net.URI`, `javax.crypto.Mac`, `org.springframework.context.ApplicationContext` and every `java.lang` primitive, `*Exception` and `*Error`. `org.openiam.util.SpringSecurityHelper` is explicitly **blacklisted** and cannot be called from a script.

To extend it:

| Property                                     | Type                             | Shipped default |
| -------------------------------------------- | -------------------------------- | --------------- |
| `org.openiam.groovy.extra.whitelist.package` | comma-separated package prefixes | **empty**       |

That property is **not** a database system property: it does not appear on **Administration → System Configuration**, you cannot change it from the Webconsole, and you cannot read its current value there either. It is supplied to each service by the deployment, so both setting it and finding out what it is set to are questions for the deployment owner — see the **Installation and Tuning Guide**. Ask for the value before you conclude a package is not allow-listed.

Two things setting it does **not** do, and both are the reason a sandbox change often appears not to work:

* **It does not install the library.** The library must also be available to every process that executes the script. Groovy Manager, the ESB, the synchronization service, the reconciliation service, the batch-task service and a remote connector are separate processes, and one having the library says nothing about another.
* **It is read once, at service start.** A change takes effect only after every affected service restarts — which puts applying it outside this guide as well.

Whitelist the narrowest prefix that clears compilation, never a vendor root, and record which script versions and which processes consume the library. A whitelisted package with a missing JAR compiles and then fails at run time with `ClassNotFoundException` in the caller process — which looks nothing like a whitelist problem.

## The sandbox on the execution path

**As of 2026.8.1**, the allow-list is applied wherever a script is compiled, not only where one is saved. Scripts invoked from storage through the execution path — reconciliation search queries and Groovy batch tasks among them — are compiled against the same allow-list the editor uses. Before this, that path compiled without it, so a stored script could do more at run time than the editor would have accepted at save time.

It is the **script-body evaluation** route that changed — the one the search query and Groovy batch scripts use. Scripts that OpenIAM parses into a class were already compiled through the sandboxed class loader, so they were checked at run time before this. The gap mattered most for a script seeded straight into `conf/iamscripts/` and run over that route, because such a script is never saved through Groovy Manager and so was never compiled against the allow-list at any point.

### Binding variables are `Object`-typed

The values a caller passes in — `param1`–`param4` and `taskObj` in a batch task, `searchFilter` in a reconciliation search query — are undeclared by definition, so the sandbox accepts them as dynamic rather than failing the compile outright. They stay `Object`-typed, and any method called on one still has to clear the allow-list. That means an untyped operation on a binding variable no longer compiles:

{% code overflow="wrap" %}

```groovy
// does not compile: no Object#plus
return param1 + param2
```

{% endcode %}

Cast first, and it does:

{% code overflow="wrap" %}

```groovy
return (param1 as Integer) + (param2 as Integer)
```

{% endcode %}

Passing a binding variable straight through — testing it, defaulting it, or casting it — needs no change, which is the shape the shipped search-query scripts already use:

{% code overflow="wrap" %}

```groovy
def sf = (binding.hasVariable("searchFilter") ? searchFilter : null) ?: "(&(objectclass=person)(cn=*))"
output = sf as String
```

{% endcode %}

{% hint style="warning" %}
Run [**Compile All Groovy Scripts**](/administration-guide/automation/manage-groovy-scripts.md#compile-the-whole-catalog) after upgrading. A custom script that relied on untyped arithmetic against a binding variable compiled before the upgrade and will not now, and the catalogue compile is what finds it before a scheduled run does.
{% endhint %}

## Related

* [Create a Groovy script](/administration-guide/automation/manage-groovy-scripts.md) — saving, branching, compiling, testing, promoting and rolling back
* [Shipped Groovy scripts](/administration-guide/reference/shipped-groovy-scripts.md) — all 745, by extension point
* [Synchronization script contracts](/administration-guide/reference/synchronization-script-contracts.md) — the contracts for the `sync/` family
* [Provisioning processor reference](/administration-guide/lifecycle/provisioning-processors/provisioning-processor-reference.md) — the contracts for the `provision/` family
* [Installation Guide](/installation/installation.md) — which schemas a deployment backs up, the script root, restarting services after a sandbox change

*Applies to OpenIAM 2026.8.1. Last reviewed 28 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/reference/groovy-script-reference.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.
