Change Management Workflow Approval Scripts in ServiceNow

/, Scripting/Change Management Workflow Approval Scripts in ServiceNow

Change Management Workflow Approval Scripts in ServiceNow

S

ervice-now.com provides a really cool Graphical Workflow engine to help manage workflows for different tasks. This graphical workflow engine is particularly useful when working with approvals for Change Requests. As a ServiceNow consultant I’ve found that change approvals usually fall into just a few different types but new administrators and consultants sometimes don’t know the best way to implement approvals. In this post I’ll share some of the common change workflow approval methods and scripts I’ve seen used before. I wouldn’t be surprised to see this list grow over time and I know I haven’t seen all of the common methods. If you have something you’ve used before please comment on this post or use the ‘Contact’ link above to send in your suggestion to share.

Simple Approvers Selection (Select Specific Group)

The simplest (and most common) type of approval is to select specific groups or users to approve the change. The screenshot below shows how you can use the standard reference field lookup to select a group to add as approvers to the change request. This method is commonly used when you want to have a specific group (like the CAB) approve at some point in your workflow.

Simple Approvers Selection (Drill to Related Records)

Another common method is to drill into the change request itself and pull the approvers from a field on the change. This method is commonly used when you want to have something like a manager approval for the person requesting the change (as shown below) or maybe when you want to have the assignment group on the change request approve.

Advanced Approvers Script (Approval Groups for all Change CIs)

The ServiceNow Graphical Workflow mechanism also allows you be even more complex in the selection of approvals for your change requests. You can use script with GlideRecord queries to return any users or groups you want to have approve. The one thing to keep in mind with these scripts is that you need to return group records if your workflow activity is a Group Approval type and user records if your workflow activity is a User Approval type.
One common request I’ve seen before that requires a script is to add approval groups based on the Configuration Items associated to the change request. Chances are you’ll have multiple CIs associated on the ‘Affected CIs’ related list for the change. This script queries for those records and then adds the groups from the ‘Approval Group’ field on each CI.

//Initialize an answer array to be returned
var answer = [];

//Add the primary CI approval group to the array
answer.push(current.cmdb_ci.change_control);

//Add the Affected CIs list approval groups to the array
var affCIs = new GlideRecord('task_ci');
affCIs.addQuery('task', current.sys_id);
affCIs.query();
while(affCIs.next()){
  answer.push(affCIs.ci_item.change_control);
}

Advanced Approvers Script (Approval Groups for all Impacted Change CIs)

There are some rare cases where clients want to pull approval groups from other CIs that will be impacted by the change based on CI relationships. For these cases you can use the CIUtils2 script I created to walk the CI relationship tree and find the impacted CIs that you need to add approvals for. In order to use these scripts you’ll need to add the CIUtils2 script include to your system first. You’ll also want to use this type of approval method sparingly since it involves a sometimes resource-intensive traversal of your CMDB (if you have hundreds of thousands of CIs). It’s also pretty expensive process-wise as well. Do you really want to require approval from every single group in your company when your network team needs to make a change to the core router? Maybe so, maybe not, but either way you’re probably talking about a lot of approvals because that CI impacts so many other CIs in your environment.

Impacted CIs approval scripts
–This first example returns Approval Groups for all of the Business Services impacted by the change request–

//Initialize a variable for the CIUtils2 Script include
var ciu = new CIUtils2();
//Initialize an answer array to be returned
var answer = [];

//Add any impacted business services for the change
var allCIs = ciu.cisAffectedByTask(current);

//Query for all CIs and return Approval Groups
for (var i = 0; i < allCIs.length; i++) {
   var cis = new GlideRecord('cmdb_ci');
   cis.addQuery('sys_id', allCIs[i]);
   cis.query();
   if (cis.next()) {
      answer.push(cis.change_control);
   }
}

–This example returns Approval Groups for all of the Business Services impacted by the change request AND Approval Groups for all directly Affected CIs on the change request. It utilizes the ‘CheckDuplicates‘ function I wrote to eliminate some unnecessary processing.–

//Initialize a variable for the CIUtils2 Script include
var ciu = new CIUtils2();
//Initialize an answer array to be returned
var answer = [];
//Initialize an array to gather and store all impacted CIs
var allCIs = [];

//Add the primary CI to the CI array
allCIs.push(current.cmdb_ci);

//Add the Affected CIs list to the CI array
var affCIs = new GlideRecord('task_ci');
affCIs.addQuery('task',current.sys_id);
affCIs.query();
while (affCIs.next()) {
   allCIs.push(affCIs.ci_item);
}

//For each directly affected CI on the change, add any impacted business services
for (var i = 0; i < allCIs.length; i++) {
   allCIs = allCIs.concat(ciu.cisAffectedByCI(allCIs[i]));
}

//Remove duplicate CIs from the array
allCIs = checkDuplicates(allCIs);

//Query for all CIs and return Approval Groups
for (var j = 0; j < allCIs.length; j++) {
   var cis = new GlideRecord('cmdb_ci');
   cis.addQuery('sys_id', allCIs[j]);
   cis.query();
   while (cis.next()) {
      answer.push(cis.change_control);
   }
}

function checkDuplicates(a) {
   //Check all values in the incoming array and eliminate any duplicates
   var r = []; //Create a new array to be returned with unique values
   //Iterate through all values in the array passed to this function
   o:for(var i = 0, n = a.length; i < n; i++){
      //Iterate through any values in the array to be returned
      for(var x = 0, y = r.length; x < y; x++){
         //Compare the current value in the return array with the current value in the incoming array
         if(r[x]==a[i]){
            //If they match, then the incoming array value is a duplicate and should be skipped
            continue o;
       }
      }
      //If the value hasn't already been added to the return array (not a duplicate) then add it
      r[r.length] = a[i];
     }
   //Return the reconstructed array of unique values
   return r;
}

Ensuring someone approves

One of the big problems you may encounter is what happens if the approvers don’t exist or aren’t active. The behavior in ServiceNow is to mark the approval activity as ‘Approved’ if no approvers are returned by the activity.

Change admin failsafe approval activity
–This script looks to see if an approver is returned, and if not it pulls in the ‘Change admins’ group as approvers for the approval activity.–

// Set the variable 'answer' to a comma-separated list of user/group ids or an array of user/group ids to add as approvers.
//
// For example:
//       var answer = [];
//       answer.push('id1');
//       answer.push('id2');

var answer = [];
//Requested by and assignment group manager approval
if(current.requested_by.manager.active){
   answer.push(current.requested_by.manager.sys_id);  
}
if(current.assignment_group.manager.active){
   answer.push(current.assignment_group.manager.sys_id);  
}

//Ensure 'Change Admins' group gets added if no other approvers (optional)
if(answer.length == 0){
   var grpName = 'Change Admins';
   //Query for change admins group
   var cGrp = new GlideRecord('sys_user_group');
   cGrp.get('name', grpName);
   answer.push(cGrp.sys_id);
}

Making a specific person the key approver

Some approval scenarios may also require that a specific individual has the full approval/rejection responsibility even as a member of a group. Even though others can approve or reject, a single person has the final say. Once they approve or reject, we move on from the activity. The following script can be used in an ‘Approval – User’ workflow activity to facilitate this type of setup.

Key Person Approval workflow activity
This script should be placed in the ‘Approval Script’ field in a ‘Approval – User’ workflow activity. The ‘Approval Script’ field displays when the ‘Wait for’ field is set to ‘Condition based on script’ as shown in the following screenshot.

//'keyApproverID' must approve or reject to advance approval
//Set the 'keyApproverID' variable to the sys_id of the user record of the key approver
var keyApproverID = '97000fcc0a0a0a6e0104ca999f619e5b';
if (approvalIDs) {
    if (approvalIDs['approved'].indexOf(keyApproverID) > -1) {
        answer = 'approved';
    }
    if (approvalIDs['rejected'].indexOf(keyApproverID) > -1) {
    answer = 'rejected';
    }
}
By | 2018-07-09T15:00:06-07:00 October 20th, 2010|Categories: Graphical workflow, Scripting|Tags: , , |18 Comments

About the Author:

18 Comments

  1. Travis Henning December 14, 2010 at 2:07 am - Reply

    This is great script and will work great in our organization. One thing I’m looking to do is build it into a UI Action rather than the workflow so the user can click a button when they’ve added the affected CIs. Is there anything different that needs to be done to push the approver groups from the array to the related list of group approves in the change record?

    • Mark Stanger December 14, 2010 at 6:09 am - Reply

      The scripts above are designed specifically for use in graphical workflow approval activities. Using them in a UI action would require the addition of some code to process the answer array and then create the approval records using a GlideRecord insert. You’re probably better off using your UI action to advance the workflow by setting some value in the change record. Then your workflow woulld have control over the approval process.

  2. Travis Henning December 14, 2010 at 9:11 am - Reply

    So if we added the approvers via GlideRecord insert, the Manual Approval Coordinator of the workflow that is already running against the change record won’t capture and manage the approvals?

    • Mark Stanger December 14, 2010 at 2:24 pm - Reply

      It should. I guess the point I was trying to make is that automated anything (in a workflow for example) is usually better than the manual alternative.

  3. Ed Perkins February 28, 2011 at 12:35 am - Reply

    Mark,

    When I tried putting this into a Fall 2010 level instance. The checkDuplicates function gives me an error on the continue o; statement. Any thoughs?

    Thanks.

    Ed

    Problem at line 66 character 24: Missing ‘;’

    continue o;

    Problem at line 66 character 25: Identifier ‘o’ already declared as live*

    continue o;

    Problem at line 66 character 25: Expected an assignment or function call and instead saw an expression.

    continue o;

    • Mark Stanger February 28, 2011 at 12:54 am - Reply

      You’ll get the same thing on the Spring 2011 build that’s on demo right now but you can ignore those errors. The script is still fine, but the syntax checker is being a little bit more stringent than it needs to be. It thinks that’s not valid syntax in the script, but the script will save and execute just fine.

  4. narinder November 8, 2011 at 6:49 am - Reply

    is there a way we can select specific group members for a group for approval. I dont want everyone in a group getting a approval request

    • Mark Stanger November 8, 2011 at 8:14 am - Reply

      You can, but you would have to have some indicator on those records so that you could identify them with a script. If you were to do something like that you would need to use a ‘User approval’ activity instead of a ‘Group approval’ activity though.

      • narinder November 8, 2011 at 9:28 am - Reply

        Ahhhh ok, soo if i do this in a user approval activity….. my script looks like this below

        answer = [];
        var stream_sys_id = current.u_deal_business_stream;
        var list = current.u_selected_stream_approvers.toString();
        var gr = new GlideRecord(‘u_deal_business_stream’);
        gr.addQuery(‘sys_id’,’=’,stream_sys_id);
        gr.query();
        if(gr.next())
        {
        var group = gr.u_business_stream;
        answer.push(group); < -----Group ID } var grm2 = new GlideRecord('sys_user_grmember'); grm2.addQuery('group','=', group); grm2.addQuery('user','=' '23rd2e3e23e23e23e23ee32fwewe3') grm2.query(); if(grm2.next()) { answer.push(grm2.user); <-------User ID } But thing is, it does'nt create the group approval..... just create the user approval......

        • Mark Stanger November 8, 2011 at 9:36 am - Reply

          That’s right. If you aren’t pulling all of the members of the group in, it should be treated as a user approval rather than a group approval. There’s no way I know of to do what you want with a group approval activity within a workflow. I suppose you could set up a business rule on the ‘sysapproval_approver’ table to abort the insertion of approval records for users who should not approve, but I’m not sure how that will scale across different workflows.

  5. narinder November 8, 2011 at 9:42 am - Reply

    Thanks for the feedback. Got to think about plan B now

  6. Rose February 24, 2012 at 1:40 pm - Reply

    I’m trying to add an advanced approval script in our Change System. The form has a checkbox for each of our 3 regions, AMER, APAC, and EMEA. A change can be submitted for 1, 2 or all 3 regions. There is a Change Manager Group that contains members from each of the regions. I only want approvals to go to users from the regions that are selected on the form. When I run the script in Scripts – Background, I get an array of all the right users. When I run the workflow, I only get the last user from each region. Any ideas?

    answer = [];
    var amer = current.u_amer;
    var apac = current.u_apac;
    var emea = current.u_emea;

    if (amer == true){
    var amercm = new GlideRecord(‘sys_user_grmember’);
    amercm.addQuery(‘group.name’,’Change Manager’);
    amercm.addQuery(‘user.u_region’,’AMER’);
    amercm.query();
    while(amercm.next()){
    answer.push(‘amercm.user’);
    }
    }

    if (apac == true){
    var apaccm = new GlideRecord(‘sys_user_grmember’);
    apaccm.addQuery(‘group.name’,’Change Manager’);
    apaccm.addQuery(‘user.u_region’,’APAC’);
    apaccm.query();
    while(apaccm.next()){
    answer.push(apaccm.user);
    }
    }
    if (emea == true){
    var emeacm = new GlideRecord(‘sys_user_grmember’);
    emeacm.addQuery(‘group.name’,’Change Manager’);
    emeacm.addQuery(‘user.u_region’,’EMEA’);
    emeacm.query();
    while(emeacm.next()){
    answer.push(emeacm.user);
    }
    }

    • Mark Stanger February 24, 2012 at 2:23 pm - Reply

      There’s a little trick you need to use when pushing items into an array from a GlideRecord query. Add ‘toString()’ to the end of all of the items you’re pushing into the array like this…

      answer.push(emeacm.user.toString());

  7. Rose February 24, 2012 at 2:32 pm - Reply

    Awesome… Thanks so much… Works Great!!!

  8. Wes August 5, 2013 at 12:59 pm - Reply

    Do you have anything that breaks up the approval list? Ie if you have 2 groups (dev and qa) and one person from each group. you see in the approval list Dev and qa grouped separately. That way if someone that isn’t familiar with the people know the QA person signed off on it but the dev person has not, vise versa… thx

  9. Tony May 17, 2017 at 5:23 pm - Reply

    Similar to a key person approval script. How can achieve a key group approval script. For example, i want to say that a particular group has the final say so. Any member in that group approves or rejects will override all responses.

    Thank you!

    • Jim Pisello July 14, 2017 at 10:25 am - Reply

      Hi Tony,

      There are a number of possible methods for accomplishing this, depending on your specific business needs. Examining them is probably beyond the scope of this blog. We’d recommend that you post your question on the ServiceNow Community Forum.

  10. Change Management July 22, 2019 at 5:13 am - Reply

    Well done. You did such an amazing job. Keep it up.

Leave A Comment