PostgresSql Manual Provisioning

Hello Everyone,

I’m trying to implement manual provisioning for PostgreSQL in SailPoint IIQ by adding a Create Provisioning Rule under the application’s connector rules section.

However, after saving the rule and performing manual provisioning (account creation), when I tried to open the application again in IIQ, the application page went blank and showed an error message instead of loading properly.

It seems that adding the custom rule somehow corrupted the application definition or XML, because the rest of the pages in IdentityIQ are working fine — only this application fails to load.

Rule:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

// Helper method to get attribute values
public String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
    if (acctReq != null) {
        AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);
        if (attrReq != null) {
            return attrReq.getValue();
        }
    }
    return null;
}

// Main Provisioning Rule
AccountRequest acctRequest = (AccountRequest) request;
ProvisioningResult result = new ProvisioningResult();
Log log = LogFactory.getLog("postgres-provisioning");

Connection connection = null;

try {
    // PostgreSQL Connection
    Class.forName("org.postgresql.Driver");
    connection = DriverManager.getConnection(
        "jdbc:postgresql://localhost:5432/viraj",   // DB URL
        "postgres",                                // DB user
        "your_password"                            // DB password
    );

    log.debug("Operation [" + acctRequest.getOperation() + "] detected.");

    if ("Create".equalsIgnoreCase(acctRequest.getOperation().toString())) {
        // INSERT into authoritative table
        PreparedStatement statement = connection.prepareStatement(
            "INSERT INTO authoritative(id,employee_id, first_name, last_name, email, department, title, manager_id, status, location) " +
            "VALUES (?,?,?,?,?,?,?,?,?,?)"
        );

        statement.setString(1, getAttributeRequestValue(acctRequest,"id"));
        statement.setString(2, getAttributeRequestValue(acctRequest,"employee_id"));
        statement.setString(2, getAttributeRequestValue(acctRequest,"first_name"));
        statement.setString(3, getAttributeRequestValue(acctRequest,"last_name"));
        statement.setString(4, getAttributeRequestValue(acctRequest,"email"));
        statement.setString(5, getAttributeRequestValue(acctRequest,"department"));
        statement.setString(6, getAttributeRequestValue(acctRequest,"title"));
        statement.setString(7, getAttributeRequestValue(acctRequest,"manager_id"));
        statement.setString(9, getAttributeRequestValue(acctRequest,"status"));
        statement.setString(10, getAttributeRequestValue(acctRequest,"location"));

        statement.executeUpdate();
        result.setStatus(ProvisioningResult.STATUS_COMMITTED);
    }
    else {
        log.debug("Unsupported operation: " + acctRequest.getOperation());
    }
}
catch (Exception e) {
    log.error("Provisioning failed: ", e);
    result.setStatus(ProvisioningResult.STATUS_FAILED);
    result.addError(e);
}
finally {
    if (connection != null) {
        try { connection.close(); } catch (SQLException e) { log.error(e); }
    }
}

return result;



<ProvisioningForms><Form name="PostgresForm" objectType="account" type="Create"><Attributes><Map><entry key="pageTitle" value="PostgresForm"/></Map></Attributes><Section name="Data"><Field displayName="ID" name="id" required="true" type="string"/><Field displayName="Employee Id" name="employee_id" required="true" type="string"/><Field displayName="First Name" name="first_name" required="true" type="string"/><Field displayName="Last Name" name="last_name" required="true" type="string"/><Field displayName="Department" name="department" required="true" type="string"/><Field displayName="Title&#x9;" name="title&#x9;" required="true" type="string"/><Field displayName="Email" name="email" required="true" type="string"/><Field displayName="Location" name="location" required="true" type="string"/><Field displayName="Manager Id" name="manager_id" required="true" type="string"/><Field displayName="Status" name="status" required="true" type="string"/></Section></Form></ProvisioningForms>

Hi @Viraj

I see multiple issues in your provisioning rule.

One example is below.

statement.setString(2, getAttributeRequestValue(acctRequest,"employee_id")); statement.setString(2, getAttributeRequestValue(acctRequest,"first_name"));

In this, you are using 2 in both the setString method. There are other issues, also like null pointer check and others. Please fix the rule and then try again.

If you get an issue, let me know.

Also,

In this setString(8, ******) is missed.

I have updated the rule:

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;import sailpoint.object.ProvisioningPlan.AccountRequest;import sailpoint.object.ProvisioningPlan.AttributeRequest;import sailpoint.object.ProvisioningResult;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;

// Helper method to get attribute valuespublic String getAttributeRequestValue(AccountRequest acctReq, String attribute) {if (acctReq != null) {AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);if (attrReq != null) {return attrReq.getValue();}}return null;}

// Main Provisioning RuleAccountRequest acctRequest = (AccountRequest) request;ProvisioningResult result = new ProvisioningResult();Log log = LogFactory.getLog(“postgres-provisioning”);

Connection connection = null;

try {// PostgreSQL ConnectionClass.forName(“org.postgresql.Driver”);connection = DriverManager.getConnection(“jdbc:postgresql://localhost:5432/viraj”,   // DB URL“postgres”,                                // DB user“your_password”                            // DB password);
log.debug("Operation [" + acctRequest.getOperation() + "] detected.");

if ("Create".equalsIgnoreCase(acctRequest.getOperation().toString())) {
    // INSERT into authoritative table
    PreparedStatement statement = connection.prepareStatement(
        "INSERT INTO authoritative(id,employee_id, first_name, last_name, email, department, title, manager_id, status, location) " +
        "VALUES (?,?,?,?,?,?,?,?,?,?)"
    );

    statement.setString(1, getAttributeRequestValue(acctRequest,"id"));
    statement.setString(2, getAttributeRequestValue(acctRequest,"employee_id"));
    statement.setString(3, getAttributeRequestValue(acctRequest,"first_name"));
    statement.setString(4, getAttributeRequestValue(acctRequest,"last_name"));
    statement.setString(5, getAttributeRequestValue(acctRequest,"email"));
    statement.setString(6, getAttributeRequestValue(acctRequest,"department"));
    statement.setString(7, getAttributeRequestValue(acctRequest,"title"));
    statement.setString(8, getAttributeRequestValue(acctRequest,"manager_id"));
    statement.setString(9, getAttributeRequestValue(acctRequest,"status"));
    statement.setString(10, getAttributeRequestValue(acctRequest,"location"));

    statement.executeUpdate();
    result.setStatus(ProvisioningResult.STATUS_COMMITTED);
}
else {
    log.debug("Unsupported operation: " + acctRequest.getOperation());
}

}catch (Exception e) {log.error("Provisioning failed: ", e);result.setStatus(ProvisioningResult.STATUS_FAILED);result.addError(e);}finally {if (connection != null) {try { connection.close(); } catch (SQLException e) { log.error(e); }}}

return result;

And again tried the manual provisioning but this time there was no error and nothing is visible in Provisioning engine

Please share your provisioningRule XML file. Will check if there is any issue with that.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule created="1758024820097" id="a9fe471b994d1c3381995271e58106ad" language="beanshell" modified="1759821099781" name="PostgresRule" type="JDBCOperationProvisioning">
  <Description>This rule is used by the JDBC connector to do provisioning of the data .</Description>
  <Signature returnType="ProvisioningResult">
    <Inputs>
      <Argument name="log" type="org.apache.commons.logging.Log">
        <Description>
          The log object associated with the SailPointContext.
        </Description>
      </Argument>
      <Argument name="context" type="sailpoint.api.SailPointContext">
        <Description>
          A sailpoint.api.SailPointContext object that can be used to query the database if necessary.
        </Description>
      </Argument>
      <Argument name="application">
        <Description>The application whose data file is being processed.</Description>
      </Argument>
      <Argument name="schema">
        <Description>The Schema currently in use.</Description>
      </Argument>
      <Argument name="connection">
        <Description>A connection object to connect to database.</Description>
      </Argument>
      <Argument name="plan">
        <Description>The ProvisioningPlan created against the JDBC application.</Description>
      </Argument>
      <Argument name="request">
        <Description>The ProvisioningRequest created against the JDBC application.</Description>
      </Argument>
    </Inputs>
    <Returns>
      <Argument name="result">
        <Description>A Provisioning Result object is desirable to return the status.IT can be a new object or part of  Provisioning Plan</Description>
      </Argument>
    </Returns>
  </Signature>
  <Source>import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

// Helper methods
String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
    if (acctReq != null) {
        AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);
        if (attrReq != null) {
            return (String) attrReq.getValue();
        }
    }
    return null;
}

String safe(String s) {
    return s == null ? "" : s;
}

// Main Provisioning Logic
AccountRequest acctRequest = (AccountRequest) request;
ProvisioningResult result = new ProvisioningResult();
Log log = LogFactory.getLog("postgres-provisioning");

Connection connection = null;

try {
    // Get DB details from application definition
    String dbUrl = application.getAttribute("url");
    String dbUser = application.getAttribute("user");
    String dbPass = application.getAttribute("password");

    Class.forName("org.postgresql.Driver");
    connection = DriverManager.getConnection(dbUrl, dbUser, dbPass);

    log.debug("Operation [" + acctRequest.getOperation() + "] detected.");

    if ("Create".equalsIgnoreCase(acctRequest.getOperation().toString())) {

        // Log all incoming attribute values for debugging
        log.error("===== Incoming Attribute Values =====");
        log.error("id: " + getAttributeRequestValue(acctRequest, "id"));
        log.error("employee_id: " + getAttributeRequestValue(acctRequest, "employee_id"));
        log.error("first_name: " + getAttributeRequestValue(acctRequest, "first_name"));
        log.error("last_name: " + getAttributeRequestValue(acctRequest, "last_name"));
        log.error("email: " + getAttributeRequestValue(acctRequest, "email"));
        log.error("department: " + getAttributeRequestValue(acctRequest, "department"));
        log.error("title: " + getAttributeRequestValue(acctRequest, "title"));
        log.error("manager_id: " + getAttributeRequestValue(acctRequest, "manager_id"));
        log.error("status: " + getAttributeRequestValue(acctRequest, "status"));
        log.error("location: " + getAttributeRequestValue(acctRequest, "location"));
        log.error("=====================================");

        PreparedStatement statement = connection.prepareStatement(
            "INSERT INTO authoritative(id, employee_id, first_name, last_name, email, department, title, manager_id, status, location) " +
            "VALUES (?,?,?,?,?,?,?,?,?,?)"
        );

        statement.setString(1, safe(getAttributeRequestValue(acctRequest,"id")));
        statement.setString(2, safe(getAttributeRequestValue(acctRequest,"employee_id")));
        statement.setString(3, safe(getAttributeRequestValue(acctRequest,"first_name")));
        statement.setString(4, safe(getAttributeRequestValue(acctRequest,"last_name")));
        statement.setString(5, safe(getAttributeRequestValue(acctRequest,"email")));
        statement.setString(6, safe(getAttributeRequestValue(acctRequest,"department")));
        statement.setString(7, safe(getAttributeRequestValue(acctRequest,"title")));
        statement.setString(8, safe(getAttributeRequestValue(acctRequest,"manager_id")));
        statement.setString(9, safe(getAttributeRequestValue(acctRequest,"status")));
        statement.setString(10, safe(getAttributeRequestValue(acctRequest,"location")));

        statement.executeUpdate();

        result.setStatus(ProvisioningResult.STATUS_COMMITTED);
        log.debug("Account created successfully in PostgreSQL for employee: " + getAttributeRequestValue(acctRequest,"employee_id"));
    }
    else {
        log.debug("Unsupported operation: " + acctRequest.getOperation());
    }
}
catch (Exception e) {
    log.error("Provisioning failed for user: " + getAttributeRequestValue(acctRequest,"employee_id"), e);
    result.setStatus(ProvisioningResult.STATUS_FAILED);
    result.addError(e);
}
finally {
    if (connection != null) {
        try { connection.close(); } catch (SQLException e) { log.error(e); }
    }
}

return result;
</Source>
</Rule>

Creating a connection object is not required, as the connection is already available as an argument.

Connection connection = null;
try {// Get DB details from application definitionString dbUrl = application.getAttribute(“url”);String dbUser = application.getAttribute(“user”);String dbPass = application.getAttribute(“password”);
Class.forName("org.postgresql.Driver");
connection = DriverManager.getConnection(dbUrl, dbUser, dbPass);

This can be removed.

Updated code:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule created="1758024820097" id="a9fe471b994d1c3381995271e58106ad" language="beanshell" modified="1759821099781" name="PostgresRule" type="JDBCOperationProvisioning">
  <Description>This rule is used by the JDBC connector to do provisioning of the data .</Description>
  <Signature returnType="ProvisioningResult">
    <Inputs>
      <Argument name="log" type="org.apache.commons.logging.Log">
        <Description>
          The log object associated with the SailPointContext.
        </Description>
      </Argument>
      <Argument name="context" type="sailpoint.api.SailPointContext">
        <Description>
          A sailpoint.api.SailPointContext object that can be used to query the database if necessary.
        </Description>
      </Argument>
      <Argument name="application">
        <Description>The application whose data file is being processed.</Description>
      </Argument>
      <Argument name="schema">
        <Description>The Schema currently in use.</Description>
      </Argument>
      <Argument name="connection">
        <Description>A connection object to connect to database.</Description>
      </Argument>
      <Argument name="plan">
        <Description>The ProvisioningPlan created against the JDBC application.</Description>
      </Argument>
      <Argument name="request">
        <Description>The ProvisioningRequest created against the JDBC application.</Description>
      </Argument>
    </Inputs>
    <Returns>
      <Argument name="result">
        <Description>A Provisioning Result object is desirable to return the status.IT can be a new object or part of  Provisioning Plan</Description>
      </Argument>
    </Returns>
  </Signature>
  <Source>import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

// Helper methods
String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
    if (acctReq != null) {
        AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);
        if (attrReq != null) {
            return (String) attrReq.getValue();
        }
    }
    return null;
}

String safe(String s) {
    return s == null ? "" : s;
}

// Main Provisioning Logic
AccountRequest acctRequest = (AccountRequest) request;
ProvisioningResult result = new ProvisioningResult();
Log log = LogFactory.getLog("postgres-provisioning");

try {

    log.debug("Operation [" + acctRequest.getOperation() + "] detected.");

    if ("Create".equalsIgnoreCase(acctRequest.getOperation().toString())) {

        // Log all incoming attribute values for debugging
        log.error("===== Incoming Attribute Values =====");
        log.error("id: " + getAttributeRequestValue(acctRequest, "id"));
        log.error("employee_id: " + getAttributeRequestValue(acctRequest, "employee_id"));
        log.error("first_name: " + getAttributeRequestValue(acctRequest, "first_name"));
        log.error("last_name: " + getAttributeRequestValue(acctRequest, "last_name"));
        log.error("email: " + getAttributeRequestValue(acctRequest, "email"));
        log.error("department: " + getAttributeRequestValue(acctRequest, "department"));
        log.error("title: " + getAttributeRequestValue(acctRequest, "title"));
        log.error("manager_id: " + getAttributeRequestValue(acctRequest, "manager_id"));
        log.error("status: " + getAttributeRequestValue(acctRequest, "status"));
        log.error("location: " + getAttributeRequestValue(acctRequest, "location"));
        log.error("=====================================");

        PreparedStatement statement = connection.prepareStatement(
            "INSERT INTO authoritative(id, employee_id, first_name, last_name, email, department, title, manager_id, status, location) " +
            "VALUES (?,?,?,?,?,?,?,?,?,?)"
        );

        statement.setString(1, safe(getAttributeRequestValue(acctRequest,"id")));
        statement.setString(2, safe(getAttributeRequestValue(acctRequest,"employee_id")));
        statement.setString(3, safe(getAttributeRequestValue(acctRequest,"first_name")));
        statement.setString(4, safe(getAttributeRequestValue(acctRequest,"last_name")));
        statement.setString(5, safe(getAttributeRequestValue(acctRequest,"email")));
        statement.setString(6, safe(getAttributeRequestValue(acctRequest,"department")));
        statement.setString(7, safe(getAttributeRequestValue(acctRequest,"title")));
        statement.setString(8, safe(getAttributeRequestValue(acctRequest,"manager_id")));
        statement.setString(9, safe(getAttributeRequestValue(acctRequest,"status")));
        statement.setString(10, safe(getAttributeRequestValue(acctRequest,"location")));

        statement.executeUpdate();

        result.setStatus(ProvisioningResult.STATUS_COMMITTED);
        log.debug("Account created successfully in PostgreSQL for employee: " + getAttributeRequestValue(acctRequest,"employee_id"));
    }
    else {
        log.debug("Unsupported operation: " + acctRequest.getOperation());
    }
}
catch (Exception e) {
    log.error("Provisioning failed for user: " + getAttributeRequestValue(acctRequest,"employee_id"), e);
    result.setStatus(ProvisioningResult.STATUS_FAILED);
    result.addError(e);
}

return result;
</Source>
</Rule>

Also tried your rule but still in Provisioning engine nothing is there.
In syslog i can only see lcm provisioning triggered nothing else

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE SyslogEvent PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<SyslogEvent classname="bsh.Reflect" created="1759830418458" eventLevel="ERROR" id="a9fe471b99b61c848199be11241a0a16" lineNumber="166" quickKey="0000029933" server="viraj" thread="http-nio-8080-exec-1" username="spadmin">
  <EventMessage>LCM PLan is: &lt;?xml version='1.0' encoding='UTF-8'?>
&lt;!DOCTYPE ProvisioningPlan PUBLIC "sailpoint.dtd" "sailpoint.dtd">
&lt;ProvisioningPlan>
  &lt;AccountRequest application="PostgresApp" op="Create">
    &lt;Attributes>
      &lt;Map>
        &lt;entry key="operation">
          &lt;value>
            &lt;AccountOperation>Create&lt;/AccountOperation>
          &lt;/value>
        &lt;/entry>
      &lt;/Map>
    &lt;/Attributes>
  &lt;/AccountRequest>
  &lt;Attributes>
    &lt;Map>
     ...</EventMessage>
</SyslogEvent>

@Viraj

Write a logger statement to print the plan in your provisioning rule. Copy the plan and send it here.

Thanks

I have added Logs in provisioning rule but rule is not getting triggered at all

only LCM provisioning is getting triggered.

Add a before-provisioning rule and try to print the plan in the before-provisioning rule.

Thanks

Also, please share the application xml and your rule xml.

PostgresApp.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Application PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Application connector="sailpoint.connector.JDBCConnector" created="1758428681295" featuresString="ADDITIONAL_ACCOUNT_REQUEST, ACCOUNT_ONLY_REQUEST" icon="databaseIcon" id="a9fe471b994d1c3381996a84544f1112" modified="1760266530983" name="PostgresApp" profileClass="" type="JDBC">
  <Attributes>
    <Map>
      <entry key="Group.SQL" value="SELECT group_id AS __identity__, group_name, description FROM gr;"/>
      <entry key="Group.aggregationMode">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.deltaTable"/>
      <entry key="Group.driverClass" value="org.postgresql.Driver"/>
      <entry key="Group.getDeltaSQL"/>
      <entry key="Group.getDirectPermObjectSQL"/>
      <entry key="Group.getObjectSQL" value="SELECT group_id AS __identity__, group_name, description &#xD;&#xA;FROM gr ;"/>
      <entry key="Group.isPermissionEnabled">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.mergeRows">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.password" value="1:ACP:ognkngGOK/+V+qTF22L+GdVZv+f3VRMhRIcrLYVFpFU="/>
      <entry key="Group.url" value="jdbc:postgresql://localhost:5432/postgres"/>
      <entry key="Group.useExecuteQuery">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.useStoredProcedureDelta">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.useStoredProcedureDirectPermission">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.useStoredProcedureGetObject">
        <value>
          <Boolean>true</Boolean>
        </value>
      </entry>
      <entry key="Group.useStoredProcedureSqlStmt">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="Group.user" value="postgres"/>
      <entry key="SQL" value="SELECT account_id AS __identity__, username, firstname, lastname, email, status &#xD;&#xA;FROM accounts;&#xD;&#xA;"/>
      <entry key="acctAggregationEnd">
        <value>
          <Date>1760266530963</Date>
        </value>
      </entry>
      <entry key="acctAggregationStart">
        <value>
          <Date>1760266530647</Date>
        </value>
      </entry>
      <entry key="afterProvisioningRule"/>
      <entry key="aggregationMode">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="beforeProvisioningRule"/>
      <entry key="compositeDefinition"/>
      <entry key="connMaxRetry" value="3"/>
      <entry key="connRetryEnable">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="connWaitTimeForRetry" value="2"/>
      <entry key="deltaTable"/>
      <entry key="driverClass" value="org.postgresql.Driver"/>
      <entry key="getDeltaSQL"/>
      <entry key="isPermissionEnabled">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="jdbcCreateProvisioningRule" value="PostgresRule"/>
      <entry key="jdbcExceptionBucketing">
        <value>
          <Boolean>true</Boolean>
        </value>
      </entry>
      <entry key="mergeRows">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="nativeChangeDetectionAttributeScope" value="entitlements"/>
      <entry key="nativeChangeDetectionAttributes"/>
      <entry key="nativeChangeDetectionEnabled">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="nativeChangeDetectionOperations"/>
      <entry key="noPermissions" value="false"/>
      <entry key="operations" value="Create, Modify, Delete, Enable, Disable, Unlock"/>
      <entry key="partitionMode">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="partitionStatements"/>
      <entry key="password" value="1:ACP:vUJDKJgFe0cArJ05A6jBo2sH2bzdevwmb7yS6m9pQrs="/>
      <entry key="provisionRule" value="operationRule"/>
      <entry key="sysDescriptions">
        <value>
          <Map>
            <entry key="en_US"/>
          </Map>
        </value>
      </entry>
      <entry key="templateApplication" value="JDBC Template"/>
      <entry key="testConnSQL" value="SELECT account_id AS __identity__, username, firstname, lastname, email, status FROM accounts;"/>
      <entry key="url" value="jdbc:postgresql://localhost:5432/postgres"/>
      <entry key="useExecuteQuery">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedureDelta">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedureDirectPermission">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedureGetObject">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedurePartition">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedureSqlStmt">
        <value>
          <Boolean>true</Boolean>
        </value>
      </entry>
      <entry key="useStoredProcedureTestConn">
        <value>
          <Boolean></Boolean>
        </value>
      </entry>
      <entry key="user" value="postgres"/>
    </Map>
  </Attributes>
  <Owner>
    <Reference class="sailpoint.object.Identity" id="c0a8210195341dcb8195341e0de700ea" name="spadmin"/>
  </Owner>
  <ProvisioningForms>
    <Form name="PostgresForm" objectType="account" type="Create">
      <Attributes>
        <Map>
          <entry key="pageTitle" value="PostgresForm"/>
        </Map>
      </Attributes>
      <Section name="Data">
        <Field displayName="Username" name="username" required="true" type="string"/>
        <Field displayName="First Name" name="firstname" required="true" type="string"/>
        <Field displayName="Last Name" name="lastname" required="true" type="string"/>
        <Field displayName="Email" name="email" required="true" type="string"/>
        <Field displayName="Status" name="status" required="true" type="string"/>
      </Section>
    </Form>
  </ProvisioningForms>
  <Schemas>
    <Schema created="1758428681296" displayAttribute="firstname" id="a9fe471b994d1c3381996a8454501114" identityAttribute="username" instanceAttribute="" modified="1760259690416" nativeObjectType="account" objectType="account" permissionsRemediationModificationType="None">
      <AttributeDefinition name="username" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="firstname" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="lastname" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="email" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="department" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="status" type="string">
        <Description></Description>
      </AttributeDefinition>
    </Schema>
    <Schema aggregationType="group" created="1760248009174" descriptionAttribute="" displayAttribute="description" id="a9fe471b99d6132e8199d6f511d601d2" identityAttribute="group_name" instanceAttribute="" modified="1760259690416" nativeObjectType="" objectType="Group">
      <AttributeDefinition name="group_name" type="string">
        <Description></Description>
      </AttributeDefinition>
      <AttributeDefinition name="description" type="string">
        <Description></Description>
      </AttributeDefinition>
    </Schema>
  </Schemas>
  <ApplicationScorecard created="1758428681295" id="a9fe471b994d1c3381996a84544f1113" modified="1760259690416"/>
</Application>

Rule:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule created="1758024820097" id="a9fe471b994d1c3381995271e58106ad" language="beanshell" modified="1760267562726" name="PostgresRule" type="JDBCOperationProvisioning">
  <Description>This rule is used by the JDBC connector to do provisioning of the data .</Description>
  <Signature returnType="ProvisioningResult">
    <Inputs>
      <Argument name="log" type="org.apache.commons.logging.Log">
        <Description>
          The log object associated with the SailPointContext.
        </Description>
      </Argument>
      <Argument name="context" type="sailpoint.api.SailPointContext">
        <Description>
          A sailpoint.api.SailPointContext object that can be used to query the database if necessary.
        </Description>
      </Argument>
      <Argument name="application">
        <Description>The application whose data file is being processed.</Description>
      </Argument>
      <Argument name="schema">
        <Description>The Schema currently in use.</Description>
      </Argument>
      <Argument name="connection">
        <Description>A connection object to connect to database.</Description>
      </Argument>
      <Argument name="plan">
        <Description>The ProvisioningPlan created against the JDBC application.</Description>
      </Argument>
      <Argument name="request">
        <Description>The ProvisioningRequest created against the JDBC application.</Description>
      </Argument>
    </Inputs>
    <Returns>
      <Argument name="result">
        <Description>A Provisioning Result object is desirable to return the status.IT can be a new object or part of  Provisioning Plan</Description>
      </Argument>
    </Returns>
  </Signature>
  <Source>import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import sailpoint.object.ProvisioningResult;
import sailpoint.object.ProvisioningPlan.AccountRequest;
import sailpoint.object.ProvisioningPlan.AttributeRequest;
import sailpoint.object.ProvisioningPlan.PermissionRequest;
 
// Helper to get single attribute value
public String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
    if (acctReq != null) {
        AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);
        if (attrReq != null &amp;&amp; attrReq.getValue() != null) {
            return attrReq.getValue().toString();
        }
    }
    return null;
}
 
ProvisioningResult result = new ProvisioningResult();
List&lt;AccountRequest> requests = plan.getAccountRequests();
 
for (AccountRequest acctReq : requests) {
 
    if (AccountRequest.Operation.Create.equals(acctReq.getOperation())) {
 
        try {
            // Insert user into accounts table and return account_id
            String insertSQL = "INSERT INTO accounts (username, firstname, lastname, email, status) VALUES (?, ?, ?, ?, ?) RETURNING account_id";
            PreparedStatement ps1 = connection.prepareStatement(insertSQL);
            ps1.setString(1, getAttributeRequestValue(acctReq, "username"));
            ps1.setString(2, getAttributeRequestValue(acctReq, "firstname"));
            ps1.setString(3, getAttributeRequestValue(acctReq, "lastname"));
            ps1.setString(4, getAttributeRequestValue(acctReq, "email"));
            ps1.setString(5, getAttributeRequestValue(acctReq, "status"));
 
            ResultSet rs = ps1.executeQuery();
            int accountId = -1;
            if (rs.next()) {
                accountId = rs.getInt(1);
            }
            rs.close();
            ps1.close();
 
            //  Assign groups if entitlement data present
            List&lt;PermissionRequest> perms = acctReq.getPermissionRequests();
            if (perms != null &amp;&amp; !perms.isEmpty()) {
                for (PermissionRequest perm : perms) {
                    if ("group".equalsIgnoreCase(perm.getTarget())) {
                        // Expect group_id as the value
                        int groupId = Integer.parseInt(perm.getValue().toString());
                        PreparedStatement ps2 = connection.prepareStatement(
                            "INSERT INTO account_group_map (account_id, group_id) VALUES (?, ?)"
                        );
                        ps2.setInt(1, accountId);
                        ps2.setInt(2, groupId);
                        ps2.executeUpdate();
                        ps2.close();
                    }
                }
            }
 
            result.setStatus(ProvisioningResult.STATUS_COMMITTED);
 
        } catch (SQLException e) {
            result.setStatus(ProvisioningResult.STATUS_FAILED);
            result.addError(e);
            e.printStackTrace();
        }
    }
}
 
return result;</Source>
</Rule>

I have written plan in before provisioning but it was not getting triggered
Before Provisioning:

import sailpoint.object.*;
import sailpoint.tools.GeneralException;
import java.util.*;

try {
    // Get the provisioning plan from arguments
    ProvisioningPlan plan = (ProvisioningPlan) arguments.get("plan");
    
    if (plan == null) {
        log.error("Provisioning plan is null — nothing to process.");
        return null;
    }

    // Log the plan in XML format for debugging
    log.error("BP Plan is: " + plan.toXml());

    // Iterate through account requests (if any)
    for (ProvisioningPlan.AccountRequest acctReq : plan.getAccountRequests()) {
        String op = acctReq.getOperation();
        log.error("Operation Type: " + op);

        if ("Create".equalsIgnoreCase(op)) {
            log.error("Processing CREATE request for account: " + acctReq.getNativeIdentity());
            // Add your create logic here
        } else if ("Modify".equalsIgnoreCase(op)) {
            log.error("Processing MODIFY request for account: " + acctReq.getNativeIdentity());
            // Add your update logic here
        } else if ("Delete".equalsIgnoreCase(op)) {
            log.error("Processing DELETE request for account: " + acctReq.getNativeIdentity());
            // Add your delete logic here
        }
    }

    // Always return the plan object at the end
    return plan;

} catch (Exception e) {
    log.error("Error in JDBC Provisioning Rule: " + e);
    throw new GeneralException("Error in JDBC Provisioning Rule", e);
}

Thanks! Let me check.

There is no provision feature string in the application, try adding PROVISION to the already available 2 values. JDBC should already have it, but it’s not there probably removed but forgotten?

Hi @Viraj

in application.xml of your application, In feature string only two values are present. For provisioning to be enabled you need add the operation that you want to do.

Please refer below URL for feature string values:

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.