Awhile ago I was asked by a client to help them get set up with the ServiceNow Subscription-based Notification plugin. I had previewed the functionality but really hadn’t worked with it very much. As I dug deeper I realized that the standard setup wasn’t going to work for them. Subscription-based notifications are really not intended to be used on a bulk-subscription basis. The idea is that you might pick a handful of CIs that you are interested in and subscribe to them.
The needs that my client had brought to light the following questions:
1How can I easily subscribe to multiple CIs without having to click through the creation of a single subscription record for each CI? If I want to subscribe to the 50 network devices I’m responsible for I don’t want to have to set each of these up one-by-one.
2How can I use CI subscription notifications and not spam the end-user with a notification for each CI? An example of this would be a system administrator who is responsible for a cluster of web servers. A configuration change on one of them typically means a configuration change on all of them. We don’t want to be sending the admin an email for each of the 100 servers every time an update is made to the change ticket.
Shown below is the solution that we came up with…
Question #1:How can I easily subscribe to multiple CIs without having to click through the creation of a single subscription record for each CI?
I accomplished this by creating a UI action link on the user record that redirects to record producer catalog item. The record producer collects the CI and user information, creates the subscriptions, and redirects the user to his/her subscription page.
1) Create a Record producer with 3 variables.
-configuration_items (list collector that references the ‘cmdb_ci’ table)
-user (reference variable that references the user table –This will be hidden by a catalog client script)
-email (single line text variable –This will be hidden by a catalog client script)
-The record producer should point to the ‘Notification Messages’ table
-The record producer should have the following in the ‘Script’ field.
var pEmail = producer.email.toString();
var pCIs = producer.configuration_items.toString();
//Find the user 'Primary email' notification device
var rec = new GlideRecord('cmn_notif_device');
rec.addQuery('user', pUser);
rec.addQuery('type', 'Email');
rec.addQuery('email_address', pEmail);
rec.query();
rec.next();
//Parse the list of configuration items
var list = pCIs;
var array = list.split(',');
for (var i=0; i < array.length; i++) {
//Query to see if this user has already subscribed to this CI
var nm = new GlideRecord('cmn_notif_message');
nm.addQuery('user', pUser);
nm.addQuery('configuration_item', array[i]);
nm.query();
if(!nm.next()){
//Create the CI notification records
var rec1 = new GlideRecord('cmn_notif_message');
rec1.initialize();
rec1.notification.setDisplayValue('CI affected');
rec1.user = pUser;
rec1.device = rec.sys_id;
rec1.configuration_item = array[i];
rec1.insert();
}
}
//Do not submit this record
current.setAbortAction(true);
//Add a notification message and redirect the user to the subscriptions page
gs.include('FormInfoHeader');
var fi = new FormInfoHeader();
producer.redirect = 'notification_preferences.do?sysparm_user=' + producer.user.toString() + '&sysparm_email=' + producer.email.toString();
var s = 'Subscriptions have been created.<br/>';
s += 'You can modify the subscription settings for individual configuration items below. <br/>';
fi.addMessage(s);
-Create an ‘onLoad’ catalog client script for your record producer with the following script. The purpose of this script is to populate the ‘user’ and ’email’ fields with data passed in from the UI action link on the user form.
//Hide the user and email fields
g_form.setDisplay('user', false);
g_form.setDisplay('email', false);
//Get parameters from url and populate them into the user and email fields
var url = document.location.toString();
var userKey = 'sysparm_user=';
var emailKey = 'sysparm_email=';
var userPosition = url.indexOf(userKey);
var emailPosition = url.indexOf(emailKey);
if (userPosition != -1){
var user = url.substr(userPosition+userKey.length, 32);
g_form.setValue('user',user);
}
if (emailPosition != -1){
var email = url.substr(emailPosition + emailKey.length);
g_form.setValue('email',email);
}
}
2) Create a ‘Form link’ UI action link on the ‘sys_user’ table that redirects to your new Record producer
-Condition: gs.getUserID() == current.sys_id || user.hasRole(‘admin’)
-Script: Note–You’ll need to put the sys_id of your record producer in here
Question #2:How can I use CI subscription notifications and not spam the end-user?
This can be accomplished by creating a business rule on the table of your choice (mine was on the ‘change_request’ table) with the following parameters. The business rule triggers an event that sends an email notification.
–The first thing you’ll want to do is de-activate the ‘Affected ci notifications’ business rule. This solution works separately from the trigger in that business rule.
1) Create the business rule
Name: Notify subscribers
Table: change_request (or whatever you choose)
-Runs after insert/update
Condition:
-Note: I chose this condition so that the notification would only go out when approval on the change was requested. You should modify this according to your needs.
Script:
gs.eventQueue('task.notify.subscribers', current, gs.getUserID(), subscribers);
function getSubscribers(){
// get affected CIs
var affectedCIs = new GlideRecord('task_ci');
affectedCIs.addQuery('task',current.sys_id);
affectedCIs.query();
var allPersons = [];
while(affectedCIs.next()){
var persons = [];
allPersons = allPersons.concat(getRelatedPersons(affectedCIs.ci_item.sys_id, persons));
}
allPersons = eliminateDuplicates(allPersons);
allPersons = allPersons.toString();
return allPersons;
}
function getRelatedPersons(ci_sys_id, persons){
var relPersons = new GlideRecord('cmn_notif_message');
relPersons.addQuery('configuration_item', ci_sys_id);
relPersons.query();
while (relPersons.next()) {
persons.push(relPersons.user.sys_id.toString());
}
var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('child', ci_sys_id);
rel.query();
while (rel.next()) {
var parentSysID = rel.parent;
// get Interested Parties for parent CIs
getRelatedPersons(parentSysID, persons);
}
return persons;
}
function eliminateDuplicates(arr) {
// remove duplicates from array
var i, len=arr.length, out=[], obj={};
for (i=0;i < len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
function notifyParents(table, itemSysID) {
var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('child', itemSysID);
rel.query();
while (rel.next()) {
var parentSysID = rel.parent;
var parentName = rel.parent.getDisplayValue();
if (!noNotify[parentSysID]) {
count ++;
gs.log('### Notifying subscribers of ' + parentName);
noNotify[parentSysID] = true;
gs.eventQueue('ci.affected', current, parentSysID, parentName);
notifyParents('cmdb_ci', parentSysID);
}
}
}
2) Register a new event in the event registry called ‘task.notify.subscribers’
3) Create a new email notification that is triggered by the ‘task.notify.subscribers’ event. I used the following parameters for my notification:
-Name: Notify CI subscribers
-Event name: task.notify.subscribers (or whatever you call your event)
-Table: change_request
-User: event.parm2
-Subject:
-Message:
Short description: ${short_description}
Affected CIs: (Please note that the CIs listed below are only those that are directly impacted by the change request. The CI you subscribed to may be impacted as a result of impact to one or more of the CIs listed below.
<mail_script>
var affectedCIs = new GlideRecord('task_ci');
affectedCIs.addQuery('task',current.sys_id);
affectedCIs.query();
while(affectedCIs.next()){
template.print(affectedCIs.ci_item.getDisplayValue() + '\n');
}
</mail_script>
As always, your posts here is very timely with my needs,
Great work Mark, much appreciated
with the 2nd question, what I did was just change the condition of the existing Affected ci notifications business rule to suit my needs.
I have implemented the first one and it looks great,
one small thing to add though, since we have tons of CIs
can you have a default filter in the CI list? so users won’t have to do the filtering on their own?
You can! Here’s an article that should help you out. It’s the same script that I used to set the filter in the image from this article.
https://servicenowguru.wpengine.com/scripting/client-sc…
Mark-For the 2nd question when you have created the User field.How should we pass the event.param2 to User Field? User field is a reference field and how we can pass the parameters then?
You collect those users in the business rule trigger and pass them in as an event parameter (as shown in step 2 above). Then you can access the value of that event parameter by using ‘event.parm2’ directly into the user field on the email notification record.
But the User field is a refernece field which expects a user id ? How can we pass the parameter to this field? Moreover the user field is not available when the notification is a subscribable one.