SailPoint BeanShell and custom connectors: the production developer’s purgatory
The real-world pain of BeanShell rules and custom connectors in SailPoint IdentityIQ: null traps, silent failures, BuildMap errors, and what changes when you move to ISC.
- SailPoint
- Security
- Operations
- Architecture
Every SailPoint IdentityIQ developer has a BeanShell story. The one where a single missing semicolon took down a certification campaign. The one where a provisioning rule silently returned null and granted access to everyone. The one where debugging meant adding System.out.println to a rule, redeploying, and praying.
BeanShell is the dark matter of IIQ. It is everywhere, poorly understood, and when it breaks, it breaks in ways that are hard to reproduce and even harder to explain to a manager who just wants to know why the quarterly attestation is stuck.
This article covers the BeanShell patterns that actually work in production, the traps I have fallen into (more than once), and how to write rules that do not come back to haunt you.
What BeanShell actually is in IIQ
BeanShell is a lightweight Java scripting language that runs inside the IIQ JVM. It has access to all Java classes and the full SailPoint object model: Identity, Link, Application, ProvisioningPlan, and so on.
The problem is that BeanShell is not Java. It is interpreted, dynamically typed, and has no compiler to catch your mistakes. A rule that works in your IDE can fail at runtime because of a subtle type coercion issue or a missing import that only manifests under certain data conditions.
Variable scoping. Variables declared inside a for loop are visible outside it. Variables declared inside an if block might not be. The rules are not what a Java developer expects.
Null handling. null.equals(anything) throws. "string".equals(null) returns false. The difference has caused more production incidents than I can count.
Type coercion. String + int concatenates. int + String also concatenates. But List.add(int) might autobox to Integer or might fail depending on the context.
Import resolution. BeanShell resolves imports lazily. A typo in an import statement does not fail at parse time — it fails when that line executes, possibly hours or days later in a certification campaign.
Rule patterns that survive production
Prefix everything with the package. Do not rely on imports. Write sailpoint.object.Identity instead of importing sailpoint.object.*. It is verbose but it never fails due to import resolution issues.
Validate inputs at the top of every rule. Every rule that receives a context, an identity, or a connection should validate it immediately. if (context == null) return null; is not defensive programming — it is survival.
Log everything in production rules. Add System.out.println or LogFactory.getLog() calls at every decision point. When the rule misbehaves at 3 AM, those log lines are all you have.
Wrap everything in try-catch. An unhandled exception in a provisioning rule can abort the entire batch. Wrap the body in try { ... } catch (Exception e) { log.error(e); return null; }
Test with real data. BeanShell rules behave differently with null fields, empty collections, and multi-valued attributes. Build a test harness that feeds real IIQ data through your rule before deploying to production.
Custom connector hell
The BuildMap rule. Every connector has a required BuildMap rule that returns a Map of attributes. The error message when it fails is famously unhelpful: Build Rule must return a Map. This is the most-asked question on Stack Overflow’s SailPoint tag. The cause is almost always a null value in the map or a type mismatch.
The ConnectorException black hole. When a connector throws ConnectorException, the error message is often the raw exception from the target system wrapped in IIQ’s error handling. The real error is buried three stack frames deep. Enable DEBUG logging for sailpoint.connector before calling support.
The schema mismatch. IIQ caches connector schemas aggressively. If you change the schema on the target system, IIQ may still use the old cached version. Clearing the application cache and re-running aggregation is the first thing to try when attributes go missing.
What changes in ISC
ISC does not use BeanShell. It uses a simplified rule engine with a fraction of the surface area. This is simultaneously the best and worst thing about the migration.
The good: ISC rules cannot accidentally take down the entire platform. They are sandboxed, time-boxed, and cannot access arbitrary Java classes.
The bad: anything complex requires a workaround. Multi-step validations, dynamic attribute computation, and complex correlation logic may need to be split across multiple rules, workflow steps, or external services.
The ugly: migrating BeanShell rules to ISC is manual. There is no automated converter. Every rule must be reviewed, redesigned, and re-implemented.
BeanShell in IIQ is a power tool. It can do almost anything, and it can break almost anything. The key to surviving it is discipline: validate everything, log everything, and never assume a rule works just because it did not throw an exception in your test environment.
Was this useful?