<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>IdentityIQ on IAMDevBox</title><link>https://www.iamdevbox.com/tags/identityiq/</link><description>Recent content in IdentityIQ on IAMDevBox</description><image><title>IAMDevBox</title><url>https://www.iamdevbox.com/IAMDevBox.com.jpg</url><link>https://www.iamdevbox.com/IAMDevBox.com.jpg</link></image><generator>Hugo -- 0.146.0</generator><language>en-us</language><lastBuildDate>Thu, 20 Aug 2026 22:18:49 -0400</lastBuildDate><atom:link href="https://www.iamdevbox.com/tags/identityiq/index.xml" rel="self" type="application/rss+xml"/><item><title>Java, MySQL, and Shell Scripting for SailPoint IdentityIQ</title><link>https://www.iamdevbox.com/posts/sailpoint-identityiq-java-mysql-shell-scripting-guide/</link><pubDate>Thu, 20 Aug 2026 14:00:00 +0000</pubDate><guid>https://www.iamdevbox.com/posts/sailpoint-identityiq-java-mysql-shell-scripting-guide/</guid><description>SailPoint IdentityIQ Java, MySQL, and shell scripting: custom JAR deployment, the spt_ schema, safe diagnostic SQL, and iiq console automation scripts.</description><content:encoded><![CDATA[<p>SailPoint IdentityIQ is a Java web application running on an application server against a relational database. Most IdentityIQ development happens in BeanShell rules and XML workflows — covered in the companion guide to <a href="/posts/sailpoint-identityiq-beanshell-rules-workflows-tasks-developer-guide/">IdentityIQ BeanShell rules, workflows, and tasks</a>. This article covers the layer underneath: when to write compiled Java instead of BeanShell, how the MySQL schema is actually laid out, and the shell scripting that turns manual console work into repeatable automation.</p>
<h2 id="the-stack-concretely">The Stack, Concretely</h2>
<p>An IdentityIQ deployment is four layers, and knowing which one a problem lives in cuts debugging time dramatically:</p>
<table>
  <thead>
      <tr>
          <th>Layer</th>
          <th>What lives there</th>
          <th>Where to look</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Application server</td>
          <td>Tomcat/WebSphere/WebLogic, JVM heap, threads</td>
          <td><code>catalina.out</code>, thread dumps</td>
      </tr>
      <tr>
          <td>IdentityIQ WAR</td>
          <td>Your JARs, rules, config</td>
          <td><code>IdentityIQ_HOME/WEB-INF/</code></td>
      </tr>
      <tr>
          <td>Database</td>
          <td>All objects, most as XML blobs</td>
          <td><code>spt_</code> tables</td>
      </tr>
      <tr>
          <td>Target systems</td>
          <td>AD, LDAP, HR feeds, apps</td>
          <td>Connector logs</td>
      </tr>
  </tbody>
</table>
<p><code>IdentityIQ_HOME</code> is wherever <code>identityiq.war</code> was expanded — typically <code>$TOMCAT_HOME/webapps/identityiq</code>. Nearly every path in this article is relative to it.</p>
<h2 id="java-when-to-leave-beanshell">Java: When to Leave BeanShell</h2>
<p>BeanShell is convenient for short scripts, but it is interpreted, untyped, and untestable. Move to compiled Java when any of these apply:</p>
<ul>
<li>The logic exceeds roughly 50 lines</li>
<li>You need unit tests</li>
<li>It runs in a hot path — per-account during aggregation, for instance</li>
<li>You need a library BeanShell struggles to use cleanly</li>
<li>You are writing a custom connector or task executor</li>
</ul>
<h3 id="compiling-against-the-identityiq-api">Compiling Against the IdentityIQ API</h3>
<p>Your code compiles against <code>identityiq.jar</code>, found in <code>IdentityIQ_HOME/WEB-INF/lib/</code>. A minimal Maven setup installs it into your local repository, since SailPoint does not publish to Maven Central:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn install:install-file <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -Dfile<span style="color:#f92672">=</span>/opt/tomcat/webapps/identityiq/WEB-INF/lib/identityiq.jar <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -DgroupId<span style="color:#f92672">=</span>sailpoint <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -DartifactId<span style="color:#f92672">=</span>identityiq <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -Dversion<span style="color:#f92672">=</span>8.4 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -Dpackaging<span style="color:#f92672">=</span>jar
</span></span></code></pre></div><p>Then declare it as <code>provided</code> — it must not be bundled into your artifact, because the container already has it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;groupId&gt;</span>sailpoint<span style="color:#f92672">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;artifactId&gt;</span>identityiq<span style="color:#f92672">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;version&gt;</span>8.4<span style="color:#f92672">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;scope&gt;</span>provided<span style="color:#f92672">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Marking it <code>compile</code> instead ships a second copy of every SailPoint class inside your JAR, producing <code>ClassCastException</code> errors where the same class loaded by two classloaders is not considered equal. This is one of the harder IdentityIQ bugs to diagnose, because the exception message names the same class on both sides.</p>
<h3 id="java-version-compatibility">Java Version Compatibility</h3>
<p>IdentityIQ 8.x supports Java 8 and 11, with 8.4 adding Java 17 on supported application servers. Compile targeting the version your application server actually runs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>java -version                       <span style="color:#75715e"># on the app server host</span>
</span></span><span style="display:flex;"><span>mvn -DskipTests package             <span style="color:#75715e"># with maven.compiler.release matching</span>
</span></span></code></pre></div><p>A mismatch produces <code>UnsupportedClassVersionError</code> at class load time — not at deployment — so the failure shows up the first time your code is invoked, often long after the deploy appeared to succeed.</p>
<h3 id="deploying-a-custom-jar">Deploying a Custom JAR</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl stop tomcat
</span></span><span style="display:flex;"><span>sudo cp target/iiq-custom-1.0.0.jar /opt/tomcat/webapps/identityiq/WEB-INF/lib/
</span></span><span style="display:flex;"><span>sudo chown tomcat:tomcat /opt/tomcat/webapps/identityiq/WEB-INF/lib/iiq-custom-1.0.0.jar
</span></span><span style="display:flex;"><span>sudo systemctl start tomcat
</span></span></code></pre></div><p>A restart is mandatory — the JVM does not reload classes from <code>WEB-INF/lib</code> at runtime. This is the key operational difference from rules, which are database objects you can update live.</p>
<p><strong>Remove the old version explicitly</strong> when deploying an update with a changed filename. Two JARs both containing <code>com.example.iiq.MyRule</code> produce nondeterministic behaviour depending on classloader ordering.</p>
<h2 id="mysql-the-identityiq-schema">MySQL: The IdentityIQ Schema</h2>
<h3 id="generating-and-loading-the-schema">Generating and Loading the Schema</h3>
<p>IdentityIQ generates its own DDL. From <code>IdentityIQ_HOME/WEB-INF/bin</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./iiq schema
</span></span></code></pre></div><p>This writes versioned scripts into <code>IdentityIQ_HOME/WEB-INF/database</code>, named like <code>create_identityiq_tables-8.4.mysql</code>. Load the one matching your database platform:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mysql -u root -p identityiq &lt; create_identityiq_tables-8.4.mysql
</span></span></code></pre></div><p>Expect this to take anywhere from 45 minutes to 2 hours. Re-run <code>./iiq schema</code> after adding extended attributes — they become real columns, and the generated DDL changes.</p>
<h3 id="connection-settings">Connection Settings</h3>
<p>Connection configuration lives in <code>IdentityIQ_HOME/WEB-INF/classes/iiq.properties</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#a6e22e">dataSource.url</span><span style="color:#f92672">=</span><span style="color:#e6db74">jdbc:mysql://db.example.com:3306/identityiq?useUnicode=true&amp;characterEncoding=utf8</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">dataSource.username</span><span style="color:#f92672">=</span><span style="color:#e6db74">identityiq</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">dataSource.password</span><span style="color:#f92672">=</span><span style="color:#e6db74">&lt;encrypted-value&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">dataSource.maxActive</span><span style="color:#f92672">=</span><span style="color:#e6db74">50</span>
</span></span></code></pre></div><p>The password must be encrypted. Generate the ciphertext with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./iiq encrypt changeit
</span></span></code></pre></div><p>Paste the output into <code>iiq.properties</code>. IdentityIQ will not accept a plaintext password here.</p>
<h3 id="the-tables-you-will-actually-query">The Tables You Will Actually Query</h3>
<p>Every table carries the <code>spt_</code> prefix. These are the ones worth knowing:</p>
<table>
  <thead>
      <tr>
          <th>Table</th>
          <th>Contents</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>spt_identity</code></td>
          <td>The identity cube — one row per person</td>
      </tr>
      <tr>
          <td><code>spt_link</code></td>
          <td>Accounts on target systems, linked to identities</td>
      </tr>
      <tr>
          <td><code>spt_application</code></td>
          <td>Connector configurations</td>
      </tr>
      <tr>
          <td><code>spt_bundle</code></td>
          <td>Roles</td>
      </tr>
      <tr>
          <td><code>spt_identity_entitlement</code></td>
          <td>Who currently holds which entitlement</td>
      </tr>
      <tr>
          <td><code>spt_task_result</code></td>
          <td>Task execution history and results</td>
      </tr>
      <tr>
          <td><code>spt_workflow_case</code></td>
          <td>In-flight workflow state</td>
      </tr>
      <tr>
          <td><code>spt_work_item</code></td>
          <td>Pending approvals and manual actions</td>
      </tr>
      <tr>
          <td><code>spt_syslog_event</code></td>
          <td>System errors and warnings</td>
      </tr>
      <tr>
          <td><code>spt_audit_event</code></td>
          <td>Audit trail</td>
      </tr>
  </tbody>
</table>
<h3 id="read-only-diagnostics-that-save-real-time">Read-Only Diagnostics That Save Real Time</h3>
<p>These queries answer questions the UI makes tedious. All are <code>SELECT</code> only.</p>
<p><strong>Which aggregations are failing, and how recently:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> name,
</span></span><span style="display:flex;"><span>       FROM_UNIXTIME(completed<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>) <span style="color:#66d9ef">AS</span> completed_at,
</span></span><span style="display:flex;"><span>       completion_status,
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">SUBSTRING</span>(messages, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">200</span>)   <span style="color:#66d9ef">AS</span> first_message
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span>   spt_task_result
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span>  completion_status <span style="color:#66d9ef">IN</span> (<span style="color:#e6db74">&#39;Error&#39;</span>, <span style="color:#e6db74">&#39;Warning&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span>  <span style="color:#66d9ef">BY</span> completed <span style="color:#66d9ef">DESC</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LIMIT</span>  <span style="color:#ae81ff">20</span>;
</span></span></code></pre></div><p><strong>Accounts that failed to correlate</strong> — the usual cause of &ldquo;the user exists but has no access&rdquo;:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> a.name        <span style="color:#66d9ef">AS</span> application,
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">COUNT</span>(<span style="color:#f92672">*</span>)      <span style="color:#66d9ef">AS</span> uncorrelated_accounts
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span>   spt_link l
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">JOIN</span>   spt_application a <span style="color:#66d9ef">ON</span> l.application <span style="color:#f92672">=</span> a.id
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span>  l.identity_id <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">GROUP</span>  <span style="color:#66d9ef">BY</span> a.name
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span>  <span style="color:#66d9ef">BY</span> uncorrelated_accounts <span style="color:#66d9ef">DESC</span>;
</span></span></code></pre></div><p><strong>Workflows stuck in flight</strong>, which accumulate invisibly and eventually degrade performance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> name,
</span></span><span style="display:flex;"><span>       FROM_UNIXTIME(created<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>) <span style="color:#66d9ef">AS</span> created_at,
</span></span><span style="display:flex;"><span>       DATEDIFF(NOW(), FROM_UNIXTIME(created<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>)) <span style="color:#66d9ef">AS</span> age_days
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span>   spt_workflow_case
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span>  DATEDIFF(NOW(), FROM_UNIXTIME(created<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>)) <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">30</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span>  <span style="color:#66d9ef">BY</span> created <span style="color:#66d9ef">ASC</span>;
</span></span></code></pre></div><p><strong>Table sizes</strong>, to find what is actually consuming disk:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#66d9ef">table_name</span>,
</span></span><span style="display:flex;"><span>       ROUND(((data_length <span style="color:#f92672">+</span> index_length) <span style="color:#f92672">/</span> <span style="color:#ae81ff">1024</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">1024</span>), <span style="color:#ae81ff">1</span>) <span style="color:#66d9ef">AS</span> size_mb,
</span></span><span style="display:flex;"><span>       table_rows
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span>   information_schema.TABLES
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span>  table_schema <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;identityiq&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span>  <span style="color:#66d9ef">BY</span> (data_length <span style="color:#f92672">+</span> index_length) <span style="color:#66d9ef">DESC</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LIMIT</span>  <span style="color:#ae81ff">15</span>;
</span></span></code></pre></div><p>Note that IdentityIQ stores timestamps as Unix epoch <strong>milliseconds</strong> in <code>BIGINT</code> columns, which is why every date needs <code>FROM_UNIXTIME(col/1000)</code>.</p>
<h3 id="never-write-directly-to-the-database">Never Write Directly to the Database</h3>
<p>This deserves emphasis because it is the most damaging mistake available to someone comfortable with SQL.</p>
<p>Most IdentityIQ objects serialize their real content into an <strong>XML blob column</strong>. The relational columns beside it are a partial, denormalized projection maintained by the application for querying. An <code>UPDATE</code> that changes a column leaves the XML blob untouched, so the object now disagrees with itself — and the XML wins the next time the object loads.</p>
<p>Compounding this, Hibernate caches objects in memory. A direct SQL change to a cached object is silently overwritten the next time the application saves it.</p>
<p>Use the iiq console, the API, or a task. <code>SELECT</code> freely; never <code>UPDATE</code>, <code>INSERT</code>, or <code>DELETE</code>.</p>
<h3 id="keeping-the-database-from-growing-forever">Keeping the Database from Growing Forever</h3>
<p>Four tables grow without bound if left alone: <code>spt_task_result</code>, <code>spt_syslog_event</code>, <code>spt_audit_event</code>, and <code>spt_provisioning_transaction</code>. On a busy deployment <code>spt_syslog_event</code> can reach tens of millions of rows, at which point ordinary queries slow noticeably.</p>
<p>Schedule the built-in <strong>Perform Maintenance</strong> task and configure retention in System Setup. Verify it is working:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#66d9ef">COUNT</span>(<span style="color:#f92672">*</span>)                          <span style="color:#66d9ef">AS</span> total,
</span></span><span style="display:flex;"><span>       FROM_UNIXTIME(<span style="color:#66d9ef">MIN</span>(created)<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>)  <span style="color:#66d9ef">AS</span> oldest,
</span></span><span style="display:flex;"><span>       FROM_UNIXTIME(<span style="color:#66d9ef">MAX</span>(created)<span style="color:#f92672">/</span><span style="color:#ae81ff">1000</span>)  <span style="color:#66d9ef">AS</span> newest
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span>   spt_syslog_event;
</span></span></code></pre></div><p>If <code>oldest</code> predates your retention window, purging is not running.</p>
<h3 id="mysql-settings-that-matter">MySQL Settings That Matter</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#66d9ef">[mysqld]</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">innodb_buffer_pool_size</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">8G        # size to available RAM</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">max_allowed_packet</span>      <span style="color:#f92672">=</span> <span style="color:#e6db74">64M       # large XML blobs exceed the 4M default</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">innodb_log_file_size</span>    <span style="color:#f92672">=</span> <span style="color:#e6db74">512M</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">character_set_server</span>    <span style="color:#f92672">=</span> <span style="color:#e6db74">utf8mb4</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">transaction_isolation</span>   <span style="color:#f92672">=</span> <span style="color:#e6db74">READ-COMMITTED</span>
</span></span></code></pre></div><p><code>max_allowed_packet</code> is the one that bites first. IdentityIQ writes large XML blobs, and the default rejects them with <code>Packet for query is too large</code>, usually during aggregation of a large application.</p>
<h2 id="shell-scripting-automating-the-console">Shell Scripting: Automating the Console</h2>
<p>The <code>iiq console</code> reads from stdin, which makes it scriptable. This is the foundation for backup, deployment, and health-check automation.</p>
<h3 id="exporting-objects-for-version-control">Exporting Objects for Version Control</h3>
<p>IdentityIQ objects live in the database and are therefore invisible to Git. A database refresh destroys uncommitted customization. This script exports them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>set -euo pipefail
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>IIQ_HOME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>IIQ_HOME<span style="color:#66d9ef">:-</span>/opt/tomcat/webapps/identityiq<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>IIQ_BIN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$IIQ_HOME<span style="color:#e6db74">/WEB-INF/bin&#34;</span>
</span></span><span style="display:flex;"><span>EXPORT_DIR<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>1<span style="color:#66d9ef">:-</span>./iiq-export<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>mkdir -p <span style="color:#e6db74">&#34;</span>$EXPORT_DIR<span style="color:#e6db74">&#34;</span>/<span style="color:#f92672">{</span>rules,workflows,tasks,applications<span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>export_class<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  local cls<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span> dest<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$2<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;Exporting </span>$cls<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;</span>$IIQ_BIN<span style="color:#e6db74">/iiq&#34;</span> console <span style="color:#e6db74">&lt;&lt;CONSOLE | grep -v &#39;^&gt;&#39; &gt; &#34;$dest/_list.txt&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">list $cls
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">CONSOLE</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">while</span> IFS<span style="color:#f92672">=</span> read -r name; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">[[</span> -z <span style="color:#e6db74">&#34;</span>$name<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]]</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>    local safe<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>name//[^a-zA-Z0-9._-]/_<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;</span>$IIQ_BIN<span style="color:#e6db74">/iiq&#34;</span> console <span style="color:#e6db74">&lt;&lt;CONSOLE &gt;/dev/null
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">checkout $cls &#34;$name&#34; $dest/$safe.xml
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">CONSOLE</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">done</span> &lt; <span style="color:#e6db74">&#34;</span>$dest<span style="color:#e6db74">/_list.txt&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>export_class Rule         <span style="color:#e6db74">&#34;</span>$EXPORT_DIR<span style="color:#e6db74">/rules&#34;</span>
</span></span><span style="display:flex;"><span>export_class Workflow     <span style="color:#e6db74">&#34;</span>$EXPORT_DIR<span style="color:#e6db74">/workflows&#34;</span>
</span></span><span style="display:flex;"><span>export_class TaskDefinition <span style="color:#e6db74">&#34;</span>$EXPORT_DIR<span style="color:#e6db74">/tasks&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Exported to </span>$EXPORT_DIR<span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><p>Run it on a schedule, commit the output, and a database refresh becomes recoverable.</p>
<h3 id="deploying-xml-with-validation">Deploying XML with Validation</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>set -euo pipefail
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>IIQ_BIN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>IIQ_HOME<span style="color:#66d9ef">:-</span>/opt/tomcat/webapps/identityiq<span style="color:#e6db74">}</span><span style="color:#e6db74">/WEB-INF/bin&#34;</span>
</span></span><span style="display:flex;"><span>TARGET<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> ! -e <span style="color:#e6db74">&#34;</span>$TARGET<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;ERROR: </span>$TARGET<span style="color:#e6db74"> not found&#34;</span> &gt;&amp;<span style="color:#ae81ff">2</span>
</span></span><span style="display:flex;"><span>  exit <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Fail before touching IdentityIQ if the XML is malformed</span>
</span></span><span style="display:flex;"><span>find <span style="color:#e6db74">&#34;</span>$TARGET<span style="color:#e6db74">&#34;</span> -name <span style="color:#e6db74">&#39;*.xml&#39;</span> -print0 | <span style="color:#66d9ef">while</span> IFS<span style="color:#f92672">=</span> read -r -d <span style="color:#e6db74">&#39;&#39;</span> f; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>  xmllint --noout <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">||</span> <span style="color:#f92672">{</span> echo <span style="color:#e6db74">&#34;ERROR: invalid XML in </span>$f<span style="color:#e6db74">&#34;</span> &gt;&amp;2; exit 1; <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> f in <span style="color:#66d9ef">$(</span>find <span style="color:#e6db74">&#34;</span>$TARGET<span style="color:#e6db74">&#34;</span> -name <span style="color:#e6db74">&#39;*.xml&#39;</span> | sort<span style="color:#66d9ef">)</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;Importing </span><span style="color:#66d9ef">$(</span>basename <span style="color:#e6db74">&#34;</span>$f<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;</span>$IIQ_BIN<span style="color:#e6db74">/iiq&#34;</span> console <span style="color:#e6db74">&lt;&lt;CONSOLE
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">import $f
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">CONSOLE</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>The <code>xmllint</code> pre-check matters because <code>import</code> on malformed XML can partially apply, leaving the environment in a state neither matching the old nor the new definition.</p>
<h3 id="a-health-check-worth-cron-ing">A Health Check Worth Cron-ing</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e"># Report IdentityIQ health; exit non-zero if anything is wrong.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DB_USER<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>DB_USER<span style="color:#66d9ef">:-</span>identityiq<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>DB_NAME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>DB_NAME<span style="color:#66d9ef">:-</span>identityiq<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>CATALINA_OUT<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>CATALINA_OUT<span style="color:#66d9ef">:-</span>/opt/tomcat/logs/catalina.out<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>status<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>q<span style="color:#f92672">()</span> <span style="color:#f92672">{</span> mysql -u <span style="color:#e6db74">&#34;</span>$DB_USER<span style="color:#e6db74">&#34;</span> -p<span style="color:#e6db74">&#34;</span>$DB_PASS<span style="color:#e6db74">&#34;</span> -N -B -e <span style="color:#e6db74">&#34;</span>$1<span style="color:#e6db74">&#34;</span> <span style="color:#e6db74">&#34;</span>$DB_NAME<span style="color:#e6db74">&#34;</span> 2&gt;/dev/null; <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>failed<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>q <span style="color:#e6db74">&#34;SELECT COUNT(*) FROM spt_task_result
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            WHERE completion_status=&#39;Error&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">              AND completed &gt; (UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY) * 1000);&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>failed<span style="color:#66d9ef">:-</span>0<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> -gt <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;WARN: </span>$failed<span style="color:#e6db74"> task(s) failed in the last 24h&#34;</span>
</span></span><span style="display:flex;"><span>  status<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>stuck<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>q <span style="color:#e6db74">&#34;SELECT COUNT(*) FROM spt_workflow_case
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">           WHERE created &lt; (UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY) * 1000);&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>stuck<span style="color:#66d9ef">:-</span>0<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> -gt <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;WARN: </span>$stuck<span style="color:#e6db74"> workflow case(s) older than 30 days&#34;</span>
</span></span><span style="display:flex;"><span>  status<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> -f <span style="color:#e6db74">&#34;</span>$CATALINA_OUT<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>  oom<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>grep -c <span style="color:#e6db74">&#39;OutOfMemoryError&#39;</span> <span style="color:#e6db74">&#34;</span>$CATALINA_OUT<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">||</span> true<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">[[</span> <span style="color:#e6db74">&#34;</span>$oom<span style="color:#e6db74">&#34;</span> -gt <span style="color:#ae81ff">0</span> <span style="color:#f92672">]]</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">{</span> echo <span style="color:#e6db74">&#34;CRITICAL: </span>$oom<span style="color:#e6db74"> OutOfMemoryError in catalina.out&#34;</span>; status<span style="color:#f92672">=</span>2; <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>exit $status
</span></span></code></pre></div><p>Note <code>-p&quot;$DB_PASS&quot;</code> reads from the environment rather than hardcoding a credential, and <code>-N -B</code> strips headers and formatting so the output parses cleanly.</p>
<h3 id="watching-aggregation-in-real-time">Watching Aggregation in Real Time</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Follow only connector activity during an aggregation run</span>
</span></span><span style="display:flex;"><span>tail -f /opt/tomcat/logs/catalina.out | grep --line-buffered -E <span style="color:#e6db74">&#39;sailpoint.connector|Aggregation&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Count errors by type from today&#39;s log</span>
</span></span><span style="display:flex;"><span>grep <span style="color:#e6db74">&#39;ERROR&#39;</span> /opt/tomcat/logs/catalina.out <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | sed -E <span style="color:#e6db74">&#39;s/.*ERROR[[:space:]]+//&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | cut -d<span style="color:#e6db74">&#39; &#39;</span> -f1 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | sort | uniq -c | sort -rn | head -20
</span></span></code></pre></div><p><code>--line-buffered</code> on <code>grep</code> is what makes the first command actually stream; without it grep buffers output and the tail appears frozen.</p>
<h2 id="putting-it-together">Putting It Together</h2>
<p>A sound IdentityIQ deployment pipeline uses all three layers: shell scripts export objects from development into Git, compiled Java holds logic too complex for BeanShell, and MySQL is queried read-only for diagnostics while all writes go through the IdentityIQ API.</p>
<p>The governance logic you build on top of this — rules, workflows, and tasks — is covered in the companion guide to <a href="/posts/sailpoint-identityiq-beanshell-rules-workflows-tasks-developer-guide/">IdentityIQ BeanShell rules, workflows, and tasks</a>. For how IdentityIQ compares to other platforms in this space, see our <a href="/posts/iam-tools-comparison-complete-guide-to-identity-platforms/">IAM tools comparison</a>.</p>
]]></content:encoded></item><item><title>SailPoint IdentityIQ BeanShell Rules, Workflows, and Tasks: A Developer's Guide</title><link>https://www.iamdevbox.com/posts/sailpoint-identityiq-beanshell-rules-workflows-tasks-developer-guide/</link><pubDate>Thu, 20 Aug 2026 10:00:00 +0000</pubDate><guid>https://www.iamdevbox.com/posts/sailpoint-identityiq-beanshell-rules-workflows-tasks-developer-guide/</guid><description>SailPoint IdentityIQ BeanShell rules, workflows, and tasks explained: rule types, SailPointContext API, workflow approvals, task executors, and iiq console.</description><content:encoded><![CDATA[<p>SailPoint IdentityIQ ships with three extension points where you write code: <strong>rules</strong> (BeanShell scripts that compute a value), <strong>workflows</strong> (XML state machines that orchestrate multi-step processes), and <strong>tasks</strong> (scheduled jobs that operate on data in bulk). Almost every IdentityIQ customization you will ever build fits into one of those three. This guide covers what each one is for, the API you use inside them, and the failure modes that cost new IdentityIQ developers the most time.</p>
<p>If you are coming from a different IAM platform, the closest analogue is scripted customization in ForgeRock — see our <a href="/posts/forgerock-am-script-customization-a-practical-guide/">ForgeRock AM script customization guide</a> for a comparison of how the two platforms approach the same problem.</p>
<h2 id="choosing-the-right-extension-point">Choosing the Right Extension Point</h2>
<p>Before writing anything, pick the correct mechanism. Choosing wrong is the most expensive mistake in IdentityIQ development, because migrating logic from a rule to a workflow later means rewriting it entirely.</p>
<table>
  <thead>
      <tr>
          <th>You need to&hellip;</th>
          <th>Use</th>
          <th>Runs</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Transform an attribute value</td>
          <td>Rule</td>
          <td>Synchronously, in-process</td>
      </tr>
      <tr>
          <td>Match an account to an identity</td>
          <td>Correlation Rule</td>
          <td>During aggregation</td>
      </tr>
      <tr>
          <td>Decide who approves a request</td>
          <td>Workflow</td>
          <td>Asynchronously, may pause for days</td>
      </tr>
      <tr>
          <td>Process every identity in bulk</td>
          <td>Task</td>
          <td>On a schedule</td>
      </tr>
      <tr>
          <td>Modify data on its way to a target system</td>
          <td>Provisioning Rule</td>
          <td>During provisioning</td>
      </tr>
  </tbody>
</table>
<p>The dividing line between a rule and a workflow is <strong>whether the logic can pause</strong>. A rule runs start to finish in a single thread and returns one value. If your logic needs to wait for a human, it must be a workflow.</p>
<h2 id="beanshell-the-language-identityiq-actually-runs">BeanShell: The Language IdentityIQ Actually Runs</h2>
<p>IdentityIQ rules are written in BeanShell, a scripting language that interprets Java syntax at runtime. This is the single most important thing to understand about IdentityIQ development, because BeanShell&rsquo;s differences from Java cause the majority of production rule failures.</p>
<h3 id="what-beanshell-does-not-support">What BeanShell Does Not Support</h3>
<p>BeanShell implements Java syntax as of roughly Java 1.4. The following will fail:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">// GENERICS — not supported. This throws a parse error.</span>
</span></span><span style="display:flex;"><span>List<span style="color:#f92672">&lt;</span>String<span style="color:#f92672">&gt;</span> names <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList<span style="color:#f92672">&lt;</span>String<span style="color:#f92672">&gt;</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Correct: use raw types</span>
</span></span><span style="display:flex;"><span>List names <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// LAMBDAS and streams — not supported</span>
</span></span><span style="display:flex;"><span>names.<span style="color:#a6e22e">stream</span>().<span style="color:#a6e22e">filter</span>(n <span style="color:#f92672">-&gt;</span> n.<span style="color:#a6e22e">startsWith</span>(<span style="color:#e6db74">&#34;a&#34;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Correct: use an explicit loop</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> 0; i <span style="color:#f92672">&lt;</span> names.<span style="color:#a6e22e">size</span>(); i<span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>    String n <span style="color:#f92672">=</span> (String) names.<span style="color:#a6e22e">get</span>(i);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (n.<span style="color:#a6e22e">startsWith</span>(<span style="color:#e6db74">&#34;a&#34;</span>)) { <span style="color:#75715e">/* ... */</span> }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ANNOTATIONS — not supported</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Override</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">toString</span>() { }
</span></span></code></pre></div><h3 id="loose-typing-hides-bugs-until-runtime">Loose Typing Hides Bugs Until Runtime</h3>
<p>BeanShell lets you declare variables without a type. This is convenient and dangerous:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">// Both are legal in BeanShell</span>
</span></span><span style="display:flex;"><span>String name <span style="color:#f92672">=</span> identity.<span style="color:#a6e22e">getName</span>();
</span></span><span style="display:flex;"><span>name <span style="color:#f92672">=</span> identity.<span style="color:#a6e22e">getName</span>();
</span></span></code></pre></div><p>Because the script is interpreted, a misspelled method name compiles fine and fails only when that specific branch executes. A rule that works in your test case can fail six months later the first time an identity hits an untested code path. Two defenses matter:</p>
<ol>
<li><strong>Always declare types explicitly.</strong> It does not make BeanShell check them at parse time, but it documents intent and catches cast errors sooner.</li>
<li><strong>Validate rules before deploying.</strong> The iiq console has a syntax checker — see the console section below.</li>
</ol>
<h3 id="null-safety-is-entirely-your-job">Null Safety Is Entirely Your Job</h3>
<p>IdentityIQ getters return <code>null</code> constantly. An identity may have no manager, a link may have no attribute, an application may not be assigned. Defensive null checks are not optional:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Identity;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Identity manager <span style="color:#f92672">=</span> identity.<span style="color:#a6e22e">getManager</span>();
</span></span><span style="display:flex;"><span>String managerEmail <span style="color:#f92672">=</span> <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (manager <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>    managerEmail <span style="color:#f92672">=</span> manager.<span style="color:#a6e22e">getStringAttribute</span>(<span style="color:#e6db74">&#34;email&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (managerEmail <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">||</span> managerEmail.<span style="color:#a6e22e">trim</span>().<span style="color:#a6e22e">length</span>() <span style="color:#f92672">==</span> 0) {
</span></span><span style="display:flex;"><span>    managerEmail <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;identity-governance@example.com&#34;</span>;  <span style="color:#75715e">// fallback</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> managerEmail;
</span></span></code></pre></div><h2 id="rules-the-most-common-extension-point">Rules: The Most Common Extension Point</h2>
<p>A rule is a <code>Rule</code> object stored in the database, containing a BeanShell script and a declared type. The type determines <strong>which arguments IdentityIQ passes in</strong>, and this is where most confusion lives — every rule type receives a different set of variables.</p>
<h3 id="anatomy-of-a-rule">Anatomy of a Rule</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#39;1.0&#39; encoding=&#39;UTF-8&#39;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE Rule PUBLIC &#34;sailpoint.dtd&#34; &#34;sailpoint.dtd&#34;&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;Rule</span> <span style="color:#a6e22e">language=</span><span style="color:#e6db74">&#34;beanshell&#34;</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Example Manager Email Rule&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;IdentityAttribute&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Description&gt;</span>
</span></span><span style="display:flex;"><span>    Returns the manager&#39;s email address, falling back to a governance mailbox.
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Description&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Signature</span> <span style="color:#a6e22e">returnType=</span><span style="color:#e6db74">&#34;String&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Inputs&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&lt;Argument</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;identity&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;sailpoint.object.Identity&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&lt;Argument</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;context&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;sailpoint.api.SailPointContext&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/Inputs&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Signature&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Source&gt;</span><span style="color:#75715e">&lt;![CDATA[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    import sailpoint.object.Identity;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    if (identity == null) {
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        return null;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    Identity manager = identity.getManager();
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    if (manager == null) {
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        return &#34;identity-governance@example.com&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    return manager.getStringAttribute(&#34;email&#34;);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">  ]]&gt;</span><span style="color:#f92672">&lt;/Source&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/Rule&gt;</span>
</span></span></code></pre></div><p>Three details matter here:</p>
<ul>
<li><strong>The <code>&lt;Source&gt;</code> must be wrapped in <code>CDATA</code>.</strong> Without it, any <code>&lt;</code>, <code>&gt;</code>, or <code>&amp;</code> in your code breaks the XML parse.</li>
<li><strong>The <code>type</code> attribute is not cosmetic.</strong> It controls the input arguments and where the rule appears in the UI dropdowns.</li>
<li><strong><code>&lt;Signature&gt;</code> is documentation, not enforcement.</strong> BeanShell does not validate arguments against it. Declaring an argument that IdentityIQ does not actually pass yields <code>null</code> at runtime, not an error.</li>
</ul>
<h3 id="rule-types-you-will-actually-write">Rule Types You Will Actually Write</h3>
<p>IdentityIQ defines dozens of rule types. In practice, a small handful cover most work:</p>
<p><strong>BuildMap</strong> — Runs once per row of incoming data during aggregation, converting a raw record into a <code>Map</code> of attributes. Required by the JDBC connector, and used heavily with delimited-file connectors. The <code>record</code> variable holds the incoming data:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.HashMap;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>HashMap resultMap <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> 0; i <span style="color:#f92672">&lt;</span> cols.<span style="color:#a6e22e">size</span>(); i<span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>    String colName <span style="color:#f92672">=</span> (String) cols.<span style="color:#a6e22e">get</span>(i);
</span></span><span style="display:flex;"><span>    Object value <span style="color:#f92672">=</span> record.<span style="color:#a6e22e">get</span>(colName);
</span></span><span style="display:flex;"><span>    resultMap.<span style="color:#a6e22e">put</span>(colName, value);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Derive a value that does not exist in the source</span>
</span></span><span style="display:flex;"><span>String status <span style="color:#f92672">=</span> (String) resultMap.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;EMP_STATUS&#34;</span>);
</span></span><span style="display:flex;"><span>resultMap.<span style="color:#a6e22e">put</span>(<span style="color:#e6db74">&#34;isActive&#34;</span>, <span style="color:#e6db74">&#34;1&#34;</span>.<span style="color:#a6e22e">equals</span>(status) <span style="color:#f92672">?</span> <span style="color:#e6db74">&#34;true&#34;</span> : <span style="color:#e6db74">&#34;false&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> resultMap;
</span></span></code></pre></div><p><strong>Correlation</strong> — Decides which identity an account belongs to when a simple attribute match is not enough. Returns a <code>Map</code> naming the identity attribute to match on:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.HashMap;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>HashMap result <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap();
</span></span><span style="display:flex;"><span>String employeeId <span style="color:#f92672">=</span> (String) account.<span style="color:#a6e22e">getAttribute</span>(<span style="color:#e6db74">&#34;employeeNumber&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (employeeId <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">&amp;&amp;</span> employeeId.<span style="color:#a6e22e">length</span>() <span style="color:#f92672">&gt;</span> 0) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Strip a legacy prefix before matching</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (employeeId.<span style="color:#a6e22e">startsWith</span>(<span style="color:#e6db74">&#34;E-&#34;</span>)) {
</span></span><span style="display:flex;"><span>        employeeId <span style="color:#f92672">=</span> employeeId.<span style="color:#a6e22e">substring</span>(2);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    result.<span style="color:#a6e22e">put</span>(<span style="color:#e6db74">&#34;identityAttributeName&#34;</span>, <span style="color:#e6db74">&#34;employeeId&#34;</span>);
</span></span><span style="display:flex;"><span>    result.<span style="color:#a6e22e">put</span>(<span style="color:#e6db74">&#34;identityAttributeValue&#34;</span>, employeeId);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> result;
</span></span></code></pre></div><p><strong>IdentityAttribute</strong> — Computes a value for an identity attribute during the Identity Refresh task. Receives <code>identity</code> and, for some configurations, <code>link</code>.</p>
<p><strong>Provisioning / BeforeProvisioning / AfterProvisioning</strong> — Modify a <code>ProvisioningPlan</code> on its way to a target system. The canonical use case is translating IdentityIQ&rsquo;s values into whatever encoding the target expects — for instance converting <code>&quot;Full&quot;</code> to the numeric code <code>1</code>.</p>
<p><strong>Certification</strong> — Filter or pre-decide certification items, typically to auto-approve low-risk entitlements so reviewers only see what matters.</p>
<blockquote>
<p><strong>Find the exact arguments for any rule type</strong> in <code>IdentityIQ_HOME/WEB-INF/config/examplerules.xml</code>. This file contains a working example of every rule type with its real input arguments, and it is more reliable than the documentation for this specific question.</p></blockquote>
<h3 id="rule-libraries-prevent-copy-paste-sprawl">Rule Libraries Prevent Copy-Paste Sprawl</h3>
<p>Do not duplicate helper logic across twenty rules. Put shared functions in a rule of type <code>null</code> and reference it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;Rule</span> <span style="color:#a6e22e">language=</span><span style="color:#e6db74">&#34;beanshell&#34;</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Example Rule Library&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;null&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Source&gt;</span><span style="color:#75715e">&lt;![CDATA[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    public static String normalizeDepartment(String raw) {
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        if (raw == null) return &#34;UNKNOWN&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        return raw.trim().toUpperCase().replaceAll(&#34;[^A-Z0-9]&#34;, &#34;_&#34;);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">  ]]&gt;</span><span style="color:#f92672">&lt;/Source&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/Rule&gt;</span>
</span></span></code></pre></div><p>Then in any consuming rule:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;Rule</span> <span style="color:#a6e22e">language=</span><span style="color:#e6db74">&#34;beanshell&#34;</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Department Attribute Rule&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;IdentityAttribute&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;ReferencedRules&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Reference</span> <span style="color:#a6e22e">class=</span><span style="color:#e6db74">&#34;sailpoint.object.Rule&#34;</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Example Rule Library&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/ReferencedRules&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Source&gt;</span><span style="color:#75715e">&lt;![CDATA[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    return normalizeDepartment(identity.getStringAttribute(&#34;dept&#34;));
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">  ]]&gt;</span><span style="color:#f92672">&lt;/Source&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/Rule&gt;</span>
</span></span></code></pre></div><h2 id="the-sailpointcontext-api">The SailPointContext API</h2>
<p><code>SailPointContext</code> is your handle to the database. Nearly every rule receives it as <code>context</code>. Four operations cover most usage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Identity;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Application;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Filter;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.QueryOptions;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.Iterator;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.List;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.ArrayList;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 1. Fetch a single object by name</span>
</span></span><span style="display:flex;"><span>Identity user <span style="color:#f92672">=</span> context.<span style="color:#a6e22e">getObjectByName</span>(Identity.<span style="color:#a6e22e">class</span>, <span style="color:#e6db74">&#34;jdoe&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 2. Fetch by ID</span>
</span></span><span style="display:flex;"><span>Application app <span style="color:#f92672">=</span> context.<span style="color:#a6e22e">getObjectById</span>(Application.<span style="color:#a6e22e">class</span>, appId);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 3. Query with filters</span>
</span></span><span style="display:flex;"><span>QueryOptions qo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> QueryOptions();
</span></span><span style="display:flex;"><span>qo.<span style="color:#a6e22e">addFilter</span>(Filter.<span style="color:#a6e22e">eq</span>(<span style="color:#e6db74">&#34;inactive&#34;</span>, <span style="color:#66d9ef">new</span> Boolean(<span style="color:#66d9ef">false</span>)));
</span></span><span style="display:flex;"><span>qo.<span style="color:#a6e22e">addFilter</span>(Filter.<span style="color:#a6e22e">like</span>(<span style="color:#e6db74">&#34;department&#34;</span>, <span style="color:#e6db74">&#34;Engineering&#34;</span>, Filter.<span style="color:#a6e22e">MatchMode</span>.<span style="color:#a6e22e">START</span>));
</span></span><span style="display:flex;"><span>List identities <span style="color:#f92672">=</span> context.<span style="color:#a6e22e">getObjects</span>(Identity.<span style="color:#a6e22e">class</span>, qo);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 4. Save changes — BOTH calls are required</span>
</span></span><span style="display:flex;"><span>user.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;riskTier&#34;</span>, <span style="color:#e6db74">&#34;HIGH&#34;</span>);
</span></span><span style="display:flex;"><span>context.<span style="color:#a6e22e">saveObject</span>(user);
</span></span><span style="display:flex;"><span>context.<span style="color:#a6e22e">commitTransaction</span>();
</span></span></code></pre></div><h3 id="use-projection-queries-for-bulk-reads">Use Projection Queries for Bulk Reads</h3>
<p><code>context.getObjects()</code> hydrates every full object into memory. Against a large identity cube this will exhaust the heap. When you only need a few fields, use a projection query, which returns an iterator over <code>Object[]</code> rows and streams results:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.QueryOptions;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Identity;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.Iterator;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.ArrayList;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.List;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>QueryOptions qo <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> QueryOptions();
</span></span><span style="display:flex;"><span>qo.<span style="color:#a6e22e">addFilter</span>(Filter.<span style="color:#a6e22e">eq</span>(<span style="color:#e6db74">&#34;inactive&#34;</span>, <span style="color:#66d9ef">new</span> Boolean(<span style="color:#66d9ef">false</span>)));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>List props <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList();
</span></span><span style="display:flex;"><span>props.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#34;id&#34;</span>);
</span></span><span style="display:flex;"><span>props.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#34;name&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Iterator it <span style="color:#f92672">=</span> context.<span style="color:#a6e22e">search</span>(Identity.<span style="color:#a6e22e">class</span>, qo, props);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> count <span style="color:#f92672">=</span> 0;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> (it.<span style="color:#a6e22e">hasNext</span>()) {
</span></span><span style="display:flex;"><span>    Object<span style="color:#f92672">[]</span> row <span style="color:#f92672">=</span> (Object<span style="color:#f92672">[]</span>) it.<span style="color:#a6e22e">next</span>();
</span></span><span style="display:flex;"><span>    String id <span style="color:#f92672">=</span> (String) row<span style="color:#f92672">[</span>0<span style="color:#f92672">]</span>;
</span></span><span style="display:flex;"><span>    String name <span style="color:#f92672">=</span> (String) row<span style="color:#f92672">[</span>1<span style="color:#f92672">]</span>;
</span></span><span style="display:flex;"><span>    count<span style="color:#f92672">++</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;Processed &#34;</span> <span style="color:#f92672">+</span> count <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34; identities&#34;</span>;
</span></span></code></pre></div><p>For long-running loops, call <code>context.decache()</code> periodically to clear the Hibernate session, or memory will grow until the task fails.</p>
<h2 id="workflows-orchestrating-processes-that-pause">Workflows: Orchestrating Processes That Pause</h2>
<p>A workflow is an XML state machine. It exists because rules cannot wait. When a user requests access and a manager must approve it, the process may sit idle for days — the workflow persists to a <code>WorkflowCase</code> row and resumes when the approval arrives, surviving application restarts.</p>
<h3 id="steps-and-transitions">Steps and Transitions</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;Workflow</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Example Access Request Approval&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;LCMProvisioning&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Variable</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;identityName&#34;</span> <span style="color:#a6e22e">input=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Variable</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;plan&#34;</span> <span style="color:#a6e22e">input=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Variable</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;approvalDecision&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Start&#34;</span> <span style="color:#a6e22e">icon=</span><span style="color:#e6db74">&#34;Start&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Evaluate Risk&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Step&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Evaluate Risk&#34;</span> <span style="color:#a6e22e">resultVariable=</span><span style="color:#e6db74">&#34;riskLevel&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Script&gt;</span><span style="color:#75715e">&lt;![CDATA[
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">      import sailpoint.object.Identity;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">      Identity id = context.getObjectByName(Identity.class, identityName);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">      if (id != null &amp;&amp; id.getScore() != null &amp;&amp; id.getScore().intValue() &gt; 500) {
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          return &#34;HIGH&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">      }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">      return &#34;LOW&#34;;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    ]]&gt;</span><span style="color:#f92672">&lt;/Script&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Manager Approval&#34;</span> <span style="color:#a6e22e">when=</span><span style="color:#e6db74">&#39;riskLevel == &#34;HIGH&#34;&#39;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Auto Approve&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Step&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Manager Approval&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Approval</span> <span style="color:#a6e22e">mode=</span><span style="color:#e6db74">&#34;serial&#34;</span> <span style="color:#a6e22e">owner=</span><span style="color:#e6db74">&#34;script:...&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&lt;Arg</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;workItemDescription&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;Approve access request&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/Approval&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Provision&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Step&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Auto Approve&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Provision&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Step&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Provision&#34;</span> <span style="color:#a6e22e">action=</span><span style="color:#e6db74">&#34;call:provisionProject&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;Transition</span> <span style="color:#a6e22e">to=</span><span style="color:#e6db74">&#34;Stop&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;/Step&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&lt;Step</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;Stop&#34;</span> <span style="color:#a6e22e">icon=</span><span style="color:#e6db74">&#34;Stop&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/Workflow&gt;</span>
</span></span></code></pre></div><p>Key mechanics:</p>
<ul>
<li><strong><code>&lt;Transition&gt;</code> order matters.</strong> They are evaluated top to bottom and the first matching <code>when</code> wins. Always place a bare <code>&lt;Transition&gt;</code> last as the default branch, or the workflow will dead-end.</li>
<li><strong><code>resultVariable</code></strong> captures a step&rsquo;s return value into a workflow variable usable by later steps and transition conditions.</li>
<li><strong>Variables marked <code>input=&quot;true&quot;</code></strong> are supplied by the caller. Everything else starts null.</li>
</ul>
<h3 id="debugging-workflows">Debugging Workflows</h3>
<p>Workflows fail silently more often than rules do, because a failed transition simply stops the case. Two techniques:</p>
<ol>
<li><strong>Enable workflow trace.</strong> Add <code>&lt;Arg name=&quot;trace&quot; value=&quot;true&quot;/&gt;</code> to the workflow, and step-by-step execution prints to stdout — usually <code>catalina.out</code> on Tomcat.</li>
<li><strong>Inspect the stuck case.</strong> In the iiq console: <code>list WorkflowCase</code> then <code>checkout WorkflowCase &quot;&lt;name&gt;&quot; /tmp/case.xml</code> to see exactly which step it halted on and the state of every variable.</li>
</ol>
<h2 id="tasks-scheduled-bulk-operations">Tasks: Scheduled Bulk Operations</h2>
<p>Tasks are <code>TaskDefinition</code> objects run on a schedule. The built-ins cover most needs — <strong>Account Aggregation</strong> pulls accounts from a source, <strong>Identity Refresh</strong> recalculates attributes, roles, and risk scores across the identity cube.</p>
<p>When you need behaviour the built-ins do not provide, write a custom task executor in Java (not BeanShell) by implementing <code>TaskExecutor</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.example.iiq.task;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.api.SailPointContext;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.Attributes;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.TaskResult;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.object.TaskSchedule;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sailpoint.task.AbstractTaskExecutor;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DormantAccountTask</span> <span style="color:#66d9ef">extends</span> AbstractTaskExecutor {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">boolean</span> terminated <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">execute</span>(SailPointContext context, TaskSchedule schedule,
</span></span><span style="display:flex;"><span>                        TaskResult result, Attributes args) <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">int</span> threshold <span style="color:#f92672">=</span> args.<span style="color:#a6e22e">getInt</span>(<span style="color:#e6db74">&#34;dormantDays&#34;</span>, 90);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">int</span> processed <span style="color:#f92672">=</span> 0;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// ... query and process identities, checking terminated each iteration</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        result.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;identitiesProcessed&#34;</span>, <span style="color:#66d9ef">new</span> Integer(processed));
</span></span><span style="display:flex;"><span>        result.<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;dormantThreshold&#34;</span>, <span style="color:#66d9ef">new</span> Integer(threshold));
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">boolean</span> <span style="color:#a6e22e">terminate</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">terminated</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Compile this into a JAR, drop it in <code>IdentityIQ_HOME/WEB-INF/lib/</code>, restart the application server, and register it with a <code>TaskDefinition</code> XML pointing at the class name.</p>
<p><strong>Always honour <code>terminate()</code>.</strong> A task that ignores it cannot be stopped from the UI, and an administrator&rsquo;s only remaining option is restarting the application server.</p>
<h2 id="the-iiq-console">The iiq Console</h2>
<p>The console is where you deploy, inspect, and debug. Launch it from <code>IdentityIQ_HOME/WEB-INF/bin</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./iiq console          <span style="color:#75715e"># Linux/macOS</span>
</span></span><span style="display:flex;"><span>iiq.bat console        <span style="color:#75715e"># Windows</span>
</span></span></code></pre></div><p>It requires the System Administrator capability and authenticates as <code>spadmin</code> by default. The commands you will use constantly:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 568 105"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='0' y='84' fill='currentColor' style='font-size:1em'>&gt;</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='16' y='68' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='24' y='36' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='24' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='24' y='84' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='32' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='32' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='40' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='56' y='68' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='72' y='68' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='68' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='304' y='36' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='304' y='52' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='304' y='84' fill='currentColor' style='font-size:1em'>#</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='320' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='320' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='320' y='52' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='320' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='320' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='328' y='36' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='328' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='328' y='68' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='328' y='84' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='336' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='336' y='36' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='336' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='336' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='84' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='344' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='344' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='344' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='352' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='352' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='352' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='360' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='368' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='368' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='368' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='368' y='84' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='376' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='376' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='376' y='84' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='384' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='384' y='68' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='384' y='84' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='392' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='392' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='400' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='400' y='52' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='400' y='68' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='84' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='408' y='36' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='408' y='52' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='408' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='36' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='416' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='416' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='424' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='84' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='432' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='432' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='432' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='440' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='440' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='440' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='448' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='448' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='448' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='448' y='84' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='456' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='456' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='456' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='464' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='464' y='84' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='472' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='472' y='36' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='472' y='52' fill='currentColor' style='font-size:1em'>X</text>
<text text-anchor='middle' x='472' y='68' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='472' y='84' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='480' y='52' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='480' y='68' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='488' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='488' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='488' y='52' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='488' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='496' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='496' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='504' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='504' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='504' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='512' y='20' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='512' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='512' y='68' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='520' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='520' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='520' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='528' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='528' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='528' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='536' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='536' y='36' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='536' y='68' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='544' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='552' y='20' fill='currentColor' style='font-size:1em'>n</text>
</g>

    </svg>
  
</div>
<p><code>checkout</code> plus <code>import</code> is the migration path between environments. Export from dev, commit the XML to version control, import into test.</p>
<h2 id="logging-and-debugging">Logging and Debugging</h2>
<p>Configure logging in <code>IdentityIQ_HOME/WEB-INF/classes/log4j2.properties</code>. IdentityIQ picks up changes to this file automatically within roughly 60 seconds — <strong>no application restart required</strong>, which is the single biggest time-saver in IdentityIQ debugging.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#75715e"># Namespace your rule logging so you can raise it without drowning in output</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.customrules.name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">com.example.iiq</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.customrules.level</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">debug</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Useful built-in loggers</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.connector.name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">sailpoint.connector</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.connector.level</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">debug</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.workflow.name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">sailpoint.workflow</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger.workflow.level</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">debug</span>
</span></span></code></pre></div><p>Inside a rule:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> org.apache.log4j.Logger;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Logger log <span style="color:#f92672">=</span> Logger.<span style="color:#a6e22e">getLogger</span>(<span style="color:#e6db74">&#34;com.example.iiq.correlation&#34;</span>);
</span></span><span style="display:flex;"><span>log.<span style="color:#a6e22e">debug</span>(<span style="color:#e6db74">&#34;Correlating account: &#34;</span> <span style="color:#f92672">+</span> account.<span style="color:#a6e22e">getNativeIdentity</span>());
</span></span></code></pre></div><p>Never use <code>System.out.println()</code> in production rules. It writes to the container log with no level control, no namespace, and no way to disable it without a code change and restart.</p>
<h2 id="deployment-practices-that-prevent-outages">Deployment Practices That Prevent Outages</h2>
<p><strong>Version-control the XML, not the database.</strong> IdentityIQ objects live in the database, which makes them invisible to Git by default. Export every custom rule, workflow, and task definition with <code>checkout</code> and commit the XML. Without this, a database refresh silently destroys work.</p>
<p><strong>Never edit rules in production through the UI.</strong> The debug pages allow direct object editing, which creates changes that exist in exactly one environment and are lost on the next deployment.</p>
<p><strong>Test correlation rules against real edge cases</strong> — accounts with null employee IDs, duplicate IDs, service accounts that should match nothing. A correlation rule that throws an exception aborts the entire aggregation run, not just the one account.</p>
<p><strong>Keep rules short.</strong> A rule doing substantial work belongs in a compiled Java class in <code>WEB-INF/lib/</code>, called from a thin BeanShell wrapper. You get compile-time type checking, real unit tests, and a debugger.</p>
<h2 id="where-this-fits-in-broader-identity-governance">Where This Fits in Broader Identity Governance</h2>
<p>Rules, workflows, and tasks are the mechanics. What you build with them is governance — access certification, joiner-mover-leaver automation, separation-of-duties enforcement. For the strategic layer above this code, see our guide to <a href="/posts/identity-governance-in-the-zero-trust-era-achieving-dynamic-privileged-access-management-with-cyberark-and-sailpoint/">identity governance in the Zero Trust era</a>, and for where the platform is heading, <a href="/posts/sailpoint-extends-identity-governance-to-ai-agents-techinformed/">SailPoint&rsquo;s extension of governance to AI agents</a>.</p>
<p>The infrastructure underneath — the Java runtime, the MySQL schema your queries hit, and the shell scripts that automate deployment — is covered in the companion article on <a href="/posts/sailpoint-identityiq-java-mysql-shell-scripting-guide/">Java, MySQL, and shell scripting for SailPoint IdentityIQ</a>.</p>
]]></content:encoded></item></channel></rss>