LDAP Nested Groups

mflora
Kilo Contributor

I am currently working on an integration with Active Directory. Within AD, we are using groups to designate the Assignment Groups within Service-now. This is working just fine as long as, in AD, the members of the groups are individual users. If another AD group is added as a member of this group, we are not able to see the individual user accounts within the nested group.

Does anyone know of a way to have Service-now traverse the nested group members?

Also, there are multiple levels within the nested groups (e.g. groups within groups within groups, etc.).

Any help or suggestions would be greatly appreciated.

Thanks

27 REPLIES 27

For anyone finding this post, we ended up syncing both the groups that contained nested groups and the nested groups and their members into ServiceNow.   Then as part of the transform map, we are querying the nested groups and their members to add them to the top level groups.   We are also checking the membership of the top level group to see if they are still in the nested group and if they are not, we are removing them.



Sample Structure:



OU A


      sub OU B


                  IT Help Desk Unit Group


                  IT Networking Unit Group


      sub OU C


                  IT Help Desk Assignment Group


                          contains IT Help Desk Unit Group from sub OU B


                  IT Networking Assignment Group


                          contains IT Networking Unit Group from sub OU B



So in the above structure, The users that exist in the "IT Help Desk Unit Group" are added to the "IT Help Desk Assignment Group" within ServiceNow.



sample Code:



// grab the name of the member group


var str = source.u_member; // Nested AD Group if applicable


var groupmembers = [];


var newGroupSys = "";


var targetGroup = source.u_name;   // Target ITSM Group


  1. gs.log(targetGroup);

var res = str.split(",");


  1. gs.log(res);

var groupname = res[0].substring(3, res[0].length);


  1. gs.log(groupname);

var groupCheck = source.u_description.indexOf('ServiceNow');


  1. gs.log(groupCheck);


if (groupCheck != -1) {


  1. gs.log('Group is a ServiceNow Group, lets populate it now');

targetGroup = targetGroup.substring(5, targetGroup.length);


      gs.log('Target Group is ' + targetGroup);



// look up the sys id of the targetGroup


var g = new GlideRecord('sys_user_group');


  1. g.addQuery('name', targetGroup);
  2. g.query();

if (g.next()) {


  var newGroupSys = g.sys_id;


log.info("TJS" +"Group " + targetGroup + " found with a SYS ID of " + newGroupSys);


}



//   look up the sys id of the member group



var g2 = new GlideRecord('sys_user_group');


g2.addQuery('name', groupname);


g2.query();


if (g2.next()) {


  var existingGroupSys = g2.sys_id;


log.info("TJS" +"Group " + groupname + " found with a SYS ID of " + existingGroupSys);


 


  // grab a list of the member group's members



var g3 = new GlideRecord('sys_user_grmember');


g3.addQuery('group', existingGroupSys);


g3.query();


while (g3.next()){


log.info("TJS" +g3.user.getDisplayValue() + " found with a sys id of " + g3.user);


log.info("TJS" +'Searching the Target Table Group Members');


         


var g4 = new GlideRecord('sys_user_grmember');


g4.addQuery('user', g3.user.sys_id);


g4.addQuery('group', newGroupSys);


g4.query();


if (g4.next()) {


log.info("TJS" +g3.user.getDisplayValue() + ' user already a member and will not be added again');


          }


else {


var g5 = new GlideRecord('sys_user_grmember');


g5.initialize();


g5.user = g3.user;


g5.group = newGroupSys;


g5.insert();


log.info("TJS" +g3.user.getDisplayValue() + ' user not a member already and has been added');


          }


}


}



// remove users from target group if they don't exist in the source group



var g8 = new GlideRecord('sys_user_grmember');


g8.addQuery('group', newGroupSys);


g8.query();


while (g8.next()) {


groupmembers.push(g8.user.sys_id); //build list of users in the ITSM Group



}



for (i=0; i < groupmembers.length; i++) {


//log.info("TJS" +'the value of i is ' + i + " and the value of groupmembers is " + groupmembers[i]);


var g6 = new GlideRecord('sys_user_grmember');


g6.addQuery('group', existingGroupSys);


g6.addQuery('user', groupmembers[i]);


g6.query();


  if (!g6.next()) {


      var g7 = new GlideRecord('sys_user_grmember');


g7.addQuery('group', newGroupSys);


g7.addQuery('user', groupmembers[i]);


g7.query();


      if (g7.next()) {


g7.deleteRecord();


log.info("TJS" +'deleting record ' + g7.user + ' because we did not find a match');


      }


else {


//log.info("TJS" +'Group: ' + g7.group.getDisplayValue() + ' synced up correctly');


      }


     


}  


}      


     


}


else


{


groupname = res[0].substring(3, res[0].length);


gs.log('Group was not a ServiceNow Group, so we will not populate it');


}


What is your LDAP query ?


and for a group does that return both the name of any users who are direct members and any groups that are nested ?



I meant to look at this a while ago, but the requirement went away and where I am now I do not have direct LDAP query access to AD do any tests. (the current client had another IDAM)


What I have done for some of our 3rd party groups is to use the "parent" field and add a new checkbox - copy members to parent group


If checked, when the group is processed the users are added to the parent group



Cheers


Hi Julian,



We have three OU definitions set up, two of which are Group Definitions and just reference two distinct OU's.   The third is the User OU Definition:



(&(objectCategory=person)(objectclass=user)(memberOf=CN=ITSM_All Users,OU=ITSM GROUPS,OU=User Groups,dc=contoso,dc=com) (!(userAccountControl:1.2.840.113556.1.4.803:=2)) )



This group that we are targeting has users directly added to it and they are the only users that we sync to ServiceNow.   We have a separate OU that contains all the groups that we are using for ServiceNow which contains the nested groups from another OU (this OU has the users directly added to those groups).   Those nested groups are set as inactive in ServiceNow and are just used as a reference.   Everything is query based for those reference groups so they are automatically populated in our AD based on predefined attributes.   So if someone where to change the department that they work in, they would automatically be removed from their old department and added to their new department group.   In ServiceNow, all of this would update as well to reflect this change so no manual work from the ITSM Team.


Going from memory here, but I am sure the last time I tried it I was only returned the list of users and no groups for each group


So if Group A has 2 members John and Jane, but also has the nested groups Billy and Bob, I am sure I was only seeing John and Jane



The LDAP was importing all users from each region before they came in the for the morning and was using the Listener for updates


Used to import the groups based on name or an email address containing something - (&(objectClass=group)(|(cn=*ITSM*)(mail=*.itops@xxxxxx.yyy)))


Perhaps it was down to the attributes that were being returned - was just going to check that, when I just came across this



Yes, using the LDAP_MATCHING_RULE_IN_CHAIN matching rule (OID 1.2.840.113556.1.4.1941). For example:


(memberOf:1.2.840.113556.1.4.1941:=cn=group,cn=users,DC=x) 

see http://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx


The contents of the member attribute contains the distinguished names of either users or groups. My script is just parsing that data and finding the group inside the attribute and then querying the ServiceNow group table to find the members and then add them back into the other group.   The built in LDAPUtils script will only match users contained within that attribute hence the need for the custom code.



The LDAP Matching Rule as far as I can tell only applies to user importing and not group importing.   It's searching the members of a group and any nested groups to import the users.