Manually Add a Record to an Update Set

//Manually Add a Record to an Update Set

Manually Add a Record to an Update Set

T

he primary mechanism in Service-now for transporting configuration changes between instances is the System Update Sets functionality. If you’ve worked with Service-now much at all, you’re familiar with this functionality and know that it can be a huge time saver. You also probably know that there are a few gotchas to update sets that can cause problems if you don’t pay attention to them. One of these gotchas is that not all changes you make in your instance get recorded in update sets. Of course, this is by design and it’s usually a good thing that saves you from problems. There are, however, some situations where you need to capture updates to a specific record in a table even though you don’t want all of the records in that table being captured. In this article I’ll show you a solution to this problem.

Manual Update Set Inclusion

First, let me give a couple of examples where this might come in handy…

Example 1

You need to modify the ‘Autoclose incidents’ Scheduled job to run once a day instead of once every hour. You can make this modification in your development instance, but it won’t be captured in an update set because the ‘sys_trigger’ table doesn’t have the ‘update_synch’ attribute set. There’s a great reason for this too. The last thing you want is for all of the records in the ‘sys_trigger’ table (SLA jobs, Inactivity monitors, Trend jobs, etc.) recording every update they ever make to an update set. You would end up with literally thousands of garbage updates in your update set in a matter of days just from normal system operations!

Until now, the solution to this problem was to export and import the file between instances as an added, manual step to your code migration process. It works (and in some cases may still be necessary), but it’s one more step that you would probably rather not have to think about.

Example 2

You’ve created a new table and application in your instance. The records in this table need to automatically receive a number when they are created so you create a new record in the ‘Number maintenance’ (‘sys_number’) table. There’s only one problem. Since the number maintenance table doesn’t include the ‘update_synch’ attribute those records won’t be included in your update set. Again, there’s a great reason for this. Number maintenance records get updated every single time you create a new task record, template, or software license. You don’t want all of that extra numbering garbage coming over to your production instance and messing things up there. All you want is to record the creation of a single Number maintenance record for your new table. Again, you’re forced to fall back on the manual export/import process to move that single record between instances.

The Solution:

PLEASE understand that this is in NO way a replacement for the out-of-box Update Sets functionality! The same guidelines recorded on the Service-now wiki for tracking customizations apply. This solution SHOULD NOT be used as a way to capture large amounts of data modifications that should be moved via import sets.

So here’s the way I came up with to fix this problem. Simply create a new UI action record as shown below on the table(s) that you need to record single-record updates on. Once you add the UI action, you can click the ‘Force to Update Set’ link on any record in that table to add it to your currently-selected update set. The UI action does three things…

  1. Check to make sure the current table isn’t already recording updates
  2. Push the current record into the currently-selected update set
  3. Reload the form and add an info. message indicating the addition
‘Force to Update Set’ UI Action
Name: Force to Update Set
Table: Wherever you need it!
Action name: force_update
Form link: True
Condition: gs.hasRole(‘admin’)
Script

//Commit any changes to the record
current.update();

//Check to make sure the table isn't synchronized already
var tbl = current.getTableName();
if(tbl.startsWith('wf_') || tbl.startsWith('sys_ui_') || tbl == 'sys_choice' || current.getED().getBooleanAttribute('update_synch') || current.getED().getBooleanAttribute('update_synch_custom')){
   gs.addErrorMessage('Updates are already being recorded for this table.');
   action.setRedirectURL(current); 
}
else{
   //Push the update into the current update set
   var um = new GlideUpdateManager2();
   um.saveRecord(current);

   //Query for the current update set to display info message
   var setID = gs.getPreference('sys_update_set');
   var us = new GlideRecord('sys_update_set');
   us.get(setID);

   //Display info message and reload the form
   gs.addInfoMessage('Record included in <a href="sys_update_set.do?sys_id=' + setID + '">' + us.name + '</a> update set.');
   action.setRedirectURL(current);
}

Forcing an update from a Background Script

In some cases, it might not be desirable to include a UI action to perform this function. In that case, you can run a script from the ‘Scripts – Background’ module to force the record into an update set. Just replace the table name and sys_id of the record in the script below.

//Query for the record
var rec = new GlideRecord('sys_number');
rec.get('973c8e8a9d022000da615b13b3a22f32');
//Push the record into the current update set  
var um = new GlideUpdateManager2();
um.saveRecord(rec);

Attachments can also be forced into an update set by including the attachment and attachment doc records. Just pass in the sys_id the same way. Thanks to John Andersen for the idea here. He’s created a script to add a full record and associated attachments all at once that you can find on his blog.

//Query for the record
var rec = new GlideRecord('sys_attachment');
rec.get('973c8e8a9d022000da615b13b3a22f32');
addAttachmentToUpdateSet(rec);

function addAttachmentToUpdateSet(attachmentGR) {
   var um = new GlideUpdateManager2();
   um.saveRecord(attachmentGR);

   var attdoc = new GlideRecord('sys_attachment_doc');
   attdoc.addQuery('sys_attachment', attachmentGR.sys_id);
   attdoc.orderBy('position');
   attdoc.query();
   while(attdoc.next()){
      um.saveRecord(attdoc);
   }
}
By | 2017-02-09T23:56:08+00:00 April 26th, 2011|Categories: System Definition|Tags: , |24 Comments

About the Author:

24 Comments

  1. Matt Gaide April 26, 2011 at 9:17 am - Reply

    This is sweet!

  2. Joe Watts May 11, 2011 at 6:59 am - Reply

    Very cool time saver, cheers!

  3. Tony Fugere September 21, 2011 at 8:51 am - Reply

    You say “Wherever you need it!”

    What if we make it a Global UI Action? Obviously, the admin should not be Forcing INC’s into an Update Set, but since it is “gs.hasRole(‘admin’)”, admin’s should know to play nice… 🙂

    What’s your thoughts on this approach to the UI Action? Am I treading on deadly ground?

    • Mark Stanger September 21, 2011 at 9:06 am - Reply

      I considered that approach but decided against it because ‘should know’ is not the same as ‘will know’ :). There are some places in the tool that it’s not completely clear whether or not something is already being handled by update sets…form and list personalizations for example. Adding these to an update set in the wrong way can cause serious problems in an instance. In my experience, there are really only a handful of tables where including this UI action would be useful. My thinking was that I didn’t want to suggest that this is something that could be used anywhere in the system without causing an issue. Even if you or the admin you’re working with understands what’s going on, the odds are that the next person to come along isn’t going to understand. In my opinion it’s not that much work, and it’s A LOT safer to only add this to the specific tables where it’s required…as the issue comes up.

  4. Jason February 27, 2012 at 1:04 am - Reply

    Hi Mark

    Slightly off topic, can you remove the chart color entries in the update set and whats the impact if any?

    I’m assuming that if you remove them from the update set they are recreated when the report(s) are run for the first time on the recipient environment.

    cheers

    • Mark Stanger February 27, 2012 at 5:32 am - Reply

      I haven’t ever tried it, but I would assume you’re correct.

  5. Ash March 5, 2013 at 12:17 am - Reply

    Hi,

    Can we capture already created table in update set using sys_id?

    • Mark Stanger March 5, 2013 at 7:04 am - Reply

      Not with this solution, but you should be able to go to the ‘sys_update_xml’ table and find the updates and move them into your update set. You can create a quick test table in the SN demo instance to see what the update XML looks like.

  6. Ash March 5, 2013 at 10:38 pm - Reply

    Hey Mark,
    Your idea worked for me….Thank you….:)

  7. Michael McCall July 1, 2013 at 3:22 pm - Reply

    Similar to Example 1, I’m using this for moving scheduled jobs between instances, but I’m creating new ones. I went with adding this UI Action to the Scheduled Job [sysauto] table, and that actually means that a corresponding [sys_trigger] record doesn’t get carried with the update set, so my jobs don’t run in the destination instance until I manually change them (and create a [sys_trigger] record that way).

    What’s the best approach for moving scheduled jobs, then? (Is it just modifying the code to additionally store a [sys_trigger] record?)

    • Mark Stanger July 1, 2013 at 3:47 pm - Reply

      That should be possible, but you’ll want to make sure that you don’t end up with duplicate ‘sys_trigger’ entries. I think the best method is just to make an update to the scheduled jobs after migration.

      • Michael McCall July 1, 2013 at 4:20 pm - Reply

        I’m personally fine with making the manual update. If nothing else, I hope this helps those who may not realize that their jobs aren’t running in production. (I only found out when users noticed that scheduled dates had passed without their records being generated!)

        As always, thanks for another useful Service-Now trick!

  8. Ravish Shetty July 27, 2014 at 11:35 pm - Reply

    In my case, i needed to always capture data in my custom table.
    I added the background script code as a async business rule (insert/update) to a custom table.
    One more way in which this script can be useful.

  9. Travis October 23, 2014 at 3:33 pm - Reply

    Thanks for posting this! I had 50+ records I wanted to add to an update set but I didn’t want to go through and update them all. So a background script with your code and a while loop worked some magic!

    For reference:

    var rec = new GlideRecord('sys_soap_message_function');
    rec.addQuery( 'soap_message', 'sysidhere' );
    rec.query();

    while( rec.next() ) {
       gs.log( "Function: " + rec.function_name );
      //Push the record into the current update set  
      if (typeof GlideUpdateManager2 != 'undefined')
         var um = new GlideUpdateManager2();
      else
         var um = new Packages.com.glide.update.UpdateManager2();
      um.saveRecord(rec);
    }

    gs.log( "done" );
  10. Marcelo Moreli October 6, 2015 at 3:46 pm - Reply

    Hi Mark,
    Little observation: If you add some record (let’s say a KB) to the update set, do another update to it (something you remembered), and add it again to the update set using this UI action, you will have both Updates (XMLs) on the Update set.

    Problem with that is that you cannot foresee which version will be finally installed on the environment, normally an old version.. This causes a lot of trouble for me.

    I’m working something out to remove the previous record before inserting the new one, if we already have something for that particular record on the same update set.

    • Mark Stanger October 6, 2015 at 3:55 pm - Reply

      Marcelo,

      I’m not sure what’s going on in your system, but I’ve been using this solution for several years now and have never had that problem reported, nor experienced it myself. I just tested again to be sure. You might add this code to a demo environment to confirm. If you can reproduce the error in a system I can get to I’d be happy to look further.

  11. Marcelo Moreli October 7, 2015 at 7:43 am - Reply

    Hi Mark,

    You are right. I’m not sure what I did do before, but I cannot reproduce it! Probably it was my mistake,

    Sorry for that

  12. Trey Carroll October 31, 2016 at 9:59 am - Reply

    We tried to use this (awesome) UI Action today ( which we typically use for capturing updates to groups and roles ) to capture updates to a scheduled job, however it failed to capture the update with the message “~ updates are already being captured for this table”. Problem was traced to a bug in the code:

    current.getED().hasAttribute(‘update_synch_custom’)

    (sysauto_report has the attribute but it is false.)

    After changing the line to:

    /true/i.test(current.getED().getAttribute(‘update_synch’))

    we were able to capture the update.

    Thanks, Mark, for the awesome contribution!

    • Mark Stanger October 31, 2016 at 11:11 am - Reply

      Thanks Trey, it’s great to hear from you! I may be mistaken, but I don’t see that anywhere in my code above. I’m actually testing for the value of those attributes in my code above (which I think is correct and does what your code does). Have you tried the code above to see if that works better?

  13. Terryio March 28, 2017 at 3:36 am - Reply

    Thanks Mark, it’s exactly that I looking for !

  14. Marcelo Moreli May 3, 2017 at 6:37 am - Reply

    Hi Mark!
    Any idea on how to create something similar in order to “Add to Team Development Local Changes” ?

    I was thinking on adding it to sys_sync_changes manually, but I didn’t test it yet.

  15. Zach Bloomquist July 20, 2017 at 9:36 am - Reply

    Thanks for this time-saver!

  16. Peter R. September 27, 2017 at 1:18 am - Reply

    How can I add to update set deleted record?

    I want to store the “DELETE” flag, so the record on target instance will be deleted.

    The code below is (of course) not working:

    gr.deleteRecord();
    var um = new GlideUpdateManager2();
    um.saveRecord(gr); // does not work, because gr is deleted….

    Any ideas please?

Leave A Comment