Hello everyone, I would like to ask about the Identity Provisioning Policy. Is there any way to skip it when creating an identity using a Batch Request?
Currently, I created a new workflow for the Batch Request that only shows the logs. However, after I created a new Identity Provisioning Policy, I can no longer create a new identity with the same CSV file, because it checks the headers and cannot find them since they don’t match the Identity Provisioning Policy.
How can I handle this?
This is my workflow — it has just one step for showing the logs.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow configForm="Provisioning Workflow Config Form" created="1755577523742" explicitTransitions="true" handler="sailpoint.api.StandardWorkflowHandler" id="7f00010198bb1a8d8198c0931e1e0471" libraries="Identity,BatchRequest" modified="1756441713539" monitored="true" name="LCM Create and Update test with Batch" significantModified="1756441713539" taskType="LCM" type="LCMIdentity">
<Variable input="true" name="identityName">
<Description>The name of the identity we&#39;re supposed to update.</Description>
</Variable>
<Variable initializer="script:(identityDisplayName != void) ? identityDisplayName : resolveDisplayName(identityName)" input="true" name="identityDisplayName">
<Description>The displayName of the identity being updated.
Query for this using a projection query and fall back to the name.</Description>
</Variable>
<Variable initializer="false" input="true" name="endOnManualWorkItems">
<Description>Option to skip requests with manual work items.</Description>
</Variable>
<Variable initializer="false" input="true" name="endOnProvisioningForms">
<Description>Option to skip requests with provisioning forms.</Description>
</Variable>
<Variable input="true" name="batchRequestItemId">
<Description>Used by the batch interface to record back individual request item status. The specific item id for the individual request in the batch file.</Description>
</Variable>
<Variable input="true" name="plan">
<Description>The provisioning plan ready to execute.</Description>
</Variable>
<Variable input="true" name="flow">
<Description>The name of the LCM flow that launched this workflow.
This is one of these two values:
IdentityCreateRequest
IdentityEditRequest</Description>
</Variable>
<Variable editable="true" name="optimisticProvisioning">
<Description>Set to true to enable optimistic provisioning. This will cause
changes to the entitlements compiled from role assignments to be
applied immediately to the identity cube rather than waiting
for the next refresh/reaggregation after the provisioning system
completes the request.</Description>
</Variable>
<Variable editable="true" initializer="true" name="foregroundProvisioning">
<Description>Normally provisioning is done in a step that uses the &quot;background&quot;
option to force the workflow to be suspend and be resumed in a
background task thread. This prevents the browser session from
hanging since provision can sometimes take a long time. For demos
and testing it can be better to do this in the foreground so that
provisioning will have been performed when control is returned to the
user. This prevents having to run the Perform Maintenance task to
see the results of the request.</Description>
</Variable>
<Variable editable="true" initializer="false" name="doRefresh">
<Description>Set to true to cause an identity refresh after the changes in the plan
have been provisioned. This is normally off, you might want this on
if you want modification of identity or link attributes to result in
an immediate re-evaluation of assigned and detected roles.</Description>
</Variable>
<Variable initializer="Normal" input="true" name="workItemPriority">
<Description>The String version of a WorkItem.Priority. This variable is
used to set the priority on all of the workitems generated
as part of this workflow and also set on the IdentityRequest
object.</Description>
</Variable>
<Variable initializer="user, requester" input="true" name="notificationScheme">
<Description>A string that specifies who should be notified when the request has been complete.
The value can be null or a csv of one or more of the following options.
none or null
disable notifications
user
Identity that is being update will be notified.
manager
The manager of the Identity that is being updated will be notified.
requester
The person that has requested the update will be notified.</Description>
</Variable>
<Variable initializer="LCM User Notification" input="true" name="userEmailTemplate">
<Description>The email template to use for user notification.</Description>
</Variable>
<Variable initializer="LCM Requester Notification" input="true" name="requesterEmailTemplate">
<Description>The email template to use for requester notification.</Description>
</Variable>
<Variable initializer="LCM Manager Notification" input="true" name="managerEmailTemplate">
<Description>The email template to use for manager notification.</Description>
</Variable>
<Variable input="true" name="securityOfficerEmailTemplate">
<Description>The email template to use for security officer notification.</Description>
</Variable>
<Variable initializer="serial" input="true" name="approvalMode">
<Description>A string that specifies how we should handle the approvals.
By default this is serial since most of these request with
the exception of manager transfers will have only one approver.
parallel
Approvals are processed concurrently and there must be consensus,
we wait for all approvers to approve. The first approver that
rejects terminates the entire approval.
parallelPoll
Approvals are processed concurrently but consensus is not required.
All approvals will be processed, we don&#39;t stop if there are any
rejections.
serial
Approvals are processed one at a time and there must be consensus.
The first approver that rejects terminates the entire approval.
serialPoll
Approvals are processed in order but consensus is not required.
All approvals will be processed, we don&#39;t stop if there are any
rejections. In effect we are &quot;taking a poll&quot; of the approvers.
any
Approvals are processed concurrently, the first approver to
respond makes the decision for the group.</Description>
</Variable>
<Variable initializer="manager, newManager" input="true" name="approvalScheme">
<Description>A String that specifies how approvals should be generated for
this workflow there are three built-in modes
none - disabled approvals
manager - The user&#39;s current manager will get approvals
newManager - The newly assigned manager will get approvals when
manager transfers occur. Otherwise the user&#39;s manager
current manager will be the approver.</Description>
</Variable>
<Variable initializer="spadmin" input="true" name="fallbackApprover">
<Description>A String that specifies the name of the Identity that will
be assigned any approvals where the owner of the approver
can&#39;t be resolved. Example if the scheme is &quot;owner&quot; and the
application doesn&#39;t specify and owner.</Description>
</Variable>
<Variable initializer="LCM Identity Update Approval" input="true" name="approvalEmailTemplate">
<Description>The email template to use for approval notifications.</Description>
</Variable>
<Variable input="true" name="securityOfficerName">
<Description>The name of the identity that will be sent approvals
during security officer approvals.</Description>
</Variable>
<Variable initializer="continue" input="true" name="policyScheme">
<Description>A String that specifies how policy checks effect the overall
process.
none - disabled policy checking
fail - fail and exit the workflow if any policy violations are found
continue - continue if policy violations are found</Description>
</Variable>
<Variable input="true" name="ticketManagementApplication">
<Description>Name of the application that can handle ticket requests.
When non-null the Manage Ticket Steps will be visited to open
tickets during the workflow lifecycle.</Description>
</Variable>
<Variable name="ticketId">
<Description>The id of the ticket that is generated by the ticketingManagementApplication.
This is typically generated on the &quot;open&quot; call, and then used in subsequent
calls. It is also stored on the IdentityRequest object under the
externalTicketId variable.</Description>
</Variable>
<Variable input="true" name="policiesToCheck">
<Description>A List of policies that should be checked. If this list is
empty all violations will be checked. Used in combination
with policyScheme.</Description>
</Variable>
<Variable name="policyViolations">
<Description>List of policy violations that were found during our initial policy scan.
This list is passed into each work item so the approvers can see
pending violations.</Description>
</Variable>
<Variable initializer="LCM" input="true" name="source">
<Description>String version of sailpoint.object.Source to indicate
where the request originated. Defaults to LCM.</Description>
</Variable>
<Variable name="approvalSet">
<Description>This attributes is set during the &quot;Build Approval Set&quot; step,
which builds this list by going through the ProvisioningPlan
to build the line items that need to be approved,
This variable includes all ApprovalItems that are part of
the request process and is updated during the AfterScript
of the approval process by assimilating the decisions
and comments from the Approvals copy of the ApprovalItem.</Description>
</Variable>
<Variable initializer="false" name="trace">
<Description>Used for debugging this workflow and when set to true trace
will be sent to stdout.</Description>
</Variable>
<Variable name="project">
<Description>ProvisioningProject which is just a compiled version of the ProvisioningPlan.</Description>
</Variable>
<Variable name="approvalForm">
<Description>The form that is generated based on the changes that were made. This
form will be sent into the workitem and allow editing of the
requested values during the approval process. This is generated
in &quot;Build Approval Form&quot; based on the passed-in plan.</Description>
</Variable>
<Variable name="identityRequestId" output="true">
<Description>The sequence id of the Identity request object which is stored in
the name field of the identity request.</Description>
</Variable>
<Variable input="true" name="approverElectronicSignature">
<Description>The name of the electronic signature object that should be used when workitems
are completed by the batch approver when performing batch operations.</Description>
</Variable>
<RuleLibraries>
<Reference class="sailpoint.object.Rule" id="7f00010196161e778196165f30ef018a" name="Approval Library"/>
<Reference class="sailpoint.object.Rule" id="7f00010196161e778196165f39b2019d" name="LCM Workflow Library"/>
</RuleLibraries>
<Step icon="Start" monitored="true" name="Start" posX="14" posY="12">
<Transition to="Build Approval Set"/>
</Step>
<Step action="rule:LCM Build Identity ApprovalSet" icon="Task" monitored="true" name="Build Approval Set" posX="97" posY="25" resultVariable="approvalSet">
<Description>
The rule will go through the plan and build an approvalItem for each AccountRequest,
typically there is just one for the IIQ application requests.
The rule will also annotate the plan with the previousValues so that they
can be assimilated onto the Form that is build, which is used during the approval
process for edits.
Because this has some special handling keep it here in the workflow
and pass the approval set into the initialization process.
</Description>
<Transition to="Initialize"/>
</Step>
<Step icon="Task" monitored="true" name="Initialize" posX="183" posY="12">
<Arg name="formTemplate"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="enableRetryRequest"/>
<Arg name="allowRequestsWithViolations"/>
<Arg name="endOnManualWorkItems" value="ref:endOnManualWorkItems"/>
<Arg name="policiesToCheck" value="ref:policiesToCheck"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="workItemComments"/>
<Arg name="source" value="ref:source"/>
<Arg name="violationReviewDecision"/>
<Arg name="policyScheme" value="ref:policyScheme"/>
<Arg name="identityDisplayName" value="ref:identityDisplayName"/>
<Arg name="requireViolationReviewComments"/>
<Arg name="identityRequest"/>
<Arg name="trace" value="ref:trace"/>
<Arg name="batchRequestItemId" value="ref:batchRequestItemId"/>
<Arg name="enableApprovalRecommendations"/>
<Arg name="asyncCacheRefresh"/>
<Arg name="endOnProvisioningForms" value="ref:endOnProvisioningForms"/>
<Arg name="optimisticProvisioning" value="false"/>
<Arg name="plan" value="ref:plan"/>
<Arg name="flow" value="ref:flow"/>
<Arg name="launcher" value="ref:launcher"/>
<Description>
Call the standard subprocess to initialize the request, this includes
auditing, building the approvalset, compiling the plan into
a project and checking policy violations.
</Description>
<Return name="policyViolations" to="policyViolations"/>
<Return name="identityRequestId" to="identityRequestId"/>
<Return name="project" to="project"/>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196166021670316" name="Identity Request Initialize"/>
</WorkflowRef>
<Transition to="Exit On Manual Work Items" when="script:(isTrue(endOnManualWorkItems) && (project.getUnmanagedPlan() != null))"/>
<Transition to="Exit On Provisioning Form" when="script:(isTrue(endOnProvisioningForms) && (project.hasQuestions()))"/>
<Transition to="Exit On Policy Violation" when="script:((size(policyViolations) > 0 ) && (policyScheme.equals("fail")))"/>
<Transition to="Disable Provisioning"/>
</Step>
<Step condition="script:(ticketManagementApplication != null)" icon="Task" monitored="true" name="Create Ticket" posX="435" posY="7">
<Arg name="trace" value="ref:trace"/>
<Arg name="ticketManagementApplication" value="ref:ticketManagementApplication"/>
<Arg name="project" value="ref:project"/>
<Arg name="action" value="open"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="source" value="ref:source"/>
<Arg name="ticketProject"/>
<Arg name="ticketDataGenerationRule"/>
<Arg name="ticketPlan"/>
<Description>
Call a subprocess to create a ticket in the ticketManagementApplication is non-null.
You can specify a specific 'ticketDataGenerationRule' here or you can also specify
it on the application. It'll be read from the argument first and fall back to the '
application config.
</Description>
<Return name="ticketId" to="externalTicketId"/>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196165f34f90191" name="Manage Ticket"/>
</WorkflowRef>
<Transition to="Approve"/>
</Step>
<Step icon="Task" monitored="true" name="Approve" posX="488" posY="8">
<Arg name="formTemplate"/>
<Arg name="approvalScheme" value="ref:approvalScheme"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="fallbackApprover" value="ref:fallbackApprover"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="approvers"/>
<Arg name="policyViolations" value="ref:policyViolations"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="workItemComments"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="identityDisplayName" value="ref:identityDisplayName"/>
<Arg name="approvalMode" value="ref:approvalMode"/>
<Arg name="trace" value="ref:trace"/>
<Arg name="approvalForm"/>
<Arg name="requireCommentsForDenial" value="ref:requireCommentsForDenial"/>
<Arg name="approvalEmailTemplate" value="ref:approvalEmailTemplate"/>
<Arg name="securityOfficerName" value="ref:securityOfficerName"/>
<Arg name="approverElectronicSignature" value="ref:approverElectronicSignature"/>
<Arg name="plan" value="ref:plan"/>
<Arg name="requireCommentsForApproval" value="ref:requireCommentsForApproval"/>
<Arg name="launcher" value="ref:launcher"/>
<Description>
Call the standard subprocess that will handle the built-in
owner, manager and security officer approval schemes.
</Description>
<Return name="approvalSet"/>
<Return name="plan"/>
<Return name="workItemComments"/>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196166022ec031b" name="Identity Request Approve Identity Changes"/>
</WorkflowRef>
<Transition to="Update Ticket Post Approval"/>
</Step>
<Step condition="script:(ticketManagementApplication != null)" icon="Task" monitored="true" name="Update Ticket Post Approval" posX="599" posY="8">
<Arg name="trace" value="ref:trace"/>
<Arg name="ticketManagementApplication" value="ref:ticketManagementApplication"/>
<Arg name="project" value="ref:project"/>
<Arg name="action" value="postApproval"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="source" value="ref:source"/>
<Arg name="ticketProject"/>
<Arg name="ticketId"/>
<Arg name="ticketDataGenerationRule"/>
<Arg name="ticketPlan"/>
<Description>
Call a subprocess to update the ticket in the ticketManagementApplication is non-null.
You can specify a specific 'ticketDataGenerationRule' here or you can also specify
it on the application. It'll be read from the argument first and fall back to the '
application config.
</Description>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196165f34f90191" name="Manage Ticket"/>
</WorkflowRef>
<Transition to="Process Approval Decisions"/>
</Step>
<Step action="call:processPlanApprovalDecisions" icon="Task" monitored="true" name="Process Approval Decisions" posX="764" posY="9" resultVariable="plan">
<Arg name="disableAudit" value="true"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="plan" value="ref:plan"/>
<Transition to="Notify" when="script:approvalSet.hasRejected()"/>
<Transition to="Recompile Project"/>
</Step>
<Step action="call:recompileProvisioningProject" icon="Task" monitored="true" name="Recompile Project" posX="913" posY="5" resultVariable="project">
<Arg name="requester" value="ref:launcher"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="project" value="ref:project"/>
<Arg name="source" value="ref:source"/>
<Arg name="optimisticProvisioning" value="ref:optimisticProvisioning"/>
<Arg name="plan" value="ref:plan"/>
<Description>Recompile the provisioning project with the plan.
The plan may contain modifications from the original plan if
it was modified during the approval process.
If you need to pass in provisioner options like "noFiltering"
or "noRoleDeprovisioning" you must pass them as explicit
arguments to the call.
The evaluation options "requester" and "source" are commonly
set here.
You can also pass things into the Template and Field scripts by
defining Args in this step.</Description>
<Transition to="Provision"/>
</Step>
<Step icon="Task" monitored="true" name="Provision" posX="1014" posY="6">
<Arg name="formTemplate" value="Identity Update"/>
<Arg name="approvalScheme" value="ref:approvalScheme"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="fallbackApprover" value="ref:fallbackApprover"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="manualActionsEmailTemplate" value="Pending Manual Changes"/>
<Arg name="workItemComments"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="project" value="ref:project"/>
<Arg name="policyViolations" value="ref:policyViolations"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="policyScheme" value="ref:policyScheme"/>
<Arg name="splitProvisioning"/>
<Arg name="saveUnmanagedPlan"/>
<Arg name="foregroundProvisioning" value="ref:foregroundProvisioning"/>
<Arg name="noTriggers"/>
<Arg name="identityDisplayName" value="ref:identityDisplayName"/>
<Arg name="trace" value="ref:trace"/>
<Arg name="saveUnmanagedPlan_WithProjectArgument"/>
<Arg name="recompile" value="false"/>
<Arg name="optimisticProvisioning" value="ref:optimisticProvisioning"/>
<Arg name="plan"/>
<Arg name="flow" value="ref:flow"/>
<Arg name="launcher" value="ref:launcher"/>
<Description>
Call the standard subprocess that will process the
approval decisions and do provisioning. This
includes calling any configured provisioning
connectors and building manual actions.
</Description>
<Return name="project" to="project"/>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e7781961660224c0319" name="Identity Request Provision"/>
</WorkflowRef>
<Transition to="Post Provision"/>
</Step>
<Step action="script:approvalSet.setAllProvisioned();" icon="Task" monitored="true" name="Post Provision" posX="1110" posY="7">
<Description>
Mark all of the items in the approvalset provisioned since these
are all synchronous activities.
</Description>
<Transition to="Update Ticket Post Provision"/>
</Step>
<Step condition="script:(ticketManagementApplication != null)" icon="Task" monitored="true" name="Update Ticket Post Provision" posX="1111" posY="115">
<Arg name="trace" value="ref:trace"/>
<Arg name="ticketManagementApplication" value="ref:ticketManagementApplication"/>
<Arg name="project" value="ref:project"/>
<Arg name="action" value="postProvisioning"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="source" value="ref:source"/>
<Arg name="ticketProject"/>
<Arg name="ticketId"/>
<Arg name="ticketDataGenerationRule"/>
<Arg name="ticketPlan"/>
<Description>
Call a subprocess to update the ticket in the ticketManagementApplication is non-null.
You can specify a specific 'ticketDataGenerationRule' here or you can also specify
it on the application. It'll be read from the argument first and fall back to the '
application config.
</Description>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196165f34f90191" name="Manage Ticket"/>
</WorkflowRef>
<Transition to="Refresh Identity"/>
</Step>
<Step action="call:refreshIdentity" condition="ref:doRefresh" icon="Task" monitored="true" name="Refresh Identity" posX="954" posY="114">
<Arg name="identityName" value="ref:identityName"/>
<Arg name="provision" value="true"/>
<Arg name="correlateEntitlements" value="true"/>
<Arg name="synchronizeAttributes" value="true"/>
<Description>
Add arguments as necessary to enable refresh features. Typically you only want this
to correlate roles and possibly provision if we notice new assigned roles.
Note that provisioning will be done in the Identity Refresh workflow so if there
are any provisioning forms to display we won't feed them directly to the
current user, they'll have to return to the inbox.
</Description>
<Transition to="Notify"/>
</Step>
<Step icon="Task" monitored="true" name="Notify" posX="830" posY="114">
<Arg name="approvalScheme" value="ref:approvalScheme"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="notificationScheme" value="ref:notificationScheme"/>
<Arg name="userEmailTemplate" value="ref:userEmailTemplate"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="policyViolations" value="ref:policyViolations"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="source" value="ref:source"/>
<Arg name="identityDisplayName" value="ref:identityDisplayName"/>
<Arg name="trace" value="ref:trace"/>
<Arg name="requesterEmailTemplate" value="ref:requesterEmailTemplate"/>
<Arg name="securityOfficerName" value="ref:securityOfficerName"/>
<Arg name="securityOfficerEmailTemplate" value="ref:securityOfficerEmailTemplate"/>
<Arg name="plan" value="ref:plan"/>
<Arg name="flow" value="ref:flow"/>
<Arg name="managerEmailTemplate" value="ref:managerEmailTemplate"/>
<Arg name="launcher" value="ref:launcher"/>
<Description>
Call the standard subprocess that will notify the various
actors based on notification scheme.
</Description>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196166021f50318" name="Identity Request Notify"/>
</WorkflowRef>
<Transition to="end"/>
</Step>
<Step action="call:addMessage" monitored="true" name="Exit On Policy Violation" posX="250" posY="225">
<Arg name="message" value="Failed due to policy violation(s)"/>
<Arg name="type" value="Error"/>
<Transition to="end"/>
</Step>
<Step action="call:addMessage" monitored="true" name="Exit On Manual Work Items" posX="280" posY="141">
<Arg name="message" value="Failed due to manual work item(s)"/>
<Arg name="type" value="Error"/>
<Transition to="end"/>
</Step>
<Step action="call:addMessage" monitored="true" name="Exit On Provisioning Form" posX="352" posY="105">
<Arg name="message" value="Failed due to provisioning form"/>
<Arg name="type" value="Error"/>
<Transition to="end"/>
</Step>
<Step catches="complete" icon="Catches" monitored="true" name="Finalize" posX="759" posY="284">
<Arg name="trace" value="ref:trace"/>
<Arg name="batchRequestItemId" value="ref:batchRequestItemId"/>
<Arg name="identityName" value="ref:identityName"/>
<Arg name="autoVerifyIdentityRequest"/>
<Arg name="approvalSet" value="ref:approvalSet"/>
<Arg name="ticketManagementApplication" value="ref:ticketManagementApplication"/>
<Arg name="project" value="ref:project"/>
<Arg name="workItemPriority" value="ref:workItemPriority"/>
<Arg name="identityRequestId" value="ref:identityRequestId"/>
<Arg name="ticketDataGenerationRule"/>
<Description>
Call the standard subprocess that can audit/finalize the request.
</Description>
<WorkflowRef>
<Reference class="sailpoint.object.Workflow" id="7f00010196161e778196166022b7031a" name="Identity Request Finalize"/>
</WorkflowRef>
<Transition to="end"/>
</Step>
<Step icon="Stop" monitored="true" name="end" posX="830" posY="225"/>
<Step icon="Default" monitored="true" name="Generic Step" posX="375" posY="11" resultVariable="isCreate">
<Arg name="plan" value="ref:plan"/>
<Script>
<Source>
import sailpoint.object.BatchRequestItem;
import sailpoint.object.BatchRequest;
import java.util.regex.Pattern;
import sailpoint.object.AuditEvent;
import sailpoint.server.Auditor;
AuditEvent ae = new AuditEvent();
System.out.println("=================================================");
boolean isValid = true; // assume valid unless a check fails
String header = null;
if (batchRequestItemId != null) {
BatchRequestItem batchRequestItem = context.getObjectById(BatchRequestItem.class, batchRequestItemId);
if (batchRequestItem != null) {
BatchRequest batchRequest = batchRequestItem.getBatchRequest();
header = (batchRequest != null) ? batchRequest.getHeader() : null;
// Request data from CSV row (comma-separated string)
String requestData = batchRequestItem.getRequestData();
System.out.println("Request Data --- " + requestData);
System.out.println("Header: " + header);
// Split into columns
String[] headers = (header != null) ? header.split(",") : new String[0];
String[] values = (requestData != null) ? requestData.split(",") : new String[0];
// Perform validation checks
for (int i = 0; i < headers.length && i < values.length; i++) {
String column = headers[i].trim();
String value = values[i].trim();
if ("email".equalsIgnoreCase(column)) {
boolean validEmail = Pattern.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$", value);
if (!validEmail) {
System.out.println("X - Invalid email format: " + value);
isValid = false; // mark invalid
} else {
System.out.println("/ - Valid email: " + value);
}
}
if ("name".equalsIgnoreCase(column)) {
if (value.length() < 3 || value.length() > 50) {
System.out.println("X - Invalid name length: " + value);
isValid = false;
} else {
System.out.println("/ - Valid name length: " + value);
}
}
if ("operation".equalsIgnoreCase(column)) {
if (!"CreateIdentity".equalsIgnoreCase(value)) {
System.out.println("X - Invalid operation: " + value);
isValid = false;
} else {
System.out.println("/ - Valid operation: " + value);
}
}
}
}
}
// Log final result
if (isValid) {
ae.setSource(launcher);
ae.setTarget(identityDisplayName);
ae.setApplication("Test");
ae.setAction(emailSent);
ae.setServerHost(Util.getHostName());
Auditor.log(ae);
System.out.println("/ - BatchRequestItem validation PASSED");
} else {
ae.setSource(launcher);
ae.setTarget(identityDisplayName);
ae.setApplication("Test");
ae.setAction("emailSent");
ae.setServerHost(Util.getHostName());
Auditor.log(ae);
System.out.println("X - BatchRequestItem validation FAILED");
}
System.out.println("=================================================");
context.commitTransaction();
// Return result
return isValid;
</Source>
</Script>
<Transition to="end" when="return isCreate == false;"/>
<Transition to="Create Ticket" when="return isCreate == true;"/>
</Step>
<Step icon="Default" monitored="true" name="Generic Step (1)" posX="504" posY="83"/>
<Step icon="Default" monitored="true" name="Disable Provisioning" posX="136" posY="139">
<Script>
<Source>
import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningProject;
import sailpoint.workflow.WorkflowContext;
log.info("Skipping provisioning policy validation for batch request");
// Set the skip flag in context
context.put("skipProvisioningPolicy", true);
// ADD THESE LINES - These actually skip the policy!
if (project != null) {
project.setProvisioningPolicy(null); // THIS removes the policy
}
if (plan != null) {
plan.setCheckPolicies(false); // THIS disables policy checking
}
log.info("Successfully configured to skip provisioning policy");
</Source>
</Script>
<Transition to="Generic Step"/>
</Step>
</Workflow>
I am a bit late on catching this. Here’s attached workflow and Test Rule to test the Workflow for your perusal. I have also attached the output of the workflow from tomcat stdout logs. You might need to configure the stdout to capture it.
Here’s a plain-English rundown of the two files attached.
1) Workflow — “LCM Create and Update test with Batch”
Purpose: Run an LCM identity create/update, but when the request comes from BATCH, it skips provisioning policy checks and still proceeds.
How it decides to skip: In Initialize, if source == "BATCH", it goes to Disable Provisioning → Log Vars → the normal flow. Otherwise it just logs and continues normally.
Disable Provisioning step: Removes any policy/form references from the plan arguments (e.g., form, policy, provisioningPolicy), sets skipPolicy = true, and writes to logs.
Log Vars step: Prints the key inputs (identityName, identityDisplayName, flow, source, skipPolicy, batchRequestItemId) and also writes an AuditEvent tagged BatchVars for visibility.
Generic Step (CSV validation): If the request came via Batch, it reads the CSV header and row values and does quick checks:
email looks like an email,
name length is reasonable,
operation equals CreateIdentity.
It logs PASSED/FAILED and only continues if valid.
Rest of the path: Standard LCM subflows—build approvals, approve, recompile, provision, optional refresh, and notify.
Purpose: A super-compatible launcher for the workflow that works across different IIQ builds. It prints your inputs, derives or accepts skipPolicy, launches the workflow using several possible APIs, and writes an AuditEvent with the launch variables.
Inputs it handles:identityName, identityDisplayName, flow, source, batchRequestItemId, and (optionally) skipPolicy. If skipPolicy isn’t provided, it sets it to true when source == "BATCH".
What it prints to stdout: A block with those values (including skipPolicy) before launching, and then Launched workflow case: <id> after launch.
How it launches (compatibility tricks):
Tries several context.startWorkflow(...) / context.launch(...) signatures via reflection,
If that fails, it builds a WorkflowCase directly, attaches the workflow + variables, saves it, and tries to start/queue it using WorkflowEngine via reflection (so it doesn’t hard-depend on specific classes).
Auditing: Logs an event (Application=Test, Action=LauncherVars) with all the launch details in the Attributes map for maximum version compatibility.
In short:
The workflow skips policy when source=BATCH, logs what it’s doing, validates your batch row, and then runs the usual LCM steps.
The rule is a robust launcher that prints the variables (including skipPolicy), launches the workflow using whatever API your IIQ supports, and records the inputs in Audit for easy troubleshooting.