Custom Workflow

Hi team,

I have developed custom workflow which is creating provisioning plan and do provisioning of an application.(fyi,. only for Add and Remove Entitlement)

Using REST API, launching a workflow and its working fine.

FYI, through workflowArgs i am sending username and multiple responsibilies with different startDate and endDate and i am iterating one by one and creating a plan.

If i use LCM provisioning workflow for provisioning, responsibilies getting added correctly but startDate and endDate it not properly provisioning to target(only last responsibility startDate and endDate is getting added for both responsibilies).

I am using Provisioner class to avoid that issue(which is working fine), Is there any way to handle this type issue?

And If any error comes in Application API response, that I want to print in the response which is our workflow Launch API response. If that is possible, let me know how we can do?

Hi @Venkatesh0510 ,

could you share the provisioning plan code from the custom workflow? Also, can you log the plan and share the output here.

can you please share your custom workflow code??

ERP.xml (12.1 KB)

@naveenkumar3 this is my custom workflow

Just gone through with your workflow, can you try below recommendation??

Add proper try-catch around compile/execute and capture the real message

try {
    ProvisioningProject proj = provisioner.compile(provisioningPlan);
    log.error("proj: " + proj.toXml());

    provisioner.execute(proj);

    ProvisioningProject executedProject = provisioner.getProject();

    if (executedProject != null) {
        List projectPlans = executedProject.getPlans();
        if (projectPlans != null) {
            for (Object pObj : projectPlans) {
                ProvisioningPlan p = (ProvisioningPlan) pObj;
                if (p != null && p.getAccountRequests() != null) {
                    for (Object arObj : p.getAccountRequests()) {
                        AccountRequest ar = (AccountRequest) arObj;
                        if (ar.getAttributeRequests() != null) {
                            for (Object attObj : ar.getAttributeRequests()) {
                                AttributeRequest atReq = (AttributeRequest) attObj;
                                ProvisioningResult provResult = atReq.getResult();

                                if (provResult != null && ProvisioningResult.STATUS_FAILED.equals(provResult.getStatus())) {
                                    hasErrors = true;

                                    List provErrors = provResult.getErrors();
                                    if (provErrors != null && !provErrors.isEmpty()) {
                                        StringBuilder sb = new StringBuilder();
                                        for (Object errObj : provErrors) {
                                            String errStr = String.valueOf(errObj);
                                            log.error("Raw Error String: " + errStr);

                                            // keep raw message
                                            sb.append(errStr).append(" | ");

                                            // if API response contains JSON, extract mw_error.message
                                            try {
                                                int start = errStr.indexOf("{");
                                                int end = errStr.lastIndexOf("}") + 1;
                                                if (start >= 0 && end > start) {
                                                    String jsonString = errStr.substring(start, end);
                                                    JSONObject json = new JSONObject(jsonString);

                                                    if (json.has("mw_error")) {
                                                        JSONObject error = json.getJSONObject("mw_error");
                                                        String apiMsg = error.optString("message");
                                                        if (apiMsg != null && apiMsg.length() > 0) {
                                                            sb.append("API Message: ").append(apiMsg).append(" | ");
                                                        }
                                                    }
                                                }
                                            } catch (Exception ex) {
                                                log.error("Failed to parse error JSON", ex);
                                            }
                                        }
                                        errorMess = sb.toString();
                                    } else {
                                        errorMess = "Provisioning failed for responsibility: " + displayName;
                                    }

                                    workflow.put("result", "FAIL");
                                }
                            }
                        }
                    }
                }
            }
        }
    }

} catch (Exception ex) {
    hasErrors = true;
    errorMess = ex.getMessage();

    // if nested API/connector error exists, include it
    Throwable root = ex;
    while (root.getCause() != null) {
        root = root.getCause();
    }
    if (root != null && root.getMessage() != null && !root.getMessage().equals(errorMess)) {
        errorMess = errorMess + " | Root cause: " + root.getMessage();
    }

    log.error("Provisioning execution failed", ex);
    workflow.put("result", "FAIL");
}
  1. can you update if(operation.equalsIgnoreCase(“ADD”)) to if (“Add”.equalsIgnoreCase(operation)) safer against null.

  2. last one Inside loop, if managed attribute is not found: You should immediately skip execution for that responsibility: update the code

if (list == null || list.isEmpty()) {
    hasErrors = true;
    errorMess = "No ManagedAttribute found for: " + displayName;
    workflow.put("result", "FAIL");
    continue;
}

what if we need to show in access request? How we can do that?

Split each responsibility into a separate Access Request item**

  • store startDate and endDate with that item
  • then display those values in:
    • request summary
    • approval page
    • request history

Is your requirement now?? or issue resolved??

@Venkatesh0510 Have you tried passing the plan to LCM Provisioning workflow? It should generate access request and if there are any failures, it should update the provisioningresult object. Once it is updated there, it should be visible on the access request as well and same can be sent to workflow response as well.

Note: Found a fix?Help the community by marking the comment as solution. Feel free to react(:heart:,:+1:, etc.)with an emoji to show your appreciation or message me directly if your problem requires a deeper dive.

Yes, client is asking that actually.

But when i tried passing plan to LCM provisioning step, start date and end date not updating properly.

please suggest how to use LCM provisioning in my workflow.

@Venkatesh0510 Could you please share the code you are using to generate plan and setting start/end date? Also print the plan and share the plan xml as well for review.

@neel193 i have already shared my workflow xml in previous messages.

@Venkatesh0510 Based on your workflow, you are not setting startDate or endDate at the IIQ level, meant you are not setting the sunrise or sunset date.. Instead you are assigning the responsibility and passing expireDate and effectiveDate as attribute requests.

So your requirement is not to set sunrise/sunset dates in IIQ but pass expireDate/effectiveDate for every responsibility assignment? Is this correct assumption?

also, have you tested the code snippet via rule in IIQ? What is the result.

Please print the plan and share it. I didn’t find it your workflow.

LCM workflow merges/overwrites effectiveDate /expireDate at plan level — last one wins. You need to set dates at AttributeRequest level using sunrise and sunset .

Fix 1: Build Provisioning Plan — Build AttributeRequest

Replace below lines
acctReq.add(new AttributeRequest(“entitlements”, ProvisioningPlan.Operation.Add, responsibilityName));
if (startDateVal != null && startDateVal.length() > 0) {
acctReq.add(new AttributeRequest(“effectiveDate”, df.parse(startDateVal)));
}
if (endDateVal != null && endDateVal.length() > 0) {
acctReq.add(new AttributeRequest(“expireDate”, df.parse(endDateVal)));
}

to this

AttributeRequest attrReq = new AttributeRequest(
    "entitlements",
    ProvisioningPlan.Operation.Add,
    responsibilityValue
);

if (startDateVal != null && startDateVal.trim().length() > 0) {
    Date sDate = df.parse(startDateVal);
    attrReq.setAddDate(sDate);          // sunrise — per AttributeRequest
    attrReq.setAttribute("sunrise", sDate);
}

if (endDateVal != null && endDateVal.trim().length() > 0) {
    Date eDate = df.parse(endDateVal);
    attrReq.setRemoveDate(eDate);       // sunset — per AttributeRequest
    attrReq.setAttribute("sunset", eDate);
}

acctReq.add(attrReq);
  1. Move plan and acctReq creation OUTSIDE the loop, You were creating a new plan and executing inside the loop — that caused only the last dates to survive.
provisioningPlan = new ProvisioningPlan();
provisioningPlan.setIdentity(idName);

AccountRequest acctReq = new AccountRequest(
    ProvisioningPlan.AccountRequest.Operation.Modify,
    application, null, identityName
);

// your for loop here — only acctReq.add(attrReq) inside loop

// AFTER loop:
provisioningPlan.add(acctReq);
  1. Remove compile/execute from inside the loop. After the loop:
if (!hasErrors) {
    ProvisioningProject proj = provisioner.compile(provisioningPlan);
    provisioner.execute(proj);
    // your result checking code here
}

Update these steps in your workflow and try ii, it should work.