One strategy for carrying out actions like LDAP account re-names or other account provisioning actions that depend on prerequisite data is to have IdentityIQ schedule the action to be carried out in a workflow at a future date. This is called "scheduling a workflow event". Under the hood what happens is a new Request object is created and committed to the database. The Request object is of type "Workflow Request", and it carries all of the arguments required to launch the workflow at a future date. Every five seconds the request processor polls the database for new requests that need to be processed. When the "eventDate" of the workflow matches or is behind the current time the request processor will launch the workflow and carry out whatever logic it contains. When the workflow is launched it is is launched in a clean context that is separate from the thread that scheduled the workflow event.
Attached are two example artifacts: an example "hello world" workflow that simply logs the arguments passed to it and stops and the rule that demonstrates how to configure a workflow to be launched at a future date. You can import both of these artifacts into your environment and then run the rule from either the debug page or the console. Five seconds after the rule is executed the request scheduler will launch the workflow. You should see the workflow logs printed in your Identity IQ logs under your application server.
The following illustrates the boilerplate code required to schedule the future launch a Workflow from any block of BeanShell or Rule code. The import statements required are provided in the attached example files. The strings in the example below are designed to be easily edited in the field so you can modify them to substitute in your own variables.
// This rule demonstrates launching a workflow using the Request Scheduler
// features of IdentityIQ 6.2+. This places a Request in the queue to launch
// the specified workflow with the specified arguments. This feature allows
// architects to schedule events to immediately follow Identity refresh
// operations. This takes processing out of Identity refresh tasks and allows
// for more use of the parallelism features in the request processor.
String workflowName = "Hello World Example";
String identityName = "john.doe";
String argumentVal = "CN=john.doe,DC=corp,DC=acme,DC=com";
String caseName = "Run '" + workflowName + "' for: " + identityName;
String requesterId = "spadmin";
Workflow eventWorkflow = context.getObject(Workflow.class, workflowName);
if (null == eventWorkflow) {
log.error("Could not find a workflow named: " + workflowName);
throw new GeneralException("Invalid worklfow: " + workflowName);
}
// Simulate the request being submitted by a user. Default: spadmin.
Identity id = context.getObjectByName(Identity.class, requesterId);
if (null == id) {
log.error("Could not find a requester Identity: " + requesterId);
throw new GeneralException("Invalid identity: " + requesterId);
}
// Ask the Request Processor to start the workflow 5 seconds from now.
// Append the time stamp to the workflow case name to ensure it's unique.
long launchTime = System.currentTimeMillis() + 5000;
caseName = caseName + "(" + launchTime + ")";
// Build out a map of arguments to pass to the Request Scheduler.
Attributes reqArgs = new Attributes();
reqArgs.put(StandardWorkflowHandler.ARG_REQUEST_DEFINITION,
sailpoint.request.WorkflowRequestExecutor.DEFINITION_NAME);
reqArgs.put(sailpoint.workflow.StandardWorkflowHandler.ARG_WORKFLOW,
workflowName);
reqArgs.put(sailpoint.workflow.StandardWorkflowHandler.ARG_REQUEST_NAME,
caseName);
reqArgs.put( "requestName", caseName );
// Build a map of arguments to pass to the Workflow case when it launches.
Attributes wfArgs = new Attributes();
wfArgs.put("identityName", identityName);
wfArgs.put("exampleArgument", argumentVal);
wfArgs.put("workflow", eventWorkflow.getId());
reqArgs.putAll(wfArgs);
// Use the Request Launcher to schedule the workflow reqeust. This requires
// a Request object to store the properties of the request item.
Request req = new Request();
RequestDefinition reqdef = context.getObject(RequestDefinition.class, "Workflow Request");
req.setDefinition(reqdef);
req.setEventDate( new Date( launchTime ) );
req.setOwner(id);
req.setName(caseName);
req.setAttributes( reqdef, reqArgs );
// Schedule the work flow via the request manager.
RequestManager.addRequest(context, req);
Please see the attached example files for more details. When run from the console the rule produces no output unless there is an exception during execution.
In the log file for the application server you an see the following messages from the workflow when it gets launched:
Starting workflow Hello World Example
Variable initialized with launch input: identityName
--> john.doe
Variable initialized with launch input: exampleArgument
--> CN=john.doe,DC=corp,DC=acme,DC=com
Initial case variables:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Attributes PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Attributes>
<Map>
<entry key="exampleArgument" value="CN=john.doe,DC=corp,DC=acme,DC=com"/>
<entry key="identityName" value="john.doe"/>
<entry key="launcher" value="RequestHandler"/>
<entry key="maxThreads" value="12"/>
<entry key="requestDefinition" value="Workflow Request"/>
<entry key="requestName" value="Run 'Hello World Example' for: john.doe(1412087864210)"/>
<entry key="sessionOwner" value="RequestHandler"/>
<entry key="trace" value="true"/>
<entry key="transient" value="false"/>
</Map>
</Attributes>
Starting step Start
Ending step Start
Starting step Middle
2014-09-30 09:37:45,685 DEBUG Thread-8 services.workflow.helloWorld.example:? - Hello World! id:john.doe arg:CN=john.doe,DC=corp,DC=acme,DC=com
Ending step Middle
Starting step End
Ending step End
Ending workflow Hello World Example
Starting in IdentityIQ releases 6.4 and newer, this feature can be exploited to ensure that specific workflow code runs on a specific application server. This mechanism bypasses the concept of "restricted" request processors that was introduced in these versions of IdentityIQ. Request objects can be targeted to a specific Request Scheduler host by specifying the case-sensitive host name of the application server host to run the request on. An example of that API looks like the following:
Request req = new Request();
// ....
// Specify the request get handled by a specific target host.
req.setHost("iiq-ui-server-01.acme.com");
// ...
// continue submitting the request as shown in the example above.
You're welcome. I'm happy to hear this KB article from 3.5 years ago is still valuable!
Cheers,
--Adam
getting a warning with the code, how can this be resolved ...
WARN sailpoint.persistence.HQLFilterVisitor:960 - Filter.ignoreCase used with a case sensitive index: Request name
There's another similar question. You may want to follow that - sailpoint.persistence.HQLFilterVisitor:960 - Filter.ignoreCase used with a case sensitive index: Req...
Very helpful
Hey adam.hampton,
Thanks for this sharing this helpful topic. I have some quarries regarding request processor. As per the above mentioned code we have to save the request object using Request Manager class,
// Schedule the work flow via the request manager.
RequestManager.addRequest(context, req);
But I have seen that even if we save the Request object using context it's getting launch from the request processor.
context.saveObject(req);
context.saveTransction();
So I was wondering what is difference between this two approaches. Seems like both of the code snippets are doing the same thing. What is the best approach for doing this? It would be very helpful if you could clarify this behavior of IdentityIQ.
Regards,
Ankan
This works fine for me. But is there a way to see the Workflows being queued in the Task Result tab? To me they seem invisible in the background and only show up in Task Result after they're already done.
This is a great sample. I see an auditing problem in my environment though. After this workflow runs, my taskresult and request show launcher as "RequestHandler". How do I get the actual "launcher" to be shown?
<TaskResult completionStatus="Success" id="8a86ca2b7084f5220170b281b039006d" launched="1583542801727" launcher="RequestHandler" modified="1583543129349" name="Scheduled Termination for 8a86cad266ae81960166e4d129331607 ( launched 1583542800000)" targetClass="Identity" targetId="8a86cad266ae81960166e4d129331607" targetName="testid" type="Workflow">
The code is supposed to through an exception if no requesterid is there, but all the taskresults and the request still show RequestHandler as the launcher.
Thanks.
Pasha
7.3p3
This line will cause issues with IdentityIQ8.1
wfArgs.put("workflow", eventWorkflow.getId());
they seem to accept workflow.name only with 8.1
I ran into the same issue as @martin_dehn.
You'll see an error like this in your logs:
2020-12-30T18:11:18,194 ERROR Thread-4264 sailpoint.request.WorkflowRequestExecutor:115 - Unable to launch workflow : ca1e161376911fe78176b556cc2e3fc4
This doesn't work in 8.1:
wfArgs.put("workflow", eventWorkflow.getId());
Use this instead:
wfArgs.put("workflow", eventWorkflow.getName());
Don't forget that you might have Request objects that are scheduled and need to be updated.
I wrote a small rule that updated all of our inflight request objects by converting workflow IDs to workflow names.
Hi All,
I can schedule future workflow using the above mentioned example but I need to retry 2 times triggering the same workflow with some condition. How can I do so. I have used the following code to add retryInterval and retrycount but is not working. Workflow is removed from the RequestList after 1st trigger.
Request req = new Request();
RequestDefinition reqdef = context.getObject(RequestDefinition.class, "Workflow Request");
req.setDefinition(reqdef);
req.setEventDate( new Date( launchTime ) );
req.setOwner(id);
req.setName(caseName);
//Set another retry after 30 mins interval if initial 90 minutes is not sufficient for azure sync
req.setRetryCount(2);
req.setRetryInterval(60);
req.setAttributes( reqdef, reqArgs );
// Schedule the work flow via the request manager.
RequestManager.addRequest(context, req);
log.error("Workflow Scheduled "+workflowName+" at "+launchTime);
return true;
Generated Scheduled Workflow Request:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Request PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Request created="1612942286619" id="059d9005778a53e601778ada1f1b1d32" name="Run 'Test_Workflow_Update' for: identityName(1612942586564)" nextLaunch="1612942886564" retryCount="1" retryInterval="300000">
<Attributes>
<Map>
<entry key="getDn" value="dn value"/>
<entry key="identityName" value="identityName"/>
<entry key="requestDefinition" value="Workflow Request"/>
<entry key="requestName" value="Run 'Test_Workflow_Update' for: identityName(1612942586564)"/>
<entry key="workflow" value="059d900577812a550177822714613f93"/>
</Map>
</Attributes>
<Definition>
<Reference class="sailpoint.object.RequestDefinition" id="058f2c1953be2b540153be2bce4d00e8" name="Workflow Request"/>
</Definition>
<Owner>
<Reference class="sailpoint.object.Identity" id="058f2c1953be2b540153be2bc5ee00cb" name="spadmin"/>
</Owner>
</Request>
Can anyone please help me on that?
Thanks,
Suman