Script includes Archives - ServiceNow Guru https://servicenowguru.com/tag/script-includes/ ServiceNow Consulting Scripting Administration Development Fri, 26 Jul 2024 12:31:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.2 https://servicenowguru.com/wp-content/uploads/2024/05/cropped-SNGuru-Icon-32x32.png Script includes Archives - ServiceNow Guru https://servicenowguru.com/tag/script-includes/ 32 32 Easily Move Customer Updates Between Update Sets https://servicenowguru.com/system-definition/easily-move-customer-updates-between-update-sets/ https://servicenowguru.com/system-definition/easily-move-customer-updates-between-update-sets/#comments Fri, 26 Jul 2024 12:31:03 +0000 https://servicenowguru.com/?p=15964 As ServiceNow developers, we often encounter scenarios where we need to migrate configurations from one update set to another. Traditionally, this involves manually editing each customer update record to change its update set, which can be tedious and time-consuming, especially when dealing with a large number of updates. This article demonstrates how to streamline this

The post Easily Move Customer Updates Between Update Sets appeared first on ServiceNow Guru.

]]>
As ServiceNow developers, we often encounter scenarios where we need to migrate configurations from one update set to another. Traditionally, this involves manually editing each customer update record to change its update set, which can be tedious and time-consuming, especially when dealing with a large number of updates.

This article demonstrates how to streamline this process by creating a UI Action that allows you to move multiple customer updates between update sets with a single click.

Problem Statement
When you make configurations in your instance, there are times when you need to move these updates to a different update set. The conventional method requires opening each customer update record individually and changing the update set field to the desired update set. This method becomes impractical when dealing with numerous updates.

Solution
To overcome this challenge, you can create a UI Action that facilitates the movement of multiple customer updates to another update set in one go. This approach not only saves time but also reduces the risk of manual errors.

To begin, navigate to System Definition > UI Actions and create a new UI Action:

UI Action
Name: Move Updates to Current US
Table: sys_update_xml
Action Name: move_updates_to_current_us
Show insert: true
Show update: true
Client: true
List v2 Compatible: true
List Choice: true
Onclick: showConfirmDialog()
Script:

function showConfirmDialog() {

	var entries = g_list.getChecked();
	if (!entries || entries.length == 0)
		return;

	// this function runs when the modal close
	var callback = function () {

		// To send parameters to the server script we should use GlideAjax
		var ga = new GlideAjax('MoveUpdatesToCurrentUS');
		ga.addParam('sysparm_name', 'moveEntries');
		ga.addParam('sysparm_entry_ids', entries);

		// GlideAjax callback to refresh the related list
		var afterMove = function () {
			GlideList2.get(g_form.getTableName() + '.sys_update_xml.update_set').setFilterAndRefresh('');
		};

		ga.getXMLAnswer(afterMove.bind(this));
		return true;
	};

	var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;
	var dialog = new dialogClass('glide_confirm_standard');
	dialog.setTitle(new GwtMessage().getMessage('Confirmation'));
	dialog.setPreference('warning', true);
	dialog.setPreference('title', new GwtMessage().getMessage('move_update_set_entries'));
	dialog.setPreference('onPromptComplete', callback.bind(this));
	dialog.render();
	
}

Two points I would like to highlight in this script.
– As we need to pass the selected records as a parameter to the server script, we need to use GlideAjax.
– For the callback function I needed to update only the related list. Initially I could only update the entire record but a quick search here on the portal led me to an article explaining exactly what I needed.

For the modal message to be displayed correctly, we need to create a record in the sys_ui_message table with the text we want.  Navigate to System UI > Messages and create a new message:

Now we need to create the Script Include that will perform all the necessary validations and actions. Navigate to System Definition> Script Includes and create a new script:

Script Include
Name: MoveUpdatesToCurrentUS
Accessible from: All application scopes
Client callable: true
Active: true
Description: Ajax helper class for helping the [Move Updates to Current US] UI action
Script:

var MoveUpdatesToCurrentUS = Class.create();

MoveUpdatesToCurrentUS.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    moveEntries: function () {

        var arrMoved = [];
        var objMoved = {};
        var arrNotMoved = [];
        var objNotMoved = {};
        var currUS_ID = '';
        var customUpdateLink = '';
        var customUpdateType = '';
        var baseURL = gs.getProperty('glide.servlet.uri');

        //create a link to the syslog to return the logs created by this SI on last 15 minutes 
        var sysLogQuery = 'sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()^messageLIKEMoveUpdatesToCurrentUS';
        sysLogQuery = encodeURIComponent(sysLogQuery); //https://www.w3schools.com/jsref/jsref_encodeuricomponent.asp
        var sysLogURL = baseURL + 'syslog_list.do?sysparm_query=' + sysLogQuery;
        
        // get the user current US
        var currUS = gs.getPreference('sys_update_set');
        var grUS = new GlideRecord('sys_update_set');		

        if (grUS.get(currUS)) {
            currUS_ID = grUS.getUniqueValue();
        } else {
            gs.info('[MoveUpdatesToCurrentUS] Error: Unable to get Current US.');
            gs.addErrorMessage('There was an error. Please review the <a href="http://'%20+%20sysLogURL%20+%20'">system logs</a> for more details.');
            return false;
        }

        // get the variable sent by the UI Action
        var entries = this.getParameter('sysparm_entry_ids');

        //get all selected custom updates
        var customUpGr = new GlideRecord('sys_update_xml');
        customUpGr.addEncodedQuery('sys_idIN' + entries);
        customUpGr.query();

        while (customUpGr.next()) {
            
            //check if the user can write in the table
            if (customUpGr.canWrite()) {

                //create a link to the current update and get its type
                customUpdateLink = baseURL + 'sys_update_xml.do?sys_id=' + customUpGr.getValue('sys_id');
                customUpdateType = customUpGr.getValue('type');

                // check if it trying to move do a diferent US
                if (customUpGr.getValue('update_set') == currUS_ID) {

                    objNotMoved = {
                        sys_id: customUpGr.getUniqueValue(),
                        reasonHTML: 'Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> is already in your currently US.',
                        reasonText: 'Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - is already in your currently US.'
                    };

                    arrNotMoved.push(objNotMoved);

                } else {

                    // move the customer update to de current US
                    try {

                        customUpGr.setValue('update_set', currUS);
                        customUpGr.update();
                        
                        objMoved = {
                            sys_id: customUpGr.getUniqueValue(),
                            reasonHTML: 'Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> was moved to your currently US.',
                            reasonText: 'Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - was moved to your currently US.'
                        };

                        arrMoved.push(objMoved);

                    } catch (e) {

                        objNotMoved = {
                            sys_id: customUpGr.getUniqueValue(),
                            reasonHTML: 'Unable to move the Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> due: ' + e,
                            reasonText: 'Unable to move the Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - due: ' + e
                        };
    
                        arrNotMoved.push(objNotMoved);

                    }

                }				

            } else {

                objNotMoved = {
                    sys_id: customUpGr.getUniqueValue(),
                    reasonHTML: 'Unable to move the Custom US <a href="http://'%20+%20customUpdateLink%20+%20'">' + customUpdateType + '</a> due to ACL restrictions.',
                    reasonText: 'Unable to move the Custom US ' + customUpdateType + ' - ' + customUpGr.getValue('sys_id') + ' - due to ACL restrictions.'			
                };

                arrNotMoved.push(objNotMoved);

            }			

        }

        // create 2 messages: one to the syslog and one to show to user
        var sysLogMessage = '[MoveUpdatesToCurrentUS]' + '\r\r';

        if (arrMoved.length > 0) {
            
            var successScreenMessage = 'Move Updates to Current US Action' + '<br><br>';

            sysLogMessage += 'Custom Updates moved:\r';
            successScreenMessage += 'Custom Updates moved:<br>';

            for (var i = 0; i < arrMoved.length; i++) {
                sysLogMessage += arrMoved[i].reasonText + '\r';
                successScreenMessage += arrMoved[i].reasonHTML + '<br>';
            }

            gs.addInfoMessage(successScreenMessage);

        }

        if (arrNotMoved.length > 0) {

            var failureScreenMessage = 'Move Updates to Current US Action' + '<br><br>';

            sysLogMessage += '\r\rCustom Updates NOT moved:\r';
            failureScreenMessage += 'Custom Updates NOT moved:<br>';

            for (var x = 0; x < arrNotMoved.length; x++) {
                sysLogMessage += arrNotMoved[x].reasonText + '\r';
                failureScreenMessage += arrNotMoved[x].reasonHTML + '<br>';
            }

            gs.addErrorMessage(failureScreenMessage);

        }		

        gs.info(sysLogMessage);
        
        gs.addInfoMessage('Customer Update(s) transfer has been completed. Please review the <a href="http://'%20+%20sysLogURL%20+%20'">system logs</a> for more details.');

        return true;

    },

    type: 'MoveUpdatesToCurrentUS'
});

Using

  • Open the update set from which you want to move few customer updates
  • Go to the Customer Updates Related List
  • Select all the updates you want to move
  • Click on [Move Updates to Current US] in the actions dropdown menu

  • If everything works fine all selected customer updates have been moved to your Current US and you will see a message – simple as that.

  • If any problems occur during the transfer, you will be notified.

  • Everything will be recorded in syslog

Benefits

  • Efficiency: Moves multiple updates simultaneously, significantly reducing the time required.
  • Accuracy: Minimizes the risk of errors associated with manual updates.
  • Usability: Provides a straightforward and user-friendly method for managing update sets.

 

By implementing this UI Action, you can enhance your productivity and ensure a smoother workflow when managing update sets in ServiceNow. This method is particularly beneficial in complex development environments where rapid and accurate configuration management is essential.

The post Easily Move Customer Updates Between Update Sets appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/system-definition/easily-move-customer-updates-between-update-sets/feed/ 2
Harnessing the Power of Dynamic Filters in ServiceNow https://servicenowguru.com/reporting/harnessing-power-dynamic-filters-servicenow/ https://servicenowguru.com/reporting/harnessing-power-dynamic-filters-servicenow/#comments Fri, 04 Sep 2015 12:14:54 +0000 https://servicenowguru.wpengine.com/?p=5637 ServiceNow adds a ton of great functionality to each new product release. Often times, the most helpful and useful features (at least to a long-time user of the system) are enhancements to simplify or improve on existing functionality. Unfortunately, these are often some of the most under-appreciated and end up getting lost in the marketing

The post Harnessing the Power of Dynamic Filters in ServiceNow appeared first on ServiceNow Guru.

]]>
ServiceNow adds a ton of great functionality to each new product release. Often times, the most helpful and useful features (at least to a long-time user of the system) are enhancements to simplify or improve on existing functionality. Unfortunately, these are often some of the most under-appreciated and end up getting lost in the marketing hype of all of the brand new capabilities that you may or may not use. One such example that recently arrived to ServiceNow is ‘Dynamic filters’. In this post, I’ll share what dynamic filters are, and show how you can extend and leverage this capability to improve your own ServiceNow system.

DynamicallyFilteredList

The ServiceNow wiki does a decent job of explaining the basic concept and usage of dynamic filters. There are 1000’s of scenarios where users of the system need to query for information, display a list of data, generate a report, or values from a reference field based on a specific, logical filter criteria. There are many examples of this…my Group’s work, my approvals, records assigned to other members of my groups, etc. These types of scenarios, though simple on the surface, actually require sometimes very complex code to query for and return the correct data. ServiceNow has always allowed you to do this, of course, but the approach (asking a typical end-user to remember and correctly populate a specific query string with a function call directly in the filter) really isn’t one that works well — even if that user happens to be an admin of the system. Those query strings generally look something like this and can be pasted directly into any filter criteria to return information…

javascript:gs.getUserID();
javascript:getRoledUsers();
etc...

The general idea behind dynamic filters is to allow a ServiceNow administrator to pre-define the filter query logic to return the correct information from a back-end function, then set up a dynamic filter definition to point to that information via a simple label invoked from any filter field criteria in the system. These dynamic filters can be as flexible and complex as you need them to be, but the end-user doesn’t need to know or understand any of that complexity in order to benefit from them!

There are several of these dynamic filters defined out-of-box that you can use right away as examples for creating your own. You can find them under ‘System Definition -> Dynamic Filter Options’ in your left nav. For more complex scenarios, you’ll actually point your dynamic filter to a back-end Script Include function that contains all of the logic and does the heavy lifting.

One common filter criteria that I hear about all of the time that isn’t handled out-of-box is to filter for records associated to members of my groups via some user field (usually an assignment or ownership of some sort). Tickets assigned to members of my groups, outstanding approvals for members of my groups, etc. This configuration can be added to your system by following a few simple steps as shown below…

    1. Create a new ‘On Demand’ Script Include function.

I’ve written about this capability before so you can reference that article for more details if you need. Creating this script include will allow us to easily call a reusable function to return the data we want…in this case a list of users that belong to the same group as the current user. The basic idea for this function is to get a user’s groups, then find the active group members sys_id values associated with those groups and add them to an array to be returned. You can navigate to ‘System Definition -> Script Includes’ in your left nav to create this. Don’t forget that the ‘Name’ value of any on demand script include (like this one) needs to exactly match the name of the function you’re calling in the script!

‘getMyGroupMembers’ Script Include
Name: getMyGroupMembers
Active: True
Client callable: True
Description: Queries for members of groups that the currently logged-in user is also a member of.
Script:

function getMyGroupMembers(){
var myGroups = gs.getUser().getMyGroups();
var groupsArray = new Array();
var it = myGroups.iterator();
var i=0;
var groupMemberArray = new Array();
while(it.hasNext()){
var myGroup = it.next();
//Query for group members
var grMem = new GlideRecord('sys_user_grmember');
grMem.addQuery('group', myGroup);
//Only return active users
grMem.addQuery('user.active', true);
grMem.query();
while(grMem.next()){
//Add to user sys_id to array
groupMemberArray.push(grMem.user.toString());
}
i++;
}
return groupMemberArray;
}
    1. Create a new ‘Dynamic Filter’ record

      The on-demand function is great and allows you to easily return the data you want. From any place you can call scripts in the system. This is fantastic for business rules, workflow scripts, etc. but the average user running a report or filtering a list is not going to know (nor should they need to know) the exact syntax and function name to call. This is where Dynamic Filters come in! We can wrap that script call in a friendly label that displays in any filter anywhere in the system so that a normal human being can access it as well. Your dynamic filter for the script above should look just like I’ve shown in the screenshot below. You can create it by navigating to ‘System Definition -> Dynamic Filter Options’

DynamicFilter

 

NOTE: One interesting bit of information I discovered while working with dynamic filters is the way that the system handles the encoded query strings for them. You end up with a query string (that you could reuse) that looks like this…

assigned_toDYNAMIC1a570fd90856c200aa4521695cf1eb24

The ‘DYNAMIC’ keyword indicates the use of a dynamic filter, and what follows is the sys_id of the corresponding dynamic filter record.

The end result is a nice, dynamic filter option for filtering where the user listed in a user field is a member of one of your groups! This is just one example of a fantastic capability in ServiceNow. There are lots of other use cases that you can add using this same approach.

DynamicallyFilteredList

The post Harnessing the Power of Dynamic Filters in ServiceNow appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/reporting/harnessing-power-dynamic-filters-servicenow/feed/ 10
Advanced Reference Qualifier Using a Script Include https://servicenowguru.com/script-includes-scripting/advanced-reference-qualifier-script-include/ https://servicenowguru.com/script-includes-scripting/advanced-reference-qualifier-script-include/#comments Tue, 21 Jun 2011 16:22:57 +0000 https://servicenowguru.wpengine.com/?p=3751 Reference qualifiers are a powerful tool that every ServiceNow administrator and consultant should have in their tool belt. They allow you to dynamically filter the available options from a reference field. The ServiceNow wiki has some good documentation on this topic so I won’t re-hash that here. What I do want to address is the

The post Advanced Reference Qualifier Using a Script Include appeared first on ServiceNow Guru.

]]>
Reference qualifiers are a powerful tool that every ServiceNow administrator and consultant should have in their tool belt. They allow you to dynamically filter the available options from a reference field. The ServiceNow wiki has some good documentation on this topic so I won’t re-hash that here. What I do want to address is the topic of Advanced Reference Qualifiers…specifically how to leverage a Script Include instead of a global Business Rule to run your qualifier script.

Reference Qualifier Script Include

Up until recently, the only way to get Advanced Reference Qualifiers to work was to create a global business rule to run the qualifier script. This works great, but it results in a global script that gets loaded all the time when it really isn’t necessary. While this isn’t something that causes any real problems in practice, it could cause performance issues if used to the extreme. The best practice guidance for advanced reference qualifiers should be to use a Script Include rather than a global Business Rule to run the qualifier script. Using a Script Include means that the script only gets loaded and used when you actually need to use it! In this article I’ll show you how this can be done using a common example of filtering the ‘Assignment group’ to display only groups for the ‘Assigned to’ value.

The first piece is the ‘Reference qual’ field value on the dictionary entry of the reference field (Assignment group in this case). The ‘javascript:’ prefix is the same, but you need to reference your Script Include function instead of the business rule function. In this case, I’m using a Script Include named ‘u_backfillAssignmentGroup’. Since these scripts can potentially interfere with each other, it’s best to prefix any of your custom scripts with ‘u_’ or something similar in order to distinguish them from any that ServiceNow may introduce in the future.

javascript:u_backfillAssignmentGroup();

The other piece is obviously the Script Include. Since Script Includes now allow you to use On-Demand functions, your script is identical to the one you would use in a global business rule.

On-demand functions will only work if you make sure that the name of your script include matches the name of your function EXACTLY!
function u_backfillAssignmentGroup() {
   var gp = ' ';
   var a = current.assigned_to;
   
   //return everything if the assigned_to value is empty
   if(!a)
      return;
   
   //sys_user_grmember has the user to group relationship
   var grp = new GlideRecord('sys_user_grmember');
   grp.addQuery('user',a);
   grp.query();
   while(grp.next()) {
      if (gp.length > 0) {
         //build a comma separated string of groups if there is more than one
         gp += (',' + grp.group);
      }
      else {
         gp = grp.group;
      }
   }
   // return Groups where assigned to is in those groups we use IN for lists
   return 'sys_idIN' + gp;
}

The post Advanced Reference Qualifier Using a Script Include appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/script-includes-scripting/advanced-reference-qualifier-script-include/feed/ 20
Restrict Report Table Selection by Role https://servicenowguru.com/reporting/restrict-report-table-selection-role/ https://servicenowguru.com/reporting/restrict-report-table-selection-role/#comments Mon, 20 Dec 2010 13:51:01 +0000 https://servicenowguru.wpengine.com/?p=3108 Update: ServiceNow has introduced new ACL functionality that allows you to do basically the same thing as described in this article. They’re also taking a drastically new direction with reporting starting with the Fuji release, but being introduced in the back-end code in the Eureka release. As such, the solution described here is obsolete and

The post Restrict Report Table Selection by Role appeared first on ServiceNow Guru.

]]>
Update: ServiceNow has introduced new ACL functionality that allows you to do basically the same thing as described in this article. They’re also taking a drastically new direction with reporting starting with the Fuji release, but being introduced in the back-end code in the Eureka release. As such, the solution described here is obsolete and no longer supported. Check out this wiki article to see how to restrict report table selection using standard ServiceNow ACLs.

Reporting in Service-now.com is usually very simple. One challenge I’ve seen before with the Service-now reporting interface is that it displays a lot of tables to most users as tables they can report on. The list of reportable tables is controlled by system security ACLs. If a user can read a table in the system they can also report on that table. There are many cases where users need to be able to read from a table in order for the system to work correctly for them, but you may not want users running reports on that table just because they see it as an option in the reporting interface. I’ve created the Restrict Report Tables update set as a solution to this problem. It gives Service-now report administrators granular control over which tables in the system show up as selectable options in the report interface.

Before



After


Please note that this functionality has no bearing on overall table security in your system. Users can still run filters, create bar and list charts, and otherwise run ad-hoc list reporting on any table in the system that they have read access to. If a user does not have read access to a table (as defined by your security ACLs) the functionality described here honors that security.

This customization includes the following features:

  • Limit the ‘Table’ options that display in the reporting interface by role
  • Simple script manipulation to limit any override roles for this behavior

Core components:

This solution includes three primary components. The first is the ‘Report Tables’ table definition along with the ‘Report Tables’ module. This component is restricted to users with the ‘admin’ or ‘report_admin’ role by default.

The second component is the ‘RemoveReportOptions’ UI script which handles the showing and hiding of report form elements. It also controls the override roles (‘admin’ and ‘report_admin’ by default) for the entire behavior.

Finally, the ‘GetReportTables’ Script Include handles the back-end query and selection of reportable tables based on the user role.

Usage:

This customization is only offered as an update set through ServiceNowGuru.com. You’ll need to install an update set into your instance to get this functionality. Installation and download instructions can be found below.

There are really only two configuration options that you’ll need to worry about. The first is the ‘Report Tables’ definition as shown in the screenshot below. Simply define the tables (and associated roles) that the tables should be available to. The second is the override roles contained in the ‘RemoveReportOptions’ UI script. By default, the ‘admin’ and ‘report_admin’ roles are not impacted by this behavior. You can modify that by changing or adding to the lines at the top of that script. UI Scripts are found under the ‘System UI’ application.

The post Restrict Report Table Selection by Role appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/reporting/restrict-report-table-selection-role/feed/ 28
Eliminating Duplicates from an Array with JavaScript https://servicenowguru.com/scripting/eliminating-duplicates-array-javascript/ https://servicenowguru.com/scripting/eliminating-duplicates-array-javascript/#comments Wed, 06 Jan 2010 20:21:17 +0000 https://servicenowguru.wpengine.com/?p=532 This is a great function that you can use to eliminate duplicates from an array in Service-now. It's just standard JavaScript so it will work in Client Scripts, Business Rules, or pretty much anywhere else you want to put it. The function takes an array as the input parameter and returns an array...minus the duplicate

The post Eliminating Duplicates from an Array with JavaScript appeared first on ServiceNow Guru.

]]>
This is a great function that you can use to eliminate duplicates from an array in Service-now. It’s just standard JavaScript so it will work in Client Scripts, Business Rules, or pretty much anywhere else you want to put it. The function takes an array as the input parameter and returns an array…minus the duplicate values!

function checkDuplicates(a) {
   //Check all values in the incoming array and eliminate any duplicates
   var r = new Array(); //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;
}

Use ArrayUtil for server-side scripts

ServiceNow now includes an ‘ArrayUtil’ script include that has a bunch of useful functions that you can use with server-side scripts. Among these is the ‘unique’ function. ‘unique’ can be used to eliminate duplicates from an array and is much cleaner for server-side script.

var uniqueArray = new ArrayUtil().unique(your_array);

The post Eliminating Duplicates from an Array with JavaScript appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/scripting/eliminating-duplicates-array-javascript/feed/ 2
Walking the ServiceNow CMDB Relationship Tree https://servicenowguru.com/cmdb/walking-servicenowcom-cmdb-relationship-tree/ https://servicenowguru.com/cmdb/walking-servicenowcom-cmdb-relationship-tree/#comments Wed, 06 Jan 2010 20:08:52 +0000 https://servicenowguru.wpengine.com/?p=521 One of the great features of Service-now.com is its CMDB and relational mapping. You can easily set up relationships between any CIs in the system. Once the relationships are defined, it becomes very simple to pull up a visual representation of a CI and its dependencies by using Service-now BSM maps. Using this feature allows an end user to look at that CI and identify what else in the environment is impacted by an outage or a change to that CI.

The post Walking the ServiceNow CMDB Relationship Tree appeared first on ServiceNow Guru.

]]>
Special thanks to Lyle Taylor for identifying…and fixing…a performance issue with this script that may occur with VERY large data sets! Most environments won’t be dealing with a CMDB large enough to have this be an issue, but it’s probably a good idea to update your CIUtils2 script include with the code below anyway.

 

One of the great features of ServiceNow is its CMDB and relational mapping. You can easily set up relationships between any CIs in the system. Once the relationships are defined, it becomes very simple to pull up a visual representation of a CI and its dependencies by using ServiceNow BSM maps.  Using this feature allows an end user to look at that CI and identify what else in the environment is impacted by an outage or a change to that CI.

While this works fine for most situations, it may be necessary to be able to use this relationship information outside of the map view.  What if you wanted to determine all of the Business Services impacted by a particular change request? What if you wanted to pull an approval group from each impacted Business Service to approve a specific step in your change workflow?

The ‘CIUtils’ Script Include is designed to help you get some of this information. You can find it by navigating to ‘System Definition -> Script Includes’.  To use it, you could do something like this in a Business rule, UI action, or Scheduled job…

var ciu = new CIUtils();
//Return all of the Business services affected by the current CI on this ticket
var services = ciu.servicesAffectedByCI(current.cmdb_ci);
//Do something with the array of Business Services returned

You can also use the ‘servicesAffectedByTask’ function to use a task record as your input parameter

var ciu = new CIUtils();
//Return all of the Business services affected by the current CI on this ticket
var services = ciu.servicesAffectedByTask(current);
//Do something with the array of Business Services returned

There are a couple of limitations with the CIUtils Script include though. To fill in some of those gaps, I recently created an enhancement to CIUtils called ‘CIUtils2’. It is used in pretty much the same way as the CIUtils Script include, but it gives you these added benefits…

  • Return all impacted Business Services based on the ‘cmdb_ci’ field AND the ‘Affected CIs’ related list
  • Return all impacted CIs based on the CI classes you specify
  • Return all impacted CIs throughout the entire CMDB

To use it, simply create a new Script include record with the script below WITH A NAME OF ‘CIUtils2’. You can then call the functions like this…

Usage:
var ciu = new CIUtils2();
//cisAffectedByTask takes a Task glideRecord as input and returns an array of CI sys_ids
var services = ciu.cisAffectedByTask(TaskGlideRecord); //Returns an array of impacted Business Service CIs
var services = ciu.cisAffectedByTask(TaskGlideRecord, [“cmdb_ci_service”, “cmdb_ci_web_server”]); //Returns an array of impacted CIs from the CI classes specified
var services = ciu.cisAffectedByTask(TaskGlideRecord, [“ALL”]); //Returns an array of ALL impacted CIs//cisAffectedByCI takes a CI glideRecord as input and returns an array of CI sys_ids
var services = ciu.cisAffectedByCI(current.cmdb_ci); //Returns an array of impacted Business Service CIs
var services = ciu.cisAffectedByCI(current.cmdb_ci, [“cmdb_ci_service”, “cmdb_ci_web_server”]); //Returns an array of impacted CIs from the CI classes specified
var services = ciu.cisAffectedByCI(current.cmdb_ci, [“ALL”]); //Returns an array of ALL impacted CIs. Use this with caution!!!//getCIXML takes a CI sys_id as input and returns an XML-formatted string showing all CIs impacted by an outage to the CI given
var myXML = ciu.getCIXML(current.cmdb_ci);Script:

gs.include('PrototypeServer');var CIUtils2 = Class.create();

CIUtils2.prototype = {
initialize: function () {
this.maxDepth = gs.getProperty('glide.relationship.max_depth', 10); // how deep to look
this.maxAffectedCIs = gs.getProperty('glide.relationship.threshold', 1000); // max records to return
this.defaultClasses = ['cmdb_ci_service', 'service_offering'];
this.arutil = new ArrayUtil();
},

/**
* Determine which CIs are affected by a specific CI
*
* Inputs:
* id is the sys_id of a configuration item (cmdb_ci)
* classArr is an array of CI class names that should be returned
* infoObj is an object created by _getInfoObj used to track data accross multiple
* calls to the function made by cisAffectedByTask. It is not needed when calling
* the function directly.
*
* Returns:
* an array of sys_id values for cmdb_ci records upstream of
* (or affected by) the input item
*/

cisAffectedByCI: function (id, classArr, infoObj /*optional*/) {
if (!infoObj) {
infoObj = this._getInfoObj();
}
if (infoObj.visitedCIs[id]) {
// We've already processed this CI
return [];
}
infoObj.visitedCIs[id] = true;
if (!classArr || classArr.length == 0) {
classArr = this.defaultClasses;
}

// This is to keep track of affected CIs from this CI only.
// CIs that are already listed in infoObj.affectedCIs from prior
// calls to the function will not be included.
var affectedCIs = [];
var ci = new GlideRecord('cmdb_ci');
if (ci.get(id)) {
//If class = 'ALL' then just add the CI
if (classArr[0] == 'ALL' || this.arutil.contains(classArr, ci.sys_class_name.toString())) {
affectedCIs.push(id);
this._addCI(id, infoObj);
}
this._addParentCIs(id, infoObj, affectedCIs, 1, classArr);
}
return this._unique(affectedCIs); // list of affected CIs
},

/**
* Determine which CIs are affected by a task
*
* Inputs:
* task is a task GlideRecord (e.g., incident, change_request, problem)
* classArr is an array of CI class names that should be returned
*
* Returns:
* an array of sys_id values for cmdb_ci records upstream from
* (or affected by) the configuration item referenced by the task's cmdb_ci field and Affected CIs list
*/

cisAffectedByTask: function (task, classArr) {
var infoObj = this._getInfoObj();
//Find the impacted CIs for the 'cmdb_ci' value
var id = task.cmdb_ci.toString();
if (id) {
this.cisAffectedByCI(id, classArr, infoObj);
}

//Find the impacted CIs for any Affected CIs listed on the task
var affCI = new GlideRecord('task_ci');
affCI.addQuery('task', task.sys_id);
affCI.query();
while (affCI.next()) {
this.cisAffectedByCI(affCI.ci_item.sys_id.toString(), classArr, infoObj);
}
return this._objToArray(infoObj.affectedCIs);
},

/**
* Returns an XML-formatted string showing all CIs impacted by an outage to the CI given
*
* Inputs:
* id is the sys_id of the root CI
*
* Returns:
* an XML-formatted string containing cmdb_ci records downstream of
* (or affected by) the configuration item provided as input
*/

getCIXML: function (id) {
var gr = new GlideRecord('cmdb_rel_ci');
gr.addQuery('child', id);
gr.query();
gr.next();
var str = '';
str += '';
str += '' + gr.child.sys_id + '';
str += '' + gr.child.name + '';
str += 'SELF';
ret = this._recurs(id);
if (ret) {
str += '';
str += ret;
str += '';
}
str += '';
return str;
},

_recurs: function (ci) {
var gr = new GlideRecord('cmdb_rel_ci');
gr.addQuery('child', ci);
gr.query();
var str = '';
while (gr.next()) {
str += '';
str += '' + gr.parent.sys_id + '';
str += '' + gr.parent.name + '';
str += '' + gr.type.name + '';
ret = this._recurs(gr.parent.sys_id);
if (ret) {
str += '';
str += ret;
str += '';
}
str += '';
}
return str;
},

_addParentCIs: function (id, infoObj, affectedCIs, currentDepth, classArr) {
if (infoObj.affectedCIsCount >= this.maxAffectedCIs)
return;

var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('child', id);
rel.query();

var parents = [];
while (rel.next()) {
parents.push(rel.parent.toString());
}
if (parents.length) {
var parent = new GlideRecord('cmdb_ci');
parent.addQuery('sys_id', parents);
parent.query();

while (parent.next() && infoObj.affectedCIsCount < this.maxAffectedCIs) {
var pid = parent.sys_id.toString();
if (!infoObj.visitedCIs[pid]) {
infoObj.visitedCIs[pid] = true;
if (classArr[0] == 'ALL' || this.arutil.contains(classArr, parent.sys_class_name.toString())) {
affectedCIs.push(pid);
this._addCI(pid, infoObj);
}
if (currentDepth < this.maxDepth)
this._addParentCIs(pid, infoObj, affectedCIs, currentDepth + 1, classArr);
}
}
}
},

_addCI: function (id, infoObj) {
infoObj.affectedCIs[id] = true;
infoObj.affectedCIsCount++;
},

_getInfoObj: function () {
return {
affectedCIsCount: 0, // track how many added, since can't get size() for an Object
affectedCIs: {}, // full list of affected CIs for specified classes
visitedCIs: {} // track CIs already iterated over
};
},

_objToArray: function (obj) {
var ar = [];
for (var id in obj) {
ar.push(id);
}
return ar;
},

_unique: function (a) {
var obj = {};
for (var idx in a) {
obj[a[idx]] = true;
}
return this._objToArray(obj);
},

type: 'CIUtils2'
};

 

Another practical example for Change Management

Here’s an example UI action that I use for almost all of my clients. In change management, you’re often interested in the business services that will be impacted by a given change. While you can see this in a BSM map, it’s often useful to see this in a related list directly on your change form as well. By adding the ‘Impacted Services’ related list to your change form, you can populate this data. This UI action gathers all of the information about the CIs on your change request and adds the impacted services to the list.

‘Refresh Impacted Services’ UI Action
Name: Refresh Impacted Services
Table: Change request
Action name: refresh_impacted_services
Form context menu: True
Condition: gs.hasRole(‘itil’)
Script:

current.update();
action.setRedirectURL(current);
removeAffectedServices();
addAffectedServices();function removeAffectedServices() {
var m2m = new GlideRecord('task_cmdb_ci_service');
m2m.addQuery('task',current.sys_id);
m2m.addQuery('manually_added','false');
m2m.query();
m2m.deleteMultiple();
}function addAffectedServices() {
var ciu = new CIUtils2();
//Find all impacted business services
var services = ciu.cisAffectedByTask(current);
//var services = ciu.cisAffectedByTask(current, ["cmdb_ci_service", "cmdb_ci_windows_server"]);
//var services = ciu.cisAffectedByTask(current, ["ALL"]);
var m2m = new GlideRecord('task_cmdb_ci_service');
for (var i = 0; i < services.length; i++) {
m2m.initialize();
m2m.task = current.sys_id;
m2m.cmdb_ci_service = services[i];
m2m.manually_added = 'false';
m2m.insert();
}
}

The post Walking the ServiceNow CMDB Relationship Tree appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/cmdb/walking-servicenowcom-cmdb-relationship-tree/feed/ 46
Canceling executing workflows on task closure https://servicenowguru.com/business-rules-scripting/canceling-executing-workflows-task-closure/ https://servicenowguru.com/business-rules-scripting/canceling-executing-workflows-task-closure/#comments Tue, 29 Dec 2009 12:55:14 +0000 https://servicenowguru.wpengine.com/?p=407 It is very common to use graphical workflow to help facilitate some change management process within your organization. One common requirement in change management is to be able to cancel or close the change request at any time during the process. "Simple", you say. "Just allow the user to change the value of the 'State' field to 'Closed'."
You would not be incorrect in saying something like that, but you would be forgetting about part of the problem with closing or canceling a change request or other task ticket.

The post Canceling executing workflows on task closure appeared first on ServiceNow Guru.

]]>
Service-now.com provides a very robust and simple way to manage your tasks and approvals (among other things) through its graphical workflow engine.  It is very common to use graphical workflow to help facilitate some change management process within your organization.  One common requirement in change management is to be able to cancel or close the change request at any time during the process.  “Simple”, you say.  “Just allow the user to change the value of the ‘State’ field to ‘Closed’.”

You would not be incorrect in saying something like that, but you would be forgetting about part of the problem with closing or canceling a change request or other task ticket.  What if the attached workflow(s) still think that the change request and its associated tasks and approvals are still in progress?  Should the attached workflow context(s) continue to run indefinitely?  If your workflow doesn’t have a way to know about the completion of the change request then it will continue to run (or more likely just sit and be forgotten).


The answer to this problem is actually pretty simple.  Service-now.com comes with several out-of-box workflow utility functions defined under ‘System Definition -> Script Includes’ that can be helpful in situations like these.  While you don’t want to modify these script includes, it is probably a good idea as a Service-now administrator to become familiar with the tools and functions there.  One of the functions in the ‘Workflow’ script include is called ‘cancel’.  It can be used to cancel any running workflow activities for a given record. This script could be called from a UI action button, another workflow, or a business rule. You just need to be able to tell the function what GlideRecord should have its workflows canceled. The example below shows how you could create a business rule to cancel all running workflows for a given record if the ‘active’ field changed to ‘false’. The cancellation in the example below happens for the ‘current’ GlideRecord object (which is the current record being updated).

Cancel All Workflow Contexts for a given Record

Cancel Workflow Business Rule
Name: Cancel workflows
When: After
Insert: True
Update: True
Condition: current.active.changesTo(false)
Script:

//Query for all executing workflows and cancel any running activities
new Workflow().cancel(current);

Cancel a Single Workflow Context (by name) for a given Record

You may also encounter situations where you don’t want to cancel all associated workflow contexts, just a single one, or all but one. Again, you can find the solution in the ‘Workflow’ script include by way of the ‘getRunningFlows’ and ‘cancelContext’ functions. The following script could be run from a business rule, UI action, or even within a ‘Run Script’ workflow activity. The example given here cancels any ‘Routine Change’ workflow contexts associated with the ‘current’ record.

//Query for all executing workflow contexts
var flows = new Workflow().getRunningFlows(current);
while(flows.next()){
//Check for associated workflows by name
if(flows.workflow_version.getDisplayValue() == 'Routine Change'){
//Cancel the workflow context
new Workflow().cancelContext(flows);
}
}

The post Canceling executing workflows on task closure appeared first on ServiceNow Guru.

]]>
https://servicenowguru.com/business-rules-scripting/canceling-executing-workflows-task-closure/feed/ 23