How to Run Post-Approval Actions as System User in ServiceNow (sn_si.admin role Limitation)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Hi Community,
I am working on a Service Catalog item "Manage Group Members" where users can
request to add or remove members from a group. The approval is routed to the
group manager via a workflow, and once approved, the system should automatically
add or remove the selected users from the group.
PROBLEM
After the group manager approves the request and the RITM reaches
State = Closed Complete and Approval = Approved, the selected users
are NOT being added or removed from the group.
Has anyone solved this specific scenario — group member management
post-SIR deployment — without granting sn_si.admin to approvers?
Please find the workflow item script which is written for approval of group.
Any guidance or confirmed working approach would be greatly appreciated.
Thank you.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Script you shared is only the approval routing logic — it determines who approves the request. It doesn't contain any fulfillment logic to actually add or remove users from the group after approval. That's why nothing happens post-approval.
You need a separate fulfillment activity in your workflow (or a business rule on the RITM) that fires after the approval is granted. Here's a confirmed working approach, including the SIR-related scoping challenge.
After SIR (Security Improvement Release) deployments (Washington DC+), direct GlideRecord insert/delete operations on sys_user_grmember from a scoped app are blocked unless the executing context has elevated privileges. This is why many people hit a wall here — the workflow runs, approval completes, but the membership change silently fails with no error.
Try this
Since you've dealt with cross-scope challenges before, the cleanest pattern is a global-scope Script Include that your scoped workflow can invoke.
Step 1 — Global Script Include (ManageGroupMemberUtils)
Create this in the Global scope:
var ManageGroupMemberUtils = Class.create();
ManageGroupMemberUtils.prototype = {
initialize: function() {},
addMember: function(groupSysId, userSysId) {
// Check if already a member
var grCheck = new GlideRecord('sys_user_grmember');
grCheck.addQuery('group', groupSysId);
grCheck.addQuery('user', userSysId);
grCheck.query();
if (grCheck.hasNext()) {
return 'already_member';
}
var gr = new GlideRecord('sys_user_grmember');
gr.initialize();
gr.setValue('group', groupSysId);
gr.setValue('user', userSysId);
var result = gr.insert();
return result ? 'added' : 'failed';
},
removeMember: function(groupSysId, userSysId) {
var gr = new GlideRecord('sys_user_grmember');
gr.addQuery('group', groupSysId);
gr.addQuery('user', userSysId);
gr.query();
if (gr.next()) {
gr.deleteRecord();
return 'removed';
}
return 'not_found';
},
type: 'ManageGroupMemberUtils'
};
Mark it Client callable: false, Accessible from: All application scopes.
Step 2 — Workflow "Run Script" Activity (after Approval)
In your workflow, add a Run Script activity that executes after the Approval step completes successfully. This is the fulfillment logic:
(function() {
var ritm = current; // sc_req_item
var groupID = ritm.variables.group.toString();
var action = ritm.variables.action.toString(); // 'add' or 'remove'
// Handle multi-select users (assuming a List Collector variable)
var selectedUsers = ritm.variables.selected_users.toString();
if (!selectedUsers || !groupID) {
gs.log('ManageGroupMembers: Missing group or users', 'Fulfillment');
return;
}
var util = new global.ManageGroupMemberUtils();
var userList = selectedUsers.split(',');
for (var i = 0; i < userList.length; i++) {
var userSysId = userList[i].trim();
if (!userSysId) continue;
var result;
if (action == 'add') {
result = util.addMember(groupID, userSysId);
} else if (action == 'remove') {
result = util.removeMember(groupID, userSysId);
}
gs.log('ManageGroupMembers: User ' + userSysId + ' -> ' + result, 'Fulfillment');
}
})();
Step 3 — Cross-Scope Access Record
If your catalog item lives in a scoped app, you'll need a sys_scope_privilege record granting your scoped app access to invoke ManageGroupMemberUtils. Navigate to System Definition > Cross-Scope Privileges and create a record allowing your app to call the global Script Include. Alternatively, if your workflow itself runs in global scope (which is typical for legacy workflows), this may not be needed.
Workflow Structure Summary
Your workflow should look like this:
- Begin →
- Approval – User Script (your existing routing script) →
- If Approved → Run Script (fulfillment logic above) → Set Values (close RITM) → End
- If Rejected → Set Values (reject RITM) → End
Quick Note on Your Approval Script
One thing to watch in your existing approval routing — on line 71, you're checking groupManager.active.getDisplayValue() == 'false' via a dot-walked reference field. This works but can be fragile. Consider using gr.getValue() style checks or a direct GlideRecord lookup for the manager's active state, especially if you hit intermittent issues where approvals route incorrectly.
