Skip to content

Commit c576458

Browse files
authored
Add Automated Inactive User Cleanup Scheduled Job (#2580)
* Code for Moveworks bot messages for Flow to include url links * Create script.js * Create readme.md * Delete Moveworks messages html for Flow
1 parent e77c7ec commit c576458

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
🧹 Inactive User Cleanup — ServiceNow Scheduled Job
2+
📌 Overview
3+
4+
This script automates daily cleanup of access and assignments for inactive users in ServiceNow.
5+
It removes orphaned access, ensures task accountability, and sends an email summary upon completion.
6+
7+
✅ Features Included
8+
🔹 Inactive Group Membership Cleanup
9+
10+
Searches User Group Member table (sys_user_grmember)
11+
Identifies inactive users (excluding Web Service Access Only accounts)
12+
Removes them from all associated groups
13+
Logs each removal in system logs
14+
Adds removal details to the summary email
15+
16+
🔹 Direct Role Revocation
17+
18+
Searches User Has Role table (sys_user_has_role)
19+
Removes roles not inherited via group membership
20+
Prevents unauthorized access after deactivation
21+
Logs each role removed
22+
Included in daily summary email
23+
24+
🔹 Task Ownership Cleanup
25+
26+
Searches Task table (task)
27+
Finds active tasks assigned to inactive users
28+
Clears the Assigned To field without triggering workflow
29+
Adds work notes for audit traceability
30+
Logs entries + email reporting
31+
All actions skip users where: web_service_access_only = true
32+
33+
🛠 Script Placement & Configuration
34+
Field Value
35+
Script Type ✅ Scheduled Script Execution
36+
Location Run this script section
37+
38+
Before using in your instance, update the following in script:
39+
40+
Line Update Required
41+
Line 56 Replace sender email in email.setFrom('xyz@service-now.com');
42+
Line 44 Replace system property name in gs.getProperty('glide.xyz.admin.email.recipients');
43+
🔍 System Property Required
44+
45+
Create or update the System Property to store email recipients:
46+
47+
Name (example) Value (example)
48+
glide.xyz.admin.email.recipients admin@example.com,user@example.com
49+
50+
Supports single or comma-separated recipients ✅
51+
52+
✉️ Email Summary Includes
53+
54+
Users removed from groups
55+
Direct roles removed
56+
Active tasks unassigned
57+
Timestamped logs for auditing
58+
59+
📝 Work Notes Added
60+
61+
For tasks reassigned:
62+
System Administrator removed "Assigned to" value as the user is no longer active.
63+
64+
⚠️ Best Practices
65+
66+
Run in sub-prod first
67+
Ensure proper backups/audit compliance
68+
Schedule at low-traffic hours
69+
Monitor logs initially for data impact
70+
71+
🧩 Extendability Ideas
72+
73+
You can easily modify:
74+
Email template (HTML formatting)
75+
Query filters for additional cleanup criteria
76+
Logging to include sys_id values
77+
Scheduling frequency (default recommended: Daily)
78+
79+
🧑‍💻 Maintainers
80+
81+
Feel free to update script name, System Property naming, and sender email for your organization.
82+
Pull requests & suggestions welcome! 🙌
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
///This is a Scheduled Job Script to be added in "Run this script"
2+
//replace the email in line 56- 'email.setFrom('xyz@service-now.com');'
3+
//replace the property name in line 44 'var recipientList = gs.getProperty('glide.xyz.admin.email.recipients');'
4+
5+
var emailBody = 'Daily Inactive User Group/Role/Task Clean-up Completed: \n<br><br>';
6+
7+
//REMOVE INACTIVE USERS FROM GROUPS
8+
var queryString = "user.active=false^user.web_service_access_only=false^user.sourceSTARTSWITHldap:"; //query to find inactive members belonging to groups and ignores any users with "web service access only" = TRUE.
9+
var recGrp = new GlideRecord('sys_user_grmember'); //searches the User Group Member table
10+
recGrp.addEncodedQuery(queryString);
11+
recGrp.query();
12+
while (recGrp.next()) {
13+
emailBody += 'Inactive User, ' + recGrp.user.getDisplayValue() + ', member of Group: ' + recGrp.group.getDisplayValue() + ' was removed from Group.\n<br>';
14+
gs.log('Inactive User, ' + recGrp.user.getDisplayValue() + ', member of Group: ' + recGrp.group.getDisplayValue() + ' was removed from Group.');
15+
recGrp.deleteRecord(); //deletes group membership record from inactive user
16+
}
17+
18+
//REMOVE ROLES FROM INACTIVE USERS THAT WERE NOT ADDED BY GROUP MEMBERSHIP
19+
var recRole = new GlideRecord('sys_user_has_role'); // search view User Has Role table
20+
var queryString2 = "user.active=false^user.web_service_access_only=false^user.sourceSTARTSWITHldap:^inherited=false";
21+
recRole.addEncodedQuery(queryString2); // find inactive users that have a role assigned and ignores any users with "web service access only" = TRUE.
22+
recRole.query();
23+
while (recRole.next()) {
24+
emailBody += 'Inactive User, ' + recRole.user.name + ' found - Role: ' + recRole.role.getDisplayValue() + ', was removed.\n<br>';
25+
gs.log('Inactive User, ' + recRole.user.name + ' found - Role: ' + recRole.role.getDisplayValue() + ', was removed.'); // add info message to system log about what user was inactive and role was removed
26+
recRole.deleteRecord(); //deletes role record from inactive user
27+
}
28+
29+
//CLEARS ASSIGNED TO ON ACTIVE TASKS ASSIGNED TO INACTIVE USERS
30+
var recTask = new GlideRecord('task'); // search task table
31+
var queryString3 = "assigned_to.active=false^assigned_to.web_service_access_only=false^active=true";
32+
recTask.addEncodedQuery(queryString3); // find inactive users that have active tasks assigned to them and ignores any users with "web service access only" = TRUE.
33+
recTask.query();
34+
while (recTask.next()) {
35+
emailBody += 'Removed task from Inactive User: ' + recTask.assigned_to.getDisplayValue() + ' ' + recTask.number + '.\n<br>';
36+
gs.log('Removed task from Inactive User: ' + recTask.assigned_to.getDisplayValue() + ' ' + recTask.number); // add message about what user was inactive tasks removed
37+
recTask.work_notes = 'System Administrator removed "Assigned to" value as the user is no longer active.'; //add work note explanation without workflow
38+
recTask.update();
39+
recTask.assigned_to = '';
40+
recTask.setWorkflow(false); //removes assigned_to value without workflow
41+
recTask.update();
42+
}
43+
44+
var recipientList = gs.getProperty('glide.xyz.admin.email.recipients');
45+
var email = new GlideEmailOutbound();
46+
email.setSubject('Daily Inactive User Group/Role/Task Clean-up Completed');
47+
email.setBody(emailBody);
48+
if (recipientList.includes(',')) {
49+
var recipients = recipientList.split(",");
50+
for (var i = 0; i < recipients.length; i++) {
51+
email.addRecipient(recipients[i]); // Add recipients from system property
52+
}
53+
} else {
54+
email.addRecipient(recipientList); // Add single recipient from system property
55+
}
56+
email.setFrom('xyz@service-now.com');
57+
email.save();
58+
59+
gs.log('Daily Inactive User Group/Role/Task Clean-up Completed: \n\n' + emailBody);

0 commit comments

Comments
 (0)