Blog

Group Merge Utility

Groups in ServiceNow can control a lot of functionality.  They can be used for assignment, security, task routing, and queues. 

Some companies create unique groups for these purposes.  As time progresses, some groups may need to be merged.  Maybe there was too many groups created, or certain departments were combined.  Whatever the reason, here is an nice utility to help you merge your groups if you need to.

Important Notes

  1. If groups are imported from Active Directory, merging groups can cause sync issues. This utility might not be for you in this case.

  2. If you don't want the "Deactivate Source Group" or Change Existing Task Groups" options, you can always adjust the UI Page to not show it. Add style="display:none; in the table row of the of the html.

  3. There is also a "test mode" in the utility that allows you to see what the utility will do before you decide to run. This is currently turned off, but you can adjust the UI Page HTML to show it. Remove style="display:none; in UI Page html on the Table Row for the Test Mode

  4. Always test the Group Merge utility in a Development ServiceNow instance first. You may need to adjust the utility for your ServiceNow instance. Depending on the policies of your company, you may want to disable the checkbox for updating old tasks for the new assignment group.

Screenshots

Merge Groups UI Action

Merge Groups UI Page

Merge Group Results. Roles, Group Members added

CODE

UI Action: Merge Groups

Table: Groups
Form Context Menu: true
Active: true
Show Update: true
Client: true
OnClick: mergeGroups()
Condition: !current.isNewRecord() && gs.hasRole('admin')
Script:
function mergeGroups() {
var sysId = typeof rowSysId == 'undefined' ? gel('sys_uniqueValue').value : rowSysId;
var gDialog = new GlideDialogWindow("snelite_group_merge");
gDialog.setTitle("Merge Groups");
gDialog.setPreference('sysparm_dest_sysID', sysId);
gDialog.setPreference('sysparm_dest_group_name', g_form.getValue('name'));
gDialog.render();
}


UI Page (HTML): snelite_group_merge

<g:ui_form>
 <input type="hidden" id="cancelled" name="cancelled" value="false" />
 <input type="hidden" id="destGroupSysID" name="destGroupSysID" value="$"/>
 <p style="margin:10px"><strong>Description:$[SP]</strong>Merge group members, roles, and skills from one group into one main group.</p>
 <p style="margin:10px;"><strong><font style="color:red;">Warning:$[SP]</font></strong>If groups are imported from Active Directory, merging groups will cause sync issues.Always test group merges in a development environment.</p>
<table style="margin:15px">
 <tr>
 <td nowrap="true" id="my_label" class="label" type="string" choice="0"><span id="status." class="mandatory label_description" mandatory="true" oclass="mandatory">$[SP]</span><label for="source_group_label" onclick="" dir="">Source Group:</label></td>
 <td nowrap="true">
<select name="source_group" id="source_group" onchange="setupReference()">
 <g:evaluate var="jvar_item" expression="
var gr = new GlideRecord('sys_user_group'); 
gr.orderBy('name');
gr.query();
"/>
 <j:while test="$">
<option value="$</option>
 </j:while>
</select>
 </td>
</tr>
<tr>
 <td nowrap="true" id="my_label" class="label" type="string" choice="0"><span id="status." class="mandatory label_description" mandatory="true" oclass="mandatory">$[SP]</span><label for="destination_group_label" onclick="" dir="">Destination Group:</label></td>
 <td nowrap="true">
<label>$</label>
 </td>
</tr>
<tr>
 <td nowrap="true" id="my_label" class="label" type="string" choice="0"><label>Deactivate Source Group:</label></td>
 <td>
<g:ui_checkbox name="deactivate_source_group" value="true" />
 </td>
</tr> 
<tr>
 <td nowrap="true" id="my_label" class="label" type="string" choice="0"><label>Change Existing Task Assignment Groups:</label></td>
 <td>
<g:ui_checkbox name="change_task_groups" value="false" />
 </td>
</tr>
<tr style="display:none;">
 <td nowrap="true" id="my_label" class="label" type="string" choice="0">
<label>Test Mode:</label>
 </td>
 <td>
<g:ui_checkbox name="test_mode" value="false" />
 </td>
</tr>
<tr>
 <td align="right" colspan="2">
<br />
<g:dialog_buttons_ok_cancel ok="return validateForm();" cancel="return onCancel();" />
 </td>
</tr>
 </table>
</g:ui_form>

UI Page (Client Script): snelite_group_merge

function validateForm() {
 if (gel('source_group').value == '') {
alert("${JS:gs.getMessage('Please input Source Group')}");
return false;
 }
 else if (gel('destGroupSysID').value == '') {
alert("${JS:gs.getMessage('Please input Destination Group')}");
return false;
 }
 else {
return true;
 }
}

function onCancel() {
 GlideDialogWindow.get().destroy();
 return false;
}


UI Page (Processing Script): snelite_group_merge

groupMergeRun();

function groupMergeRun() {

//Set Count Variables
var insertGroupRoleCount = 0;
var insertGroupMemberCount = 0;
var insertGroupSkillCount = 0;
var updateTaskCount = 0;
//Set Roles: Cycle through existing Source Roles
var grSourceRole = new GlideRecord('sys_group_has_role');
grSourceRole.addQuery('group.sys_id', source_group);
grSourceRole.query();
if (test_mode == 'true') {
gs.addInfoMessage('Group Roles Query: ' + grSourceRole.getEncodedQuery() + ', Group Roles Affected: ' + grSourceRole.getRowCount());
}
else {
while (grSourceRole.next()) {
//See if new Group and Role combination already exists
var grDestRoleFind = new GlideRecord('sys_group_has_role');
grDestRoleFind.addQuery('group',destGroupSysID);
grDestRoleFind.addQuery('role',grSourceRole.role);
grDestRoleFind.query();
if (grDestRoleFind.getRowCount() == 0) {
//Insert New Role
var grRoleMember = new GlideRecord('sys_group_has_role');
grRoleMember.initialize();
grRoleMember.group = destGroupSysID;
grRoleMember.role = grSourceRole.user;
grRoleMember.insert();
insertGroupRoleCount++;
}
}
}

//Set Group Members: Cycle through existing Source Group Members
var grSourceGroupMember = new GlideRecord('sys_user_grmember');
grSourceGroupMember.addQuery('group.sys_id', source_group);
grSourceGroupMember.query();
if (test_mode == 'true') {
gs.addInfoMessage('Group Members Query: ' + grSourceGroupMember.getEncodedQuery() + ', Group Members Affected: ' + grSourceGroupMember.getRowCount());
}
else {
while (grSourceGroupMember.next()) {
//See if new Group and User combination already exists
var grDestGroupMemberFind = new GlideRecord('sys_user_grmember');
grDestGroupMemberFind.addQuery('group',destGroupSysID);
grDestGroupMemberFind.addQuery('user',grSourceGroupMember.user);
grDestGroupMemberFind.query();
//gs.addInfoMessage('grDestGroupMemberFind Query: ' + grDestGroupMemberFind.getEncodedQuery() + ' = ' + grDestGroupMemberFind.getRowCount());
if (grDestGroupMemberFind.getRowCount() == 0) {
//Insert New Group Member
var grDestGroupMember = new GlideRecord('sys_user_grmember');
grDestGroupMember.initialize();
grDestGroupMember.group = destGroupSysID;
grDestGroupMember.user = grSourceGroupMember.user;
grDestGroupMember.insert();
insertGroupMemberCount++;
}
}
}

//Set Skills: Cycle through existing Source Skills
var grSourceSkill = new GlideRecord('sys_group_has_skill');
grSourceSkill.addQuery('group.sys_id', source_group);
grSourceSkill.query();
if (test_mode == 'true') {
gs.addInfoMessage('Group Skills Query: ' + grSourceSkill.getEncodedQuery() + ', Group Skills Affected: ' + grSourceSkill.getRowCount());
}
else {
while (grSourceSkill.next()) {
//See if new Group and Skill combination already exists
var grDestSkillFind = new GlideRecord('sys_group_has_skill');
grDestSkillFind.addQuery('group',destGroupSysID);
grDestSkillFind.addQuery('skill',grSourceSkill.skill);
grDestSkillFind.query();
if (grDestSkillFind.getRowCount() == 0) {
//Insert New Group Member
var grDestSkill = new GlideRecord('sys_group_has_skill');
grDestSkill.initialize();
grDestSkill.group = destGroupSysID;
grDestSkill.skill = grSourceSkill.skill;
grDestSkill.insert();
insertGroupSkillCount++;
}
}
}