scotthall
ServiceNow Employee
ServiceNow Employee

My colleague, @Ryan Balcom, and I have been plugging away on a side project to get Google Cloud Ops monitoring notifications into Event Management in ServiceNow and wanted to share the results here in case others are doing the same.

We used a Listener Transform Script to receive Google's JSON and map it to the Event record in ServiceNow.

This is definitely work-in-progress, needs further work to meet specific customer needs, and is not officially supported. It should give a head start on getting this set up though.

Some prerequisites:

  • The service account in ServiceNow used by Google to connect to the instance should have "Web service access only" checked, and must have the "evt_mgmt_integration" role assigned.
  • The url for Google's webhook to post to is https://{Instance-name}.service-now.com/api/global/em/inbound_event?source={URL_parameter_value}
    • URL_parameter_value comes from the "URL parameter value" field on the Listener Transform Script record.
    • Note: for customers hosted in GCC, the URL is slightly different: https://{Instance-name}.servicenowservices.com/api/global/em/inbound_event?source={URL_parameter_value}

We configured the Listener Transform Script as follows:

  • Name: Google Monitoring Test Events Instance
  • Type: Instance (you could use a MID server to receive events as well, but we didn't for testing)
  • URL parameter value: googleJson (this can be anything you want, it forms the end of the URL referenced above)
  • Header name: generic-header
  • Header value: true

Script:

(function process( /*RESTAPIRequest*/ request, body) {
    /*Function that receives a JSON object, adding all its fields to the Additional information object. The field name is a concatenation of the field key and the parent field key if it exists.*/
    function updateAdditionalInfo(event, field, jsonObject, additionalInfo) {
        for (var key in jsonObject) {
            var newKey = key;
            if (field != "") {
                newKey = field + '_' + key;
            }

            /* SAMPLE PAYLOAD FROM GOOGLE
			   REFERENCED HERE https://cloud.google.com/monitoring/support/notification-options
              
			  {
            	"incident": {
            	  "incident_id": "f2e08c333dc64cb09f75eaab355393bz",
            	  "resource_id": "i-4a266a2d",
            	  "resource_name": "webserver-85",
            	  "state": "open",
            	  "started_at": 1385085727,
            	  "ended_at": null,
            	  "policy_name": "Webserver Health",
            	  "condition_name": "CPU usage",
            	  "url": "https://console.cloud.google.com/monitoring/alerting/incidents?project=PROJECT_ID",
            	  "summary": "CPU for webserver-85 is above the threshold of 1% with a value of 28.5%"
            	},
            	"version": 1.1
              }
             */

            // PERFORM YOUR SPECIFIC TRANSFORMS HERE
            
			/* Unclear if this should be mapped to message_key
			if(key == "incident_id") {
               event.message_key = jsonObject[key];
            }*/

            /* Alternate to resource_name below, not sure which one to use
			if(key == "resource_id") {
               event.node = jsonObject[key];
            }*/

            if (key == "resource_name") { // OR resource_id depending on need
                event.node = jsonObject[key];
            }

			// Map Google state of "open" to SN state of "New", otherwise assume it's "Closing"
            if (key == "state") {
                if (jsonObject[key] == "open") {
                    event.resolution_state = "New";
                } else {
                    event.resolution_state = "Closing";
                }
            }
			
			// Map epoch time to SN date time
            if (key == "started_at") { // 1385085727
                event.time_of_event = convertEpochToSNTime(jsonObject[key]);
			}
			
			/* Not sure this is used in SN
			if (key == "ended_at") { // 1385085738
                if (jsonObject[key] > 0) {
					event.time_of_event = convertEpochToSNTime(jsonObject[key]);
				}
			}*/
			
			// Map policy_name to metric_name
            if (key == "policy_name") {
                event.metric_name = jsonObject[key];
			}
			
			// Map condition_name to resource and type
			if (key == "condition_name") {
                event.resource = jsonObject[key];
                event.type = jsonObject[key];
			}
			
			// Concatenate the url and summary to the event description text
            if (key == "summary") {
				// If description is empty, add a newline before, otherwise just add the summary or url
				if (event.description == "") {
					event.description = jsonObject[key];
				} else {
					event.description = jsonObject[key] + '\n' + event.description;
				}
            }
            if (key == "url") {
				// If description is empty, add a newline before, otherwise just add the summary or url
				if (event.description == "") {
					event.description = jsonObject[key];
				} else {
					event.description = event.description + '\n' + jsonObject[key];
				}
            }

            additionalInfo[newKey] = jsonObject[key];
        }
    }

    function convertEpochToSNTime(epochTime) {
        var gdt = new GlideDateTime();
        gdt.setNumericValue(epochTime * 1000);
        return gdt.toString();
    }

    try {
        gs.info("TransformEvents_generic received body:" + body);
        var jsonObject = JSON.parse(body);
        var event = new GlideRecord('em_event');
        
		//TODO: Need to define
		event.source = "Google"; 
        
		//TODO: Need to define. Specific instance of the source. For example, SCOM 2012 on 10.20.30.40
		event.event_class = "GoogleMonitoring"; 
		
		//TODO: Set the event severity. All Google events will be 1-Critical. Scale is 1-5.
        event.severity = "1"; 

        var additionalInfo = {};
        updateAdditionalInfo(event, "", jsonObject, additionalInfo);
        /*Iterates over Additional information JSON object and adds all nested objects' fields as fields of the Additional information object*/
        var notDone = true;
        while (notDone) {
            notDone = false;
            for (var key in additionalInfo) {
                if (Object.prototype.toString.call(additionalInfo[key]) == '[object Object]') {
                    notDone = true;
                    updateAdditionalInfo(event, key, additionalInfo[key], additionalInfo);
                    additionalInfo[key] = "";
                }
            }
        }
        gs.info("TransformEvents_generic generated additional information:" + JSON.stringify(additionalInfo));
        event.additional_info = JSON.stringify(additionalInfo);
        event.insert();
    } catch (er) {
        gs.error(er);
        status = 500;
        return er;
    }
    return "success";
})(request, body);
Version history
Last update:
‎09-25-2020 12:11 PM
Updated by: