IIQ allows you to run any Rule as a Task via the TaskDefintion called “Run Rule”. This is fine for most use cases, but in our customization, we have found that we want to run a Rule on all of our Identities or ManagedAttributes. In these cases, a single partitioned solution is not practical even if it is multithreaded by convention means. To this end, I have developed a new TaskDefinition type “Partitioned Rule”. This TaskDefinition takes a rule, SailPointObject class name, and a few other parameters to partition your objects and get reasonable running time for rules that need to run on all of your objects.
The specific motivation for making this TaskDefinition, and its infrastructure, was to fulfill a business requirement to trigger PolicyViolations on ManagedAttributes (AD Groups). As PolicyViolations are set up today, they only run on Identities. In my tinkering found how I could use the existing advance Policy template to take a ManagedAttribute and create the PolicyViolation but we lost the inbuilt partitioning granted to us by using an Identity refresh. This was simply unacceptable; It would take over 12 hours to chug through all of the objects. If we used the Rule, even if it were multi threaded, it would have taken over 8 hrs. The solution was to create this “Partitioned Rule” TaskDefinition. On implementation, all the objects are assigned to a partition and executed individually, similar to how Identity refresh works.
We use the SSB (Services Standard Build) for our build and having an understanding of how to customize IIQ using the SSB will be helpful. The main reason is that I use several Java files to implement this infrastructure of “Partitioned Rule”. If anyone out there wants to make, a purely BeanShell version feel free to reply to this thread or message me so we can get the code out there.
Essentially you simply need to download the code, put it into your source, build, deploy, and import. Double check your package locations match between your Java files and BeanShell. Once imported you should be able to go to the Task window, click the “New Task” in the upper right, scroll down to “Partitioned Rule” and start filling out the fields.
“objectClassName” is the name of the SailPointObject that you want to iterate over. This is slightly technical, but in the Identity refresh case, you are iteration over “Identity”. In my example above I am iteration over “ManagedAttribute”. Technically you can iterate over any SailPointObject like TaskResult, Policy, Audit, and much more.
“rule” is the Rule that you want to run on each object. The thing to note here is that you are running this rule on every object one at a time, as such, your Rule should expect a single object to act on. You do not want your Rule to expect to be running on a list of objects.
“rConfig” is the config that is passed to the Rule that you supplied above. You don’t need to supply the SailPointObject or TaskDefinition, “Partitioned Rule” does this for you. But if your Rule has other configurations you wish to supply this is where you would do it. We use this field for maxRetries and timeout values. If you have no config for your Rule, you can leave this blank.
“debugMode” is a safety flag that I added for our use case. “Partitioned Rule” only passes this value along to your Rule. If you choose not to implement “debugMode” in your Rule, then you can leave this field blank. If you do implement it, you should supply the string “true” or “false”. 6.4p4 had a weird quirk with the boolean check make that did not behave as expected if you uncheck the box. I have not tried in 7.1 yet.
“partitionSize” is the size that you want each partition to be. I have a limiter in the code that if you go over 100 partitions, it instantly fails. This is to avoid accidentally supplying a small number and getting a huge number of partitions bring your task to a crawl.
If you have any questions feel free to reply to this thread, I’ll do my best to get back to you. And, if you are interested in how I finished solving the Policy problem above let me know, and I’ll work on a new article for an article addressing it.
I have been trying to implement a version of this, but calling a workflow instead of a rule. It seems the RequestManager.addRequest part of the 'runPartitionedReadyRule' isn't triggering the requests and therefore the custom executor I wrote.. I tried to do RequestManager.scheduleRequest.. but I'm not sure how to get the executor from the RequestDefinition to actually trigger? / the requests to be fired off.
I would use the task to kick off the workflow. You can kickoff a workflow from the task by using something like the code below. This is bascailly how I do our custom policy violations.
private static void kickOffWorkflow(Workflow workflow, String caseName, SailPointContext context) throws GeneralException {
WorkflowLaunch workflowLaunch = new WorkflowLaunch();
workflowLaunch.setWorkflow(workflow);
workflowLaunch.setCaseName(caseName);
CommonUtil.logXml(logger, workflowLaunch);
Workflower workflower = new Workflower(context);
workflower.launch(workflowLaunch);
logger.info("workflower: " + workflower.toString());
}
Dear
can you provide exmpel of the Rule I have to create to us Partitioned Rule task ...
Hi jotorres,
I have a doubt regarding the flow. Can you please let me know that how the execute method of RuleRequestExecuter class is getting called?
Regards,
Enakshi
"RuleRequestExecutor.execute(...)" is called because the TaskDefinition "Partitioned Rule Definition" uses has executor="com.exp.custom.request.RuleRequestExecutor"
Hey Tony,
I think that the standard identity refresh task (with a refresh rule) allows to achieve the same result.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE TaskDefinition PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<TaskDefinition name="Your Task Name" resultAction="Rename" subType="task_item_type_identity" type="Identity">
<Attributes>
<Map>
<entry key="filterGroups" value="Custom Population"/>
<entry key="refreshRule" value="Custom Rule name"/>
<entry key="refreshThreads" value="10"/>
all other attributes are 'false'
<Parent>
<Reference class="sailpoint.object.TaskDefinition" name="Identity Refresh"/>
</Parent>
</TaskDefinition>
Regards
Serge
Thanks jotorres. But I still have few confusions.
Now the question is how suddenly the Request Definition starts executing?
Also, we have seen that in execute(...), the requests are added using RequestManager.addRequest(). Is there any connection with Request Definition?
Regards,
Enakshi
My understanding is that identity refresh will only run on identities. This rule can run on any sailpointObject. For us, we mostly use it for ManagedAttributes.
Your ordering is correct I think you are missing some context. PartitionedRuleContainer class creates a one Request object per partition and the Request references the RequestDefinition. Requests are picked up and run automatically by IIQ if start conditions are met, like blocking requests are finished and resources are available.
To slightly edit your steps
1. There is one Task Definition "Partitioned Rule". From this Task Definition, Rule "Partitioned Rule Container" is called.
2. Then runPartitionedReadyRule(...) of PartitionedRuleContainer class is called and Requests are created in IIQ and the partition TaskResults are attached to the orginal TaskResults. At this point Rule "Partitioned Rule Container" is completed but the TaskResult still shows as pending because all the partitions TaskResults are not complete yet.
3. IIQ sees the Request, checks if it's ready, and runs execute(...) of the executor class that is assigned to the Request, in this case RuleRequestExecutor. As a part of this running RuleRequestExecutor updates its partitioned TaskResult with the completion status.
4. While #3 is running the original TaskResult, the one you can see from the UI, is being updated by the running Requests. When the last one is completed This TaskResult is updated with completion info like time and status.
Thanks jotorres for the detailed explanation.
Regards,
Enakshi