Restore a Field on the User Table Back to its previous Value
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
Hi,
A bulk change was made to user on the sys_user table where a field called "u_principal_name" was cleared.
How can I get all these records to update back to have the previous value in them
I have seen on the forums that you can use a business rule with logic like "current.u_principal_name=previous.u_principal_name", but that does not work.
I also tried using flow designer to look up the history set and then the history line, but this does not work either as I have established that the history set does not get created until you right click the record and then select "History>list", so flow designer cant find a record.
I have around 4000 records to restore
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 hours ago
Hello @jonathangilbert,
The current.u_principal_name=previous.u_principal_name trick doesn't work because previous is only populated during the same transaction that fired the business rule, it's not a lookup into history. What you actually want is the sys_audit table, since that's the source data sys_history_set and sys_history_line get built from, and it doesn't depend on anyone having opened History > List first.
Before running anything against all 4000 records, check:
- the Audit flag on the u_principal_name dictionary entry (or table-level auditing on sys_user) was actually on, otherwise sys_audit has nothing to restore from
- sys_audit has rows for tablename sys_user, fieldname u_principal_name, with a non-empty oldvalue right before the bulk clear
- documentkey on those rows matches the sys_user sys_id you're fixing
If that checks out, a background script pulling the most recent oldvalue per user will do the restore:
var gr = new GlideRecord('sys_user');
gr.addQuery('u_principal_name', '');
gr.query();
while (gr.next()) {
var aud = new GlideRecord('sys_audit');
aud.addQuery('tablename', 'sys_user');
aud.addQuery('documentkey', gr.sys_id);
aud.addQuery('fieldname', 'u_principal_name');
aud.addNotNullQuery('oldvalue');
aud.orderByDesc('sys_created_on');
aud.query();
if (aud.next() && aud.oldvalue) {
gr.u_principal_name = aud.oldvalue;
gr.update();
}
}Run it against a handful of records first in a sub-prod instance, and export the current sys_user set before touching all 4000, since this only recovers what auditing actually captured, anything not audited is gone for good.
Thank you,
Vikram Karety
Octigo Solutions INC
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
43m ago
Try these options:
Option1 : Refer : How to Retrieve Previous Field Values in ServiceNow Scripting
Run a Fix Script or Background Script:
Note: First time try the script on a specific record means in 2nd line of script query accordingly
var userGR = new GlideRecord('sys_user');
userGR.addNullQuery('u_principal_name'); // Query all users where the principal name is now empty or update query as per your requirement
userGR.query();
while (userGR.next()) {
var hw = new sn_hw.HistoryWalker(userGR.getTableName(), userGR.getUniqueValue());
var totalUpdates = hw.getUpdateCount();
if (totalUpdates > 1) {
hw.walkTo(totalUpdates - 2);
var previousValue = hw.getWalkedRecord().getValue('u_principal_name');
if (previousValue) {
userGR.u_principal_name = previousValue;
userGR.setWorkflow(false); // Prevents firing business rules/notifications
userGR.autoSysFields(false); // Keeps original system fields intact
userGR.update();
gs.info('Restored user ' + userGR.user_name + ' to previous value: ' + previousValue);
}
}
}
Option 2: Target the sys_audit Table Directly
If HistoryWalker is unavailable on your instance version or configuration, go for this option
Performance Warning: The sys_audit table is typically the largest table in ServiceNow. Always limit your query by providing the exact timeframe (sys_created_on) of when the bulk update incident occurred to prevent system timeouts.
var auditGR = new GlideRecord('sys_audit');
auditGR.addQuery('tablename', 'sys_user');
auditGR.addQuery('fieldname', 'u_principal_name');
auditGR.addNullQuery('newvalue');
// EXTREMELY IMPORTANT: Replace these with the exact times the bulk script ran
auditGR.addQuery('sys_created_on', '>=', '2026-07-27 21:00:00');
auditGR.addQuery('sys_created_on', '<=', '2026-07-27 21:15:00');
auditGR.query();
while (auditGR.next()) {
var userSysId = auditGR.getValue('documentkey');
var oldVal = auditGR.getValue('oldvalue');
if (oldVal) {
var userGR = new GlideRecord('sys_user');
if (userGR.get(userSysId)) {
userGR.u_principal_name = oldVal;
userGR.setWorkflow(false);
userGR.autoSysFields(false);
userGR.update();
gs.info('Reverted u_principal_name for user ID: ' + userSysId);
}
}
}
Regards
Tanushree Maiti
ServiceNow Technical Architect
LinkedIn: https://www.linkedin.com/in/tanushreemaiti