S

erviceNow includes the ability to provide a full audit and journal history of records in the system. This is an extremely useful feature, but there are times when you need to override this audit process. Some examples may include a technician accidentally entering IT-only work notes into the customer facing ‘Additional comments’ field on an incident, or an end user supplying confidential information such as a password or social security number in a ticket.

Unfortunately, the process of deleting or updating audit, activity, or journal entries is fairly difficult to perform, and even more difficult to remember. The customization shown here makes this process much simpler by leveraging UI actions in the standard ‘History -> List’ view from any audited record.

Update ServiceNow Audit History

First, a little technical perspective as to why this solution is helpful. Without this solution, you would need to go to two or three separate tables (sys_audit, sys_history_line, and sys_journal_field) and update or delete entries. While doing this, you would need to be extremely careful about how you query the information since all three tables can be very large and accessing them incorrectly could bring your system to its knees. You would also need to know exactly what you were looking for and be cautious about where you clicked so that you didn’t ruin audit entries in the process.

This solution provides a single, consistent UI action-driven method that keeps you out of the audit tables and updates all of the necessary places in one click. It also includes a confirmation dialog to prevent accidental updates.

WARNING: It should go without saying that you should exercise extreme care when dealing with audit or journal entries in the system. Only make modifications in these areas if it’s absolutely necessary, and only do so within your organization’s change management process.

Delete audit/journal entries

Deleting audit or journal entries can be accomplished by adding a new UI action with the following settings.

‘Delete History Line’ UI Action
Name: Delete History Line
Table: History [sys_history_line]
Action name: delete_history_line
Show insert: false
Show update: true
Client: true
Form button: true
Onclick: confirmDelete()
Condition: gs.hasRole(‘admin’)
Script:

function confirmDelete(){
   if(confirm('Are you sure you want to permanently delete this history line and all corresponding audit history?\n\nTHIS ACTION CANNOT BE UNDONE!')){
      //Call the UI Action and skip the 'onclick' function
      gsftSubmit(null, g_form.getFormElement(), 'delete_history_line'); //MUST call the 'Action name' set in this UI Action
   }
   else{
      return false;
   }
}

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   deleteHistoryLine();

function deleteHistoryLine(){
   var fieldVal = current["new"];
   var fieldName = current.field;
   
   //Query for and delete the 'sys_audit' record
   var aud = new GlideRecord('sys_audit');
   aud.addQuery('documentkey', current.set.id);
   aud.addQuery('fieldname', fieldName);
   aud.addQuery('newvalue', fieldVal);
   aud.query();
   if(aud.next()){
      aud.deleteRecord();
   }
   
   //Query for and delete the 'sys_journal_field' record (if applicable)
   var je = new GlideRecord('sys_journal_field');
   je.addQuery('element_id', current.set.id);
   je.addQuery('element', fieldName);
   je.addQuery('value', fieldVal);
   je.query();
   if(je.next()){
      je.deleteRecord();
   }
   
   //Set redirect and info message for the parent record
   gs.addInfoMessage(current.label + " entry '" + fieldVal + "' deleted.");
   action.setRedirectURL(current.set.getRefRecord());
   
   //Delete the 'sys_history_line' record
   current.deleteRecord();
}

Update audit/journal entries

In order to update audit or journal entries through the ‘sys_history_line’ table, you need to open up security a bit so that you can change the ‘New’ field value. This can be easily accomplished by creating a new ‘write’ ACL on the ‘History [sys_history_line]’ table and the ‘New’ field. This ACL should limit the ‘write’ operation to the ‘admin’ role using the related list at the bottom of the ACL form.

Once you’ve opened up the security for the ‘New’ field, you can add the following UI action settings in the same way as the ‘Delete History Line’ UI action above.

‘Update History Line’ UI Action
Name: Update History Line
Table: History [sys_history_line]
Action name: update_history_line
Show insert: false
Show update: true
Client: true
Form button: true
Onclick: confirmUpdate()
Condition: gs.hasRole(‘admin’) && current[“new”].canWrite()
Script:

function confirmUpdate(){
   if(!g_form.getControl('new').changed){
      alert("Please enter a new value into the 'New' field.");
      return false;
   }
   if(confirm('Are you sure you want to permanently update this history line and all corresponding audit history?\n\nTHIS ACTION CANNOT BE UNDONE!')){
      //Call the UI Action and skip the 'onclick' function
      gsftSubmit(null, g_form.getFormElement(), 'update_history_line'); //MUST call the 'Action name' set in this UI Action
   }
   else{
      return false;
   }
}

//Code that runs without 'onclick'
//Ensure call to server-side function with no browser errors
if(typeof window == 'undefined')
   updateHistoryLine();

function updateHistoryLine(){
   var fieldVal = current["new"];
   var fieldName = current.field;
   var fieldLabel = current.label.toString();
   var setID = current.set.id.toString();
   
   //Query for and update the 'sys_audit' record
   var aud = new GlideRecord('sys_audit');
   aud.addQuery('documentkey', current.set.id);
   aud.addQuery('fieldname', fieldName);
   aud.addQuery('internal_checkpoint', current.internal_checkpoint);
   aud.query();
   if(aud.next()){
      aud.newvalue = fieldVal;
      aud.update();
   }
   
   //Query for and update the 'sys_journal_field' record (if applicable)
   var je = new GlideRecord('sys_journal_field');
   je.addQuery('element_id', current.set.id);
   je.addQuery('element', fieldName);
   je.addQuery('sys_created_on', current.update_time);
   je.query();
   if(je.next()){
      je.value = fieldVal;
      je.update();
   }
   
   //Refresh the history set
   if(typeof GlideHistorySet != 'undefined')
      GlideHistorySet(current.set.id.getRefRecord()).refresh();
   else
      Packages.com.glide.audit.HistorySet(current.set.id.getRefRecord()).refresh();  
   //Set redirect and info message for the new set record
   var newSet = new GlideRecord('sys_history_set');
   newSet.get('id', setID);
   gs.addInfoMessage(fieldLabel + " entry '" + fieldVal + "' updated.\nIf audit history list is empty, return to the parent record and select 'History -> List' to regenerate.");
   action.setRedirectURL(newSet);
}

Once the configuration above has been applied, users with the ‘admin’ role can navigate to the history of the record by right-clicking the record form header and clicking ‘History -> List’. Then, simply open the history record to modify and delete or update accordingly!