I started looking through the ServiceNow driver settings, Global Configuration Values, and Entitlements in the first article in this series.
Now let’s look at the Filter and Schema Map to see what object classes are supported.
While the entitlements query for Roles (sys_user_role) and Departments (cmn_departments) they do so directly for the object classes and they do not map through the driver.
The Schema Map shows that the User object class maps to sys_user, and Group maps to sys_user_group. The Filter is entirely Subscriber sync and Publisher ignore, since there is no real Publisher channel on this driver. It is hard to do a publisher channel, if the end point does not really have one. If you can query for changes, then you can enable a publisher channel. If you can query for all objects, in the shim, the driver author can use a State database and compare each queries results
There is not much to call out in the Schema Map or filter, other than they map Password Allow Change to password_needs_reset which I have never seen used before. Login Disabled is mapped to locked_out which is nice since it is the proper ‘tense’. That is a user who is true for Login Disabled would be true for locked_out as well. Often you see that reversed where the Active attribute is true when Login Disabled is false.
The policy flow for this driver is interesting in that it is pretty sparse, and has an almost completely empty Publisher channel. There are Input Transform policies, but although the ITP is always shown on the Publisher half of the fishbone view, it is technically not part of the Publisher thread.
Let’s start by looking at the policies in the Subscriber thread then, and first off the Event Transformation policy is empty, so on to the Matching policy which has three policy objects.
This policy is the standard, make sure it is in the proper container, has a password and so on, comprising of three rules:
Veto if nspmDistributionPassword is not available
This is a pretty simple Veto if operational attribute nspmDistributionPassword is not available.
This is important, since depending on how you create a user, often the object is created first and the password comes in a second event. You see this in many drivers, and it is useful. This seems a bit confusing, since the initial <add> event comes in and does not complete being Vetoed nice and early here. Then the following event is a <modify> of nspmDistributionPassword so how does that end up creating a user? well it is an unassociated User object with a modify so when it hits the association processor (the decision node, just past the Event Transformation in the fishbone view) where the engine converts the <modify> of an unassociated object to a synthetic add, by querying for all the attributes in the filter and then building an <add> document out of the query results.
Then that <add> event hits this policy, but now it has a password attribute and gets past it.
Only Match Users that reside in the configured container/subtree
This is a pretty common policy, same as in many drivers where it tests for class of User, and then uses the awesome If SourceDN in subtree token, which compares the current Source DN of the object, to a GCV value. This is nice since it has some idea of what the semantics of a DN look like and can tell if you are a child in the listed container, (which can be “in subtree”, as in child, grandchild, or deeper, or else it can be “in container” and only test for one level deep.)
They use the GCV ~idv.dit.data.users~ which is part of the common GCV set for most drivers. They use the tilde notation, which means that when the driver starts up, it does a search and replace in memory on policies for any GCV’s identified with tilde symbols. The only downside to this approach is that if the GCV does not exist, the driver will throw a fatal error and fail to start.
The alternative is to use the dollar sign notation, like: $idv.dit.data.users$ this can reference a GCV or Local Variable the same way, the engine figures it out. Both ways work, one has a minor downside.
Now it turned out that there used to be a bug in the engine where a variable, holding a special character (like you might use in a Regular Expression) gets the special character escaped when you use the $GCVname$ notation, but when you use the ~GCVName~ notation it is done differently and would work. But that was fixed in IDM 4.5 a while back now.
If the user is in the proper container, they use the Unmatched SourceDN token, which has to be used following an If source DN in subtree token, to return the ‘rest of the story’ of the DN.
I.e. If your user had a sourceDN of: \Tree\Org\OU1\OU2\OU3\OU4\UserA and then did an If SourceDN test looking to see if the user was in Org\OU1 (since you do not need the tree value, it is implied sometimes). Then the Unmatched Source DN token would return the value OU2\OU3\OU4\UserA.
Now the problem is, once you leave the rule (and possibly policy object) that did the If SourceDN in subtree test, Unmatched Source DN no longer returns a value. Thus you need to use it now, before it is gone, therefore the policy sets an Operation Property, unmatched-src-dn with the value of the Unmatched Source DN token results.
This is confusing to people since there is this token, there is often a visible operation property, and yet one is used to set the other but they are not the same thing.
Finally the rule sets another operation property ‘attempt-to-match’ to true. This is how they updated many of the drivers with the release of Packages. This way you can layer on other policies that chain one after another, until one sets attempt-to-match to true, and if none succeed, there is a veto as the last policy. We will see how the intervening policy from the Entitlement add on package uses this in the following policy object.
If you had additional conditions that you wanted to fail or succeed independent of the other policies you could add your own in here, with the same sort of results and integrate nicely.
Not all of the policies do it this cleanly, but the Match policy does a good job.
Only Match Groups that reside in the configured container/subtree
This rule is identical to the previous rule with the exception that it tests for a Group object (instead of User) and uses the GCV for groups (~idv.dit.data.groups~) with the same tilde notation.
It sets the unmatched-src-dn and attempt-to-match operation properties if the rule conditions evaluate to true.
There is only one rule here, “User account entitlement: validate for matching”.
This comes from the Entitlements add on package, and checks for class of User, if the GCV drv.entitlement.UserAccount is set to true. I.e. Is entitlement support enabled in the settings of the driver?
Then it uses the If Entitlement test, for UserAccount to see if it is not available.
When the conditions are true, it does not Veto, rather it sets attempt-to-match operation property to true. This shows a nice example of what I was talking about above of integrating Match polices from different packages and not interfere.
You might ask your self, how did I get here? (Talking Heads reference) but why not just Veto the event? Well it might be that you want to use Entitlements, UNLESS some other set of conditions is true, therefore if the NetIQ package vetoed, there would be no recovery and you would have to modify (and thus dirty) the package. Instead, the rule in first policy says attempt-to-match is true because it is the right subtree.
This rule says no entitlement, so set attempt-to-match to false. Then you could add another policy after this one, that implements your specific tests and then sets attempt-to-match back to true if needed.
Three rules here:
Veto out-of-scope events
The first thing that happens in this policy is to consider the attempt-to-match operation property and if it is not available or equal to false it does a Veto. I have often wondered why they do not test to see if this is NOT set to true. In that case it would handle this in one test. Absence would be NOT true. A value of false would be NOT true. Not sure why these two specific tests are needed.
If the class is user, a simple do find matching object token, looking for the user name (CN value) is sent. ServiceNow’s user table is basically a flat database, exposed over SOAP so not much to search there.
If the class is group, same as user, a simple flat search for the name of the group in the groups table in ServiceNow.
That is about it for the Matching policy set, onwards to the Creation policy set. In Creation there is only one policy object:
There are three rules in here:
Prepare to check Role entitlements after add
In this policy, similar to the Active Directory driver, if you have a Role entitlement, it adds an operation property on the user so that when it successfully completes being created, in the Input Transform, when we see the add-association event then look for this operation property and then do the Role entitlement implementation. The AD driver does this for Groups and Exchange accounts.
First up in the conditions, the GCV drv.entitlement.Role is set to true, if it is a user, and if there is a Role entitlement available, then we add an operation property “check-role-entitlements”.
Prepare to check Group entitlements after add
Very similar to the Role entitlement rule above, check to see if the gcv.entitlement.Group is true, class is User, and a Group entitlement is available, and if true sets an operation property “check-group-entitlements”.
Entitle User with the Department if any Department Entitlement found.
Departments however, are different than Role and Group entitlements, and can be added as part of the user add event. In other words, the Roles and Groups are in separate tables but the User table has a row for department.
Similar conditions, check for gcv.entitlement.Department, class is user, and a Department entitlement is available, but this time instead of adding an operation property, it loops over the Department Entitlements. The destination attribute ‘department’ is set based on the ID value in the Entitlement payload. This is an IDM4 format entitlement so it uses JSON so there can be multiple key and value pairs and you need to use an ECMA function to get the proper value from the specified key. This is the getEntParamField() which takes a JSON string then a key name. In this case, the JSON string is $current-node since a loop over an entitlement is different than a loop over an attribute, instead of the raw Path syntax value, the engine returns the entitlement payload, but not the entire XML content, but rather only the contents of the <param> node.
This is very helpful since it means you get just what you are looking to retrieve in the loop. Thus the $current-node in a loop (we are in an XPATH to use the es: ECMA function notation, so only a single dollar sign is required.
That wraps up the Creation policy, next article on to the Command Transform, since Placement is empty.
Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment. It just worked for at least one person, and perhaps it will be useful for you too. Be sure to test in a non-production environment.