Use Case: When a assigned Sev 1 / Sev 2 Case for the assignment groups say Test-INTEL,Test-CORE-99, SLA resolution and response is crossed by 50% and 75% and breached, we have to get notified on slack channel #test-core-client-internal with below message
Example - Ticket number - Customer - Short Description - Response SLA clock is ticking(“%”SLA already Crossed) on this “Severity” ticket in “Engineer’s Name” name. Please address quickly.
Solution
Steps:
- We have to get the webhook of the Slack channel where we want to post.
Webhook looks like below: https://hooks.slack.com/services/AC3R7L2XY/U01ECPSG484/39XLtLLqtXtOEQMNpjEZJlXI
-
We have an OOTB workflow “Default SLA workflow” that emits two events:
- sla.warning - sla.warning.breach
-
From the above workflow we can observe that when sla reaches 50% and 75% “sla.warning” events are getting triggered. Also when SLA breaches then “sla.warning.breach” is getting triggered.
-
We are going to listen to these events and write two script actions to send notification is slack channel.
Script Action
Slack Notif-SLA-50/75-Core/Client:
Send notification to slack when sla reaches 50% and 75%
For 50% and 75% ,sla are using same event(sla.warning) so that we are create one script action for 50% and 75%
//gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);
var isPlainTextRequired = false;
var emoji = "stopwatch";
var record = getCaseDetails(current.task);
var thresholdTolerance = 10;
var targetType = current.sla.getDisplayValue().split("-")[0];
var slackLinkByGroup = {};
//Slack-Group Mapping
slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'] = "https://hooks.slack.com/services/Tdc3fghjGM/A0fhbnjVDUG/yCFNpZCdfdd8MJIuerdf";
slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'] = "https://hooks.slack.com/services/erdfgresrM2GM/B01FCfgh4666/44cvbgfdrertztOEQEYtyghff";
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TC3R7M2GM/yuhjgfde0B7JB/LZopuiylkZ5nJsshM";
var coreServiceGroups = ["TEST-CORE-DATABASE", "TEST-CORE-Backup"];
var coreClientGroups = ["TEST-CORE-UNIX", "TEST-CORE-INTEL"];
var priorityList = ["1", "2"];
var allowedInstances = ["https://dev456.service-now.com/"];
var instanceURL = gs.getProperty('glide.servlet.uri');
if (record.next()) {
var threshold = calculatedThreshold(parseInt(current.percentage));
if (isPriorityAllowed(record.priority.toString()) &&
!isProcessed(threshold, current.sys_id) &&
isAllowedInstance() &&
current.stage == "in_progress") {
var colorCode = evaluateColorCode(threshold);
var thresholdText = threshold != 0 ? threshold + "% SLA already Crossed" : "Reopened";
var assignedTo = record.assigned_to.getDisplayValue() == "" ? "Not Assigned" : record.assigned_to.getDisplayValue();
var headingText = targetType + "SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record.priority + " ticket. " + "Assignee - " + assignedTo + ". Please address quickly.";
var ticketNumber = record.number;
var customer = record.account.getDisplayValue();
var shortDescription = record.short_description;
var incidentLink = instanceURL + "sn_customerservice_case.do?sys_id=" + record.sys_id;
var slacklink = evaluateSlackLink(record.assignment_group.getDisplayValue());
var fallBackText = ticketNumber + "-" + customer + "-" + shortDescription + "-" + headingText;
var plainTextMessage = "{\"text\": \"" + fallBackText + "\"}";
var assignmentGroup = record.assignment_group.getDisplayValue();
var template = "{ \"attachments\":[";
template = template + "{\"fallback\": \"%fallBackText%\",";
template = template + "\"color\": \"%colorCode%\",";
template = template + "\"fields\": [";
template = template + "{\"title\": \":%emoji%: %headingText%\", \"short\": false },";
template = template + "{\"value\": \"Ticket number : `%ticketNumber%`\", \"short\": false },";
template = template + "{\"value\": \"Customer : `%customer%`\", \"short\": false },";
template = template + "{\"value\": \"Assignment Group : `%assignmentGroup%`\", \"short\": false },";
template = template + "{\"value\": \"Short Description: `%shortDescription%`\", \"short\": false },";
template = template + "{\"value\": \"_Click here to view it_ : <%incidentLink%|*incident*>\",\"short\": false }";
template = template + "]}";
template = template + "]}";
template = template.replace("%fallBackText%", fallBackText);
template = template.replace("%colorCode%", colorCode);
template = template.replace("%headingText%", headingText);
template = template.replace("%ticketNumber%", ticketNumber);
template = template.replace("%customer%", customer);
template = template.replace("%assignmentGroup%", assignmentGroup);
template = template.replace("%shortDescription%", shortDescription);
template = template.replace("%incidentLink%", incidentLink);
template = template.replace("%emoji%", emoji);
var finalMessage = isPlainTextRequired ? plainTextMessage : template;
try {
//Used to prevent duplicate message getting sent for 100%, please dont remove
gs.log("Threshold for SLA -->" + threshold + '-' + current.sys_id, threshold + "-" + current.sys_id);
var request = new sn_ws.RESTMessageV2();
request.setHttpMethod('post');
request.setRequestBody(finalMessage);
request.setEndpoint(slacklink);
var response = request.execute();
} catch (ex) {
//Nothing to do
}
}
}
function getCaseDetails(caseSysId) {
var record = new GlideRecord('sn_customerservice_case');
record.addQuery('sys_id', caseSysId);
record.query();
return record;
}
function isProcessed(threshold, taskId) {
var taskList = new GlideRecord('syslog');
taskList.addQuery('source', threshold + '-' + taskId);
taskList.addEncodedQuery("sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()");
taskList.query();
if (taskList.next()) {
return true;
} else {
return false;
}
}
function calculatedThreshold(percentage) {
if (percentage >= 50 && percentage < (50 + thresholdTolerance))
return 50;
else if (percentage >= 75 && percentage < (75 + thresholdTolerance))
return 75;
else if (percentage >= 100 && percentage < (100 + thresholdTolerance))
return 100;
//Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
else
return 0;
}
function isEventForManager(eventParam2) {
return eventParam2 == "manager";
}
function evaluateColorCode(threshold) {
/**
#F1C40F --> yellow
#E67E22 --> orange
#A93226 --> red
**/
var code = "";
switch (threshold) {
case 50:
code = "#F1C40F";
break;
case 75:
code = "#E67E22";
break;
default:
code = "#A93226";
}
return code;
}
function evaluateSlackLink(groupName) {
if (coreServiceGroups.indexOf(groupName) !== -1) {
return slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'];
}
if (coreClientGroups.indexOf(groupName) !== -1) {
return slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'];
}
}
function isPriorityAllowed(priority) {
return priorityList.indexOf(priority) !== -1;
}
function isAllowedInstance() {
return allowedInstances.indexOf(instanceURL) !== -1;
}
Script Action
Slack Notif-SLA Breach-Core/Client
Send notification to slack when sla reaches 100%
For 100%, Sla are using different event so we are creating different script action.
//gs.log('eventname------>' + event.name + 'instance---->' + current.sys_id);
//Fallback to plain text if rich text format doesnt support
var isPlainTextRequired = false;
//Emoji icon to use.This text needs to be compatible with Slack emojis
var emoji = "stopwatch";
//Get case details from task sla
var record = getCaseDetails(current.task);
// This tolerance will define the range to take floor value from the range of values
// For example - percentage is 53.6 then assume 50.
var thresholdTolerance = 10;
//Extracts SLA type response/resolution from sla name
var targetType = current.sla.getDisplayValue().split("-")[0];
//Captures event paramter 2
var eventParam2 = event.parm2;
//List of all slack webhooks.Add new entry below and map it to a name
var slackLinkByGroup = {};
//Slack-Group Mapping
slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'] = "https://hooks.slack.com/services/DE4567YHJK2GM/DSF54RTHG/yFDSERTWS56TG";
slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'] = "https://hooks.slack.com/services/GHTR567EDHGF/39YBtCYqYUIOJK895GHFD";
//slackLinkByGroup['SNOW-IN-TEAM'] = "https://hooks.slack.com/services/TCfdfghjk01ENVIKIKJ/JH4567YHJGHRTS";
//List of Groups.The releationship of these groups to slack link is done in evaluateSlackLink function
var coreServiceGroups = ["TEST-CORE-DATABASE", "TEST-CORE-Backup"];
var coreClientGroups = ["TEST-CORE-UNIX", "TEST-CORE-INTEL"];
var priorityList = ["1", "2"];
var allowedInstances = ["https://dev567.service-now.com/"];
var instanceURL = gs.getProperty('glide.servlet.uri');
if (record.next()) {
//Calculate the threshold based on the current percentage
var threshold = calculatedThreshold(parseInt(current.percentage));
var thresholdText = threshold != 0 ? threshold + "% SLA already Crossed" : "Reopened";
//Send message only if the event is not fired for a manager and is not already send and with allowed priority.
if (!isEventForManager(eventParam2) &&
!isProcessed(threshold, current.sys_id) &&
isPriorityAllowed(record.priority.toString()) &&
isAllowedInstance() &&
current.stage == "in_progress") {
//Determines the colorcode based on threshold
var colorCode = evaluateColorCode(threshold);
//All below variables are constructed before applied to the template
var assignedTo = record.assigned_to.getDisplayValue() == "" ? "Not Assigned" : record.assigned_to.getDisplayValue();
var headingText = targetType + "SLA clock is ticking ( " + thresholdText + " ) on this Severity " + record.priority + " ticket. " + "Assignee - " + assignedTo + ". Please address quickly.";
var ticketNumber = record.number;
var customer = record.account.getDisplayValue();
var shortDescription = record.short_description;
var incidentLink = instanceURL + "sn_customerservice_case.do?sys_id=" + record.sys_id;
var slacklink = evaluateSlackLink(record.assignment_group.getDisplayValue());
var fallBackText = ticketNumber + "-" + customer + "-" + shortDescription + "-" + headingText;
var plainTextMessage = "{\"text\": \"" + fallBackText + "\"}";
var assignmentGroup = record.assignment_group.getDisplayValue();
//The template for slack message
var template = "{ \"attachments\":[";
template = template + "{\"fallback\": \"%fallBackText%\",";
template = template + "\"color\": \"%colorCode%\",";
template = template + "\"fields\": [";
template = template + "{\"title\": \":%emoji%: %headingText%\", \"short\": false },";
template = template + "{\"value\": \"Ticket number : `%ticketNumber%`\", \"short\": false },";
template = template + "{\"value\": \"Customer : `%customer%`\", \"short\": false },";
template = template + "{\"value\": \"Assignment Group : `%assignmentGroup%`\", \"short\": false },";
template = template + "{\"value\": \"Short Description: `%shortDescription%`\", \"short\": false },";
template = template + "{\"value\": \"_Click here to view it_ : <%incidentLink%|*incident*>\",\"short\": false }";
template = template + "]}";
template = template + "]}";
//Fill all the values in the template
template = template.replace("%fallBackText%", fallBackText);
template = template.replace("%colorCode%", colorCode);
template = template.replace("%headingText%", headingText);
template = template.replace("%ticketNumber%", ticketNumber);
template = template.replace("%customer%", customer);
template = template.replace("%assignmentGroup%", assignmentGroup);
template = template.replace("%shortDescription%", shortDescription);
template = template.replace("%incidentLink%", incidentLink);
template = template.replace("%emoji%", emoji);
var finalMessage = isPlainTextRequired ? plainTextMessage : template;
try {
//Used to prevent duplicate message getting sent for 100%, please dont remove
gs.log("Threshold for SLA -->" + threshold + '-' + current.sys_id, threshold + "-" + current.sys_id);
//Send message to slack
var request = new sn_ws.RESTMessageV2();
request.setHttpMethod('post');
request.setRequestBody(finalMessage);
request.setEndpoint(slacklink);
var response = request.execute();
} catch (ex) {
//Nothing to do
}
}
}
function getCaseDetails(caseSysId) {
var record = new GlideRecord('sn_customerservice_case');
record.addQuery('sys_id', caseSysId);
record.query();
return record;
}
function isProcessed(threshold, taskId) {
var taskList = new GlideRecord('syslog');
taskList.addQuery('source', threshold + '-' + taskId);
taskList.addEncodedQuery("sys_created_onONLast 15 minutes@javascript:gs.beginningOfLast15Minutes()@javascript:gs.endOfLast15Minutes()");
taskList.query();
if (taskList.next()) {
return true;
} else {
return false;
}
}
function calculatedThreshold(percentage) {
if (percentage >= 50 && percentage < (50 + thresholdTolerance))
return 50;
else if (percentage >= 75 && percentage < (75 + thresholdTolerance))
return 75;
else if (percentage >= 100 && percentage < (100 + thresholdTolerance))
return 100;
//Reopened SLAs shows abnormal event triggering behaviour.In such case default result to 0
else
return 0;
}
function isEventForManager(eventParam2) {
return eventParam2 == "manager";
}
function evaluateColorCode(threshold) {
/**
#F1C40F --> yellow
#E67E22 --> orange
#A93226 --> red
**/
var code = "";
switch (threshold) {
case 50:
code = "#F1C40F";
break;
case 75:
code = "#E67E22";
break;
default:
code = "#A93226";
}
return code;
}
function evaluateSlackLink(groupName) {
if (coreServiceGroups.indexOf(groupName) !== -1) {
return slackLinkByGroup['TEST-CORE-SERVICES-INTERNAL'];
}
if (coreClientGroups.indexOf(groupName) !== -1) {
return slackLinkByGroup['TEST-CORE-CLIENT-INTERNAL'];
}
}
function isPriorityAllowed(priority) {
return priorityList.indexOf(priority) !== -1;
}
function isAllowedInstance() {
return allowedInstances.indexOf(instanceURL) !== -1;
}
- Understanding Request, RITM, Task in ServiceNow
- Steps to create a case in ServiceNow (CSM)
- Performance Analytics in 10 mins
- Event Management in 10 minutes - part1
- Event Management in 10 minutes - part2
- Custom Lookup List
- Script includes in 5 minutes
- Interactive Filter in 5 minutes
- UI Policy in 6 Minutes
- Client Side Script Versus Server Side Script in 3 minutes
-
Snow
- Performance Analytics
- ServiceNow Scripts
- Script include
- Useful scripts
- Basic Glide Scripts
- Client Script
- Advance Glide Script
- Glide System Script
- Admin
- Import Set
- Work Flow
- ACL
- SLA
- Notification
- Core Application
- UI Policy
- UI Action
- Client Script
- CAB Workbech
- Data Policy
- Connect Support
- Catalog
- Discovery
- CSM
- Event Management
- HR
- Integrations
- SSO Integration
- LDAP Integration
- SCCM Integration
- AWS Intergration
- Slack Integration
- CTI Integration
- Jira Integration
- Ebonding ServiceNow
- SOAP Integration
- IBM Netcool Integration
- VIP Mobile App Integration
- Rest Integration
- Service Portal
- Questions
- ACL
- Performance analytics(PA) Interactive Filter
- Various Configurations in Performance analytics(PA)
- Service Portal
- Performance Analytics(PA) Widgets
- Performance Analytics(PA) Indicator
- Performance Analytics(PA) Buckets
- Performance Analytics(PA) Automated Breakdown
- Client Script
- Rest Integration
- Understanding the Request, RITM, Task
- Service Catalogs
- Events in ServiceNow
- Advance glide script in ServiceNow
- CAB Workbench
Comments