Some Housekeeping XPath Queries

I’ve been thinking a lot lately about maintaining data quality in the FIM Portal. When you’re working with a lot of referenced objects (and it really is the best way to go in the Portal) you find yourself copying string data around a lot. So, for example, a person selects a business unit object from an Identity Picker, but I also want to copy the business unit name to their Department field, for sync’ing to AD and other such purposes. How do we protect ourselves from unexpected failures in these processes, leading to inconsistencies in the data we’re trying to maintain?

As is often the case, my colleague Bob was well ahead of me on this one and has given me some good advice about targeting my queries so I find just the data inconsistencies, without unnecessarily re-running workflows against the majority of objects which are actually fine.

There are a few things I’ve learnt while developing my housekeeping queries.

You can’t generalise for different values

Starting with the example above where I copy the name of the linked business unit object to the department field, there is no query that says “show me all people who’s Department does not equal Unit/DisplayName”. However we can do this:

/Person[Unit='c1391740-4e40-46b9-9d18-9ae39659f153' and not(Department='Accounts')]

the GUID there belonging to the “Accounts” business unit object which is linked to the person’s “Unit” attribute.

So essentially we need a query for each case. We start by exporting all the business unit objects:

$Units= Export-FIMConfig -OnlyBaseResources -CustomConfig "/Unit"

and then loop through them, adjusting the filter for each unit object, and fixing any errors we find:

foreach ($Unit in $Units)
{
    $ObjID = ($Unit.ResourceManagementObject.ObjectIdentifier).replace("urn:uuid:","")
    $ObjName= ($Unit.ResourceManagementObject.ResourceManagementAttributes | where {$_.AttributeName -eq "DisplayName").Value
    $Filter = "/Person[Unit='{0}' and not(Department=""{1}"")]" -f $ObjID,$ObjName
    $ErrorObjs = Export-FIMConfig -OnlyBaseResources -CustomConfig $Filter
    if ($ErrorObjs)
    {
        foreach ($obj in $ErrorObjs)
        {
            $ImportObject = ModifyImportObject -TargetIdentifier $obj.ResourceManagementObject.ObjectIdentifier -ObjectType "Person"
            SetSingleValue -ImportObject $ImportObject -AttributeName "Department" -NewAttributeValue $ObjName
            $ImportObject | Import-FIMConfig
        }
    }
}

Note: the doubled double quotes around {1} are to account for names which may have a single quote character in them.

Multivalued Attributes

You may also need to check multivalued string attributes have the right values. Here again I found it more efficient to focus on the individual possible values rather than checking through a person’s whole list. But now I need two filters – one that shows missing values that should be added, and one that shows extra values that should be removed.

In this example I have a multivalued reference field called “Applications” which links to another object type. I also copy the names of the application objects to the multivalued string attribute “ApplicationNames”, as strings are easier to use in the Sync Service. I want to check that everyone with a particular linked application also has the application name, and that no one has the application name without also having a link to the application object.

$AddStringFilter = "/Person[Applications='{0}' and not(ApplicationNames=""{1}"")]" -f $ObjID,$ObjName
$RemoveStringFilter = "/Person[not(Applications='{0}') and ApplicationNames=""{1}""]" -f $ObjID,$ObjName

Comparison with a third object type

In the design I’m working on, people get applications and other services via “entitlement” objects. Once the entitlement is in an active state, a role object is directly linked to the person. So I need to be checking that people’s linked roles match their active entitlements, and also that they don’t have any linked roles where an active entitlement does not exist.

$AddRoleFilter = "/Person[ (ObjectID = /Entitlement[Status = 'Active' and RoleLink = '{0}']/PersonLink) and not (Applications = '{0}') ]" -f $ObjID
$RemoveRoleFilter = "/Person[ (Applications = '{0}') and not(ObjectID = /Entitlement[Status = 'Active' and RoleLink = '{0}']/PersonLink) ]" -f $ObjID

Comparison with a Set

The final query uses a set membership. Perhaps you have a TransitionIn Set MPR that triggers a workflow, and you want to periodically check that everyone in the Set does indeed have whatever that MPR should have given them.

You could set the associated workflow to “Run on Policy Update” and periodically disable/enable the MPR, however for a large set this can have a catastrophic impact on Portal performance, tying up FIM Service resources until every single member of the set has been processed, and you can’t stop those jobs once they start.

So again we’re better off targeting queries to look for discrepancies, and again we’ll have to be specific – looking at individual sets and specific expected values. In this example filter I expect all members of the specified set to have an entitlement with a particular linked role:

$Filter = "/Person[ (ObjectID = /Set[ObjectID = '{0}']/ComputedMember) and not(ObjectID = /Entitlement[RoleLink = '{1}']/PersonLink)]" -f $SetID,$RoleID

Summary

Data quality is everything in identity management. The Sync Service is great at enforcing rules and we can re-sync the same data set as often as we need to – but the Portal operates quite differently. By putting in some extra effort with scheduled housekeeping operations we can maintain data quality – but we always need to keep performance in mind, and keep any queries and updates as efficient as possible.

1 Reply to “Some Housekeeping XPath Queries”

  1. Awesome article Carol! Thought I’d explain how I’d implement my non-Powershell alternative 😉

    To implement the first example using the housekeeping model described on TheFIMTeam.com website linked by Carol, here is how you would set up your policy:
    1. set “All Units” (filter=/Unit)
    2. workflow “Housekeeping: Validate user departments”
    2.1 acivity = generic update resource(s) activity, object class = Person, filter = “/Person[Unit='[//Target/ObjectID]’ and not(Department='[//Target/DisplayName]’)]”, update attributes = “Department=string:[//Target/DisplayName]”
    3. MPR “Housekeeping: User departments are validated for all units”, disabled = true, set transition, target set = “All Units”, workflow = “Housekeeping: Validate user departments”
    4. BatchJob “User departments are validated weekly”, target MPR = “Housekeeping: User departments are validated for all units”, frequency = 7

    The other examples can be approached in a similar way, but obviously if you haven’t got your own “generic update resource(s) activity” then the Powershell option is probably for you.

Leave a Reply

Your email address will not be published. Required fields are marked *


*