Cleanup disconnected entitlements from the account

Account does not have entitlement but showing in Identity Entitlements details report as a disconnected.

Was the entitlement requested via the Access Request page or possibly assigned via an Access Review approval where the certification config had ‘Update Assignments’ enabled? That could cause the entitlement to “stick” on the Identity even after the entitlement has been removed in the application itself.

You can also try refreshing the identity with the Refresh Identity Entitlements for all links option enabled and see if that helps true-up the identity, but that wouldn’t solve the issue if the issue is around a ‘stuck’ AttributeAssignment or the like.

Hi @souravpanja1
As @brian_weigel mentioned this due to Attribute Assignment(Sticky Attribute) in IIQ. Whenever a user is provisioned via Access Request(LCM) this sticky attribute is added to the identity. The will be part of the provisioning plan under attributes as assignment = true. Refreshing the identity will retry provisioning of missing entitlements and accounts. Removing the sticky attribute from Identity cube will fix the issue.The ideal way will be via a remove request instead of direct update of identity cube

1 Like

Hi @Jarin_James How to delete from identity cube because there is no entry with applicationRef and Attribute assignment. Can you provide more detail.

I had recently same problem.
Regarding entitlements, neither removing attributeAssignments nor sticky wont help alone. Identity has still also entries in the IdentityEntitlements and these have to be removed as well.

When removing just broken attributeAssignments then Sailpoint will not retry to provision them again, indeed. But these entitlements still show up in the Identity cube UI as broken. Removing also IdentityEntitlements finally helps.

Hi @sachinkeshavsutar ,

You can check this following sample task which can give you some insights on Deleting Identity Entitlements and Attribute Assignment for an application.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE TaskDefinition PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<TaskDefinition name="UpdateAttributeAssignments" resultAction="Delete" subType="task_item_type_generic" type="Generic">
  <Attributes>
    <Map>
      <entry key="TaskDefinition.runLengthAverage" value="0"/>
      <entry key="TaskDefinition.runLengthTotal" value="0"/>
      <entry key="TaskDefinition.runs" value="21"/>
      <entry key="TaskSchedule.host"/>
      <entry key="applicationName" value="%ApplicationName%"/>
      <entry key="isUpdate" value="false"/>
      <entry key="ruleName" value="UpdateAttributeAssignmentRule"/>
      <entry key="taskCompletionEmailNotify" value="Disabled"/>
      <entry key="taskCompletionEmailRecipients"/>
      <entry key="taskCompletionEmailTemplate"/>
    </Map>
  </Attributes>
  <Description>A task that can be used to run an arbitrary rule.</Description>
  <Owner>
    <Reference class="sailpoint.object.Identity"  name="spadmin"/>
  </Owner>
  <Parent>
    <Reference class="sailpoint.object.TaskDefinition" name="Run Rule"/>
  </Parent>
  <Signature>
    <Inputs>
      <Argument helpKey="The Application name for this task should be executed" name="applicationName" required="true" type="string">
        <Prompt>Application Name</Prompt>
      </Argument>
      <Argument helpKey="When this is checked, the task will only run for the specific identity" name="userLogin" type="string">
        <Prompt>Please enter tNumber of the user for which AttributeAssignment has to be updated</Prompt>
      </Argument>
      <Argument helpKey="When this is checked, AttributeAssignment and Identity Entitlement will be updated. Else task result will show the identities to be updated having AttributeAssignment for the application" name="isUpdate" type="boolean">
        <Prompt>Update the Attribute Assignments.</Prompt>
      </Argument>
    </Inputs>
    <Returns>
      <Argument name="totalIdentities" type="int">
        <Prompt>Total Identities</Prompt>
      </Argument>
      <Argument name="totalIdentityEntitlements" type="int">
        <Prompt>Total IdentityEntitlements</Prompt>
      </Argument>
      <Argument name="identitiesUpdatedList" type="string">
        <Prompt>Identities to be updated</Prompt>
      </Argument>
      <Argument name="totalIdentitiesUpdated" type="int">
        <Prompt>Total Identities Updated</Prompt>
      </Argument>
      <Argument name="totalIdentityEntitlementsUpdated" type="int">
        <Prompt>Total IdentityEntitlements Updated</Prompt>
      </Argument>
      <Argument name="identitiesNotUpdated" type="string">
        <Prompt>Identities not Updated</Prompt>
      </Argument>
      <Argument name="identityEntitlementsNotUpdated" type="int">
        <Prompt>IdentityEntitlements not updated</Prompt>
      </Argument>
    </Returns>
  </Signature>
</TaskDefinition>

Rule :

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import sailpoint.api.IncrementalObjectIterator;
import sailpoint.api.ObjectUtil;
import sailpoint.api.SailPointContext;
import sailpoint.object.AttributeAssignment;
import sailpoint.object.Filter;
import sailpoint.object.Identity;
import sailpoint.object.IdentityEntitlement;
import sailpoint.object.ObjectConfig;
import sailpoint.object.QueryOptions;
import sailpoint.object.TaskResult;
import sailpoint.tools.GeneralException;
import sailpoint.tools.Message;
import sailpoint.tools.Util;
private static Log logger =
    LogFactory.getLog("com.security.sailpoint.rule.UpdateAttributeAssignmentRule");
int totalIdentityEntitlements = 0;
int totalIdentities = 0;
Set identitiesUpdatedList = new HashSet();
Set identitiesNotUpdated;
int identityEntitlementsNotUpdated;
int totalIdentityEntitlementsUpdated;
int totalIdentitiesUpdated;
public void updateTaskResult(TaskResult taskResult, boolean isUpdate) {
  if (isUpdate) {
    taskResult.put("identitiesNotUpdated", identitiesNotUpdated);
    taskResult.put("identityEntitlementsNotUpdated", identityEntitlementsNotUpdated);
    taskResult.put("totalIdentityEntitlementsUpdated", totalIdentityEntitlementsUpdated);
    taskResult.put("totalIdentitiesUpdated", totalIdentitiesUpdated);
  } else {
    taskResult.put("identitiesUpdatedList", identitiesUpdatedList);
  }
  taskResult.put("totalIdentityEntitlements", totalIdentityEntitlements);
  taskResult.put("totalIdentities", totalIdentities);
}public void updateIdentityEntitlement(IdentityEntitlement identityEntitlement,
    SailPointContext context, String name, boolean isUpdate) {
  logger.trace("UpdateAttributeAssignmentRule : updateIdentityEntitlement method Begins");
  try {
    if (!identityEntitlement.getNativeIdentity().equals(name)) {
      totalIdentityEntitlements++;
      logger
          .trace("UpdateAttributeAssignmentRule : updateIdentityEntitlement method : identityEntitlement nativeidentity is intranet");
      if (isUpdate) {
        identityEntitlement.setNativeIdentity(name);
        context.saveObject(identityEntitlement);
        context.commitTransaction();
        totalIdentityEntitlementsUpdated++;
        logger
            .trace("UpdateAttributeAssignmentRule : updateIdentityEntitlement method : identityEntitlement updated");
      }
    }
  } catch (GeneralException e) {
    ++identityEntitlementsNotUpdated;
    logger
        .error("UpdateAttributeAssignmentRule : updateIdentityEntitlement method : GeneralException "
            + e);
  }
  logger.trace("UpdateAttributeAssignmentRule : updateIdentityEntitlement method Ends");
}public void updateAttributeAssignment(SailPointContext context, String applicationName,
    Identity identity, String name, boolean isUpdate) {
  logger.trace("UpdateAttributeAssignmentRule : updateAttributeAssignment method Begins");
  boolean isUpdated = false;
  Iterator iterator = null;
  boolean containsAssignment = false;
  try {
    identity = ObjectUtil.lockIdentityByName(context, name, ObjectUtil.DEFAULT_LOCK_TIMEOUT);
    iterator = identity.getAttributeAssignments().iterator();
    while (iterator.hasNext()) {
      AttributeAssignment attributeAssignment = (AttributeAssignment) iterator.next();
      if (attributeAssignment.getApplicationName().equals(applicationName)
          &amp;&amp; !attributeAssignment.getNativeIdentity().equals(name)) {
        containsAssignment = true;
        logger
            .info("UpdateAttributeAssignmentRule : updateAttributeAssignment method : identity has AttributeAssignment");
        if (isUpdate) {
          attributeAssignment.setNativeIdentity(name);
          isUpdated = true;
        } else {
          identitiesUpdatedList.add(name);
        }
      }
    }
    if (containsAssignment) {
      ++totalIdentities;
    }
  } catch (GeneralException e) {
    logger
        .error("UpdateAttributeAssignmentRule : updateAttributeAssignment method : GeneralException "
            + e);
    identitiesNotUpdated.add(name);
  } finally {
    if (isUpdated) {
      context.saveObject(identity);
      context.commitTransaction();
      identitiesUpdatedList.add(name);
      totalIdentitiesUpdated++;
      logger
          .info("UpdateAttributeAssignmentRule : updateAttributeAssignment method : identity updated");
    }
    if (identity.isLocked()) {
      context.unlockObject(identity);
    }
    if (iterator != null) {
      Util.flushIterator(iterator);
    }
  }
  logger.trace("UpdateAttributeAssignmentRule : updateAttributeAssignment method ends");
}boolean isUpdate = Util.otob(config.get("isUpdate"));
String applicationName = Util.otoa(config.get("applicationName"));
String userLogin = Util.otoa(config.get("userLogin"));
logger.trace("UpdateAttributeAssignment Task : UpdateAttributeAssignmentRule : Begins");
String result = "Fail";
IncrementalObjectIterator iterator = null;
if (Util.isNullOrEmpty(applicationName) || null == taskResult) {
  logger
      .error("UpdateAttributeAssignment Task : UpdateAttributeAssignmentRule : Application Name or TaskResult is null");
  return result;
}
try {
  QueryOptions queryOptions = new QueryOptions();
  queryOptions.setDistinct(true);
  if (Util.isNotNullOrEmpty(userLogin)) {
    queryOptions.addFilter(Filter.ignoreCase(Filter.eq("identity.name", userLogin)));
  }
  queryOptions.addFilter(Filter.ignoreCase(Filter.eq("application.name", applicationName)));
  queryOptions.addFilter(Filter.ignoreCase(Filter.eq("source", "LCM")));
  iterator = new IncrementalObjectIterator(context, IdentityEntitlement.class, queryOptions);
  if (isUpdate) {
    identitiesNotUpdated = new HashSet();
    identityEntitlementsNotUpdated = 0;
    totalIdentityEntitlementsUpdated = 0;
    totalIdentitiesUpdated = 0;
  }
  while (iterator.hasNext()) {
    IdentityEntitlement identityEntitlement = (IdentityEntitlement) iterator.next();
    Identity identity = identityEntitlement.getIdentity();
    if (identity != null) {
      String name = identity.getName();
      logger
          .trace("UpdateAttributeAssignment Task : UpdateAttributeAssignmentRule : User =" +name);
      if (Util.isNotNullOrEmpty(name) &amp;&amp; !identitiesUpdatedList.contains(name)) {
        updateAttributeAssignment(context, applicationName, identity, name, isUpdate);
      }
      updateIdentityEntitlement(identityEntitlement, context, name, isUpdate);
    }
  }
  result = "Success";
} catch (GeneralException e) {
  taskResult.addMessage(new Message(Message.Type.Error, "GeneralException occurred: "
      + e.getMessage(), e));
  logger
      .error("UpdateAttributeAssignment Task : UpdateAttributeAssignmentRule : GeneralException"
          + e);
} finally {
  if (iterator != null) {
    Util.flushIterator(iterator);
  }
}
updateTaskResult(taskResult, isUpdate);
return result;

Also find couple of articles on AttributeAssignment

3 Likes

@Jarin_James Thank you for your assistance. I value the insights and guidance you provide.

This issue resolved. Please close the issue.

@souravpanja1 Can you please share the details of how you resolved your issue for future readers?

Firstly, thanks @Jarin_James for your help.
Resolutions steps

  1. Identify the redundant sticky entitlements
  2. Clean up manually then we build the rule/task

Note: As sticky entitlements are related to role so verification steps prioritized before cleanup.

Hi @souravpanja1 ,

You can itself mark the response which helped you to fix the issue as Solution.

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