One of the most common LDAP integration requirements is to disable users in ServiceNow when they become disabled in the LDAP source.

It is probably worth mentioning that you always want to deactivate user records (and most other records too) in ServiceNow instead of deleting them. Once a user record has been created in ServiceNow it should always remain in ServiceNow because that record could be linked to hundreds of other records (tasks, CIs, etc.). Deleting the record kills the relationship to those other records. Deactivating the record keeps that relationship in place.

Because the exact steps to set up this behavior vary depending on your LDAP setup and processes, this configuration isn’t something that can be predefined in ServiceNow. Typically a ServiceNow consultant assists with this setup and specific requirements are determined on a client-by-client basis. It has been my experience that there are two common approaches that can be used to disable ServiceNow users from LDAP. This article explains these approaches and how you can implement the needed functionality.

The basic idea to keep in mind when you set up either of these configurations is that the system needs to see some consistent data indicator per user object in LDAP that the user has been disabled. Generally this indicator is membership in a specific OU (which could be identified by parsing the ‘dn’ attribute) or through the use of the ‘useraccountcontrol’ attribute…or both. The bottom line is that ServiceNow cannot disable users from an external source unless it receives consistent data about those users indicating which ones should be disabled and which ones should not. With either of the approaches below this data MUST come into ServiceNow by way of an import set table where it can be evaluated and transformed. If these terms seem foreign to you you’ll want to familiarize yourself with ServiceNow Import Sets before you continue.

Disabling users in Service-now from an LDAP source typically involves one of these two methods…

1Separate import process from LDAP just for disabled users: In this approach you don’t attempt to handle disabled users through the standard LDAP import at all. Instead, you (or your LDAP Admin) produce a separate extract from your LDAP source that contains the users that need to be disabled. This extract can then be used in a separate import to simply set the ‘active’ flag on every record in the import to ‘false’. The entire script (‘target.active=false’) can be placed in the ‘Script’ field directly on the ‘Table Transform Map’ record.

The benefits to this solution are that the scripting is extremely simple and you don’t have to worry about identifying which users are active or inactive in ServiceNow. You simply deactivate everything in the feed! An additional benefit is that you don’t have to bring a bunch of users into a temporary import table just to see if they should be deactivated or not. Depending on the number of users records that have to be evaluated in your transform (and the number of attributes being brought in per record), this may be the only way to disable users without impacting the performance of the LDAP load. The obvious drawback to this method is that you have to have a separate process to create and drop the extract of disabled users in a location where your ServiceNow data source can pick it up.

2Open up the LDAP OU filter to bring everything in to your import set table (including disabled users) and then ignore inserts of disabled users based on certain script conditions: The sample ‘Users’ OU definition that ServiceNow provides in its out-of-box LDAP sample contains a filter that looks like this…

This filter is important because it defines what user records will be brought into the ServiceNow import set table to be evaluated. The red-highlighted portion of the filter in the image above represents the common problem faced when you try to disable users from an LDAP import. What it basically says is that all disabled users in your LDAP source should be filtered out. This is great but you can’t very well disable a user record when you cannot see because it is disabled! The solution in this case is to remove that portion of the filter so that you can see all of those user records. “Seeing” those user records means that they are all brought into your temporary import set table so that they can be evaluated by an import set transform and disabled if necessary.

Once those records hit your import set table your transform takes over and can manipulate the target records in the ServiceNow user table as required. The exact script that you write depends on the LDAP attributes that you bring into ServiceNow that indicate a disabled user. Here are a couple of common examples that I’ve used before to filter based on OU (from the ‘dn’ attribute which comes into your import table as ‘u_dn’) and the ‘useraccountcontrol’ attribute (which is usually 514 or 546 for a disabled user). For more information on the ‘userAccountControl’ attribute see this page.

//Deactivate LDAP-disabled users during transform based on 'userAccountControl' attribute
if(source.u_useraccountcontrol == '514' || source.u_useraccountcontrol == '546'){
   target.active=false;
   target.locked_out=true;
}
//Deactivate LDAP-disabled users during transform based on OU membership in 'dn'
if(source.u_dn.indexOf('OU=Disabled Accounts') > -1){
   target.active = false;
   target.locked_out = true;
}

The last piece to this solution is VERY IMPORTANT. Because you’ve opened your OU definition filter so that you can see enabled and disabled accounts from your LDAP source, ServiceNow will evaluate all of those records. That part is desirable. What isn’t desirable is the insertion of 50 thousand inactive LDAP accounts from 5 years ago that you don’t want to be created in ServiceNow. You should NEVER create a user record in ServiceNow for a user that is inactive. The only thing you want to do is disable any existing ServiceNow users as they are disabled in your LDAP source. In order to prevent this from happening you need to set up a script that runs before the transform of each record and identifies if a record is disabled AND is being inserted. If an insert of a disabled user is happening then the operation should be ignored by the transform. Here’s what that ‘onBefore’ transform map script looks like…

Please note that the exact content of this script depends on the way disabled users are defined in your LDAP source. You may need to change this script to meet the needs of your environment.

//Ignore any insert of a disabled record as defined by the 'userAccountControl' attribute
var uc = source.u_useraccountcontrol;
if((uc == '514' || uc == '546') && action == 'insert'){
   ignore = true;
}

The script examples above are given to show the different parts of solution #2. If you put all of it together you can produce a very elegant solution that only requires a single script. Here’s a great script that I got from Valor Poland that puts all of this together in a single script…and makes it so that you don’t have to rely on hard-coded ‘userAccountControl’ values. It also allows you the option of reactivating LDAP user accounts as well. The script can be placed in the ‘Script’ field of the ‘Table Transform Map’ record or in an ‘onBefore’ transform map script. Very, very cool.

Note that this script makes use of the ‘userAccountControl’ attribute. If you are using another method to indicate disabled users in your LDAP source then this script wouldn’t apply.
//Deactivate LDAP-disabled users during transform based on 'userAccountControl' attribute
//Convert the userAccountControl attribute back to a hex value
var ctrl = parseInt(source.u_useraccountcontrol, 10);
ctrl = ctrl.toString(16);

//The only digit we care about is the final one
//A final hex digit value of '2' in 'ctrl' means disabled
if(ctrl.substr(-1) == '2'){
   //Deactivate and lock the user account
   target.active = false;
   target.locked_out = true;
   //Ignore any insert of a disabled record
   if(action == 'insert'){
      ignore = true;
   }
}
else {
   //Optional:  Reactivate and unlock the user account
   //target.active = true;
   //target.locked_out = ctrl.substr(-2, 1) == '1';
}