BLOG - Auditing Record Field Changes
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
Tracking record changes is essential for maintaining transparency, audit readiness, and operational control in ServiceNow. Although the Activity Formatter helps track field updates, it requires manually reviewing each record, which can be time-consuming when monitoring multiple changes.
Implementing proactive notifications or centralized tracking improves visibility, enhances audit efficiency, and ensures that critical user and group record updates are quickly communicated to the right stakeholders.
The sys_audit table in ServiceNow stores audit history for record updates across the platform. While it provides complete tracking of field-level changes, it can become cluttered since it captures both relevant and non-relevant modifications. This makes it difficult to quickly identify meaningful changes, especially when monitoring specific records such as users or groups.
Use Case: Proactive Visibility of User & Group Record Changes
User and group record changes directly impact access control, security, and governance. Relying on manual audits to track these changes can lead to delays and oversight risks.
Real-time notifications provide instant visibility into administrative updates, improving accountability, compliance, and operational transparency.
To implement this solution, we focus on two primary tables where most administrative changes occur:
User Table – sys_user
Group Table – sys_user_group
To track field-level modifications and notify stakeholders, the approach is as follows:
User Details:
Event :
- Create a Event for User Details Update (for example : kap.user.contact.update).
- To create a event navigate to All > Platform Analytics Administration > Data Collector > Event Registry.
- Select the Table : User[sys_user]
Business Rule :
- Create a Before Business Rule.
- To create business rule navigate to All > System Definition > Business Rule.
- Select table as 'User[sys_user]'.
Here, the Business Rule is configured to trigger only for selected fields instead of monitoring all fields. Certain fields, such as Last Login, are updated frequently during normal system usage and do not require governance-level tracking. Monitoring such fields would generate unnecessary notifications.
var gru = GlideScriptRecordUtil.get(current);
var changedFields = gru.getChangedFieldNames(); // Java List
gs.info(changedFields);
// Convert Java List to JS Array
gs.include('j2js');
changedFields = j2js(changedFields);
gs.info(changedFields);
// Fields you want to track
var allowedFields = [
'user_name',
'email',
'first_name',
'last_name',
'department',
'manager',
'active',
'locked_out',
'password_needs_reset',
'location'
];
// Filter only allowed + changed fields
var finalFields = [];
for (var i = 0; i < changedFields.length; i++) {
if (allowedFields.indexOf(changedFields[i]) > -1) {
finalFields.push(changedFields[i]);
}
}
var table = "user";
// Build parm2 JSON
var payload = {
type: table,
updatedBy: gs.getUserDisplayName(),
};
// Send event only if relevant fields changed
if (finalFields.length > 0) {
gs.eventQueue(
"kap.user.contact.update",
current,
finalFields, // parm1
JSON.stringify(payload) // parm2 (JSON)
);
}This implementation leverages GlideScriptRecordUtil to dynamically detect modified fields, eliminating the need to manually check each field using hardcoded conditions. The getChangedFieldNames() method returns the list of updated fields as a Java List, which cannot be directly handled using standard JavaScript operations. To address this, the j2js utility is used to convert the Java List into a JavaScript array, enabling easy iteration and filtering. This approach improves performance, avoids repetitive current.<field>.changes() checks, and results in a more scalable and maintainable change-tracking solution. The detected field changes are then passed as event parameters and rendered dynamically within the email script.
Here, the above info message logs defines the use of the j2js utility as you can find the without j2js its Java Array and after use of j2js we get javascript parsable array.
Email Script :
- Create a Email Script.
- To create email script navigate to All > System Notification > Email > Notification Email Scripts.
if (!event || !event.parm1 || !event.parm2) {
template.print("Update details are unavailable.");
return;
}
var fields = event.parm1.split(',');
var data = JSON.parse(event.parm2);
var subject = data.type === "user" ?
"User Details Updated: " + current.name :
"Group Details Updated: " + current.name;
var bodyStart = data.type === "user" ?
"user/contact" :
"group";
email.setSubject(subject);
template.print(
'<p>This is to inform you that updates have been made to the ' +
bodyStart + ' <b>' + current.name +
'</b>. Please find the details below:</p>'
);
template.print('<p>');
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
if (!current.isValidField(field)) {
continue;
}
var fieldLabel = current[field].getLabel();
var fieldVal = current[field].getDisplayValue();
template.print(
'<b>' + fieldLabel + ':</b> ' + fieldVal + '<br/>'
);
}
template.print(
'<b>Updated By:</b> ' + data.updatedBy
);
template.print('</p>');Here, I am using the same email script to both the user and group notifications as in the parm 2 i am sending the table name so modify the content accordingly.
Notification :
- Create a Notification
- To create a notification navigate to All > System Notification > Email > Notifications.
User Details Update Notification
Group Details :
Event :
- Create a Event for User Details Update (for example : kap.group.details.update).
- Select the Table : Group[sys_user_group]
Business Rule :
- Create a Before Business Rule.
- Select table as 'Group[sys_user_group]'.
var gru = GlideScriptRecordUtil.get(current);
var changedFields = gru.getChangedFieldNames(); // Java List
// Convert Java List to JS Array
gs.include('j2js');
changedFields = j2js(changedFields);
// Fields you want to track
var allowedFields = [
'name',
'email',
'manager',
'active'
];
// Filter only allowed + changed fields
var finalFields = [];
for (var i = 0; i < changedFields.length; i++) {
if (allowedFields.indexOf(changedFields[i]) > -1) {
finalFields.push(changedFields[i]);
}
}
var table = "group";
// Build parm2 JSON
var payload = {
type: table,
updatedBy: gs.getUserDisplayName(),
};
// Send event only if relevant fields changed
if (finalFields.length > 0) {
gs.eventQueue(
"kap.group.details.update",
current,
finalFields, // parm1
JSON.stringify(payload) // parm2 (JSON)
);
}
Email Script :
- Create a Email Script, if you need. As I am using the same email script for both.
Notification :
- Create a Notification.
Group Details Update Notification
Note: This implementation is not limited to user and group records. The same approach can be extended to other tables where field-level change tracking and notification visibility are required.
This article helped me a lot: Click Here 👈
If you found this Blog helpful, please mark it as Helpful 👍.
Regards,
Tausif
