In this post I want to demystify the #ServiceNow vROPS outbound plugin for use in large organisations…. I will expand on the VMWARE blog post “ServiceNow Notifications with vRealize Operations 7.5” by Matt Bradford but will go into some detail / troubleshooting so you don’t have to.

Let me start off by saying that like most of my other custom outbound plugin posts this is totally unsupported by VMware so GSS says NO… but if your like me and need a solution YESTERDAY… keep reading!

So if you have tried to create an outbound plugin for #ServiceNow in #vROPS and got the 403 error when clicking “test” it’s likely that your organisation enforces “Data Policy and/or uses non standard fields” so the out of the box from vRops 7.5 might not work for you!

So just ignore the error and save the Outbound plugin anyway…

So to start I suggest using an REST client to test that the account vRops is using has the required permissions (the user needs the “ITIL” role) and you know which required fields meet your organisations ServiceNow data policy for incident creation.

Below is an example of the body in JSON to POST (depending on your organisation it will differ…) also note that some fields in #ServiceNow will require the sys_id and not a string of the friendly name… For example: caller_id, business_service and assignment_group

{
"state": "1",
"caller_id": "jahsgdfkadsrf56sdf65sd789fsdfgdf",
"contact_type": "System-generated",
"business_service": "dsf457sdf6tgsdkskjssduif90654xdffgd",
"category": "Incident",
"assignment_group": "dfsdfsdt675rsd7gyusdousdhfiuhds",
"short_description": "vMan Testing the short description",
"description": "vMan Testing the normal description",
"impact": "3",
"work_notes": "Hooooooo My Gawd vRops detected broken stuffs"
}

Obtain the sys_id for the “caller” by right clicking on the user in the #ServiceNow GUI, don’t freak out… this is data from fake dev instance provided by the Development ServiceNow portal.

Don’t forget to do the same for other fields that require sys_id

So here is an example of a successful post using “description” as an additional mandatory field…

Woohoo it worked… I know the account has the required access / fields.

Using the default #vROPS #ServiceNow outbound plugin I was getting the following 403 error

{
"error": {
"message": "Operation Failed",
"detail": "Data Policy Exception: The following fields are mandatory: Description"
},
"status": "failure"
}

Below is the config.json file from the #vROPS #ServiceNow outbound plugin which you can see has comments instead of description and doesn’t work in my case….

{
  "newAlert": {
    "requestMethod": "POST",
    "urlPostfix": "api/now/table/incident",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "payload": {
      "short_description": "${alertName} - ${alertId}",
      "comments": "New alert was generated ${host:-N/A} at ${startDate}: \nAlert Name: ${alertName} \nResource Id: ${resourceId} \nResource Name: ${resourceName} \nAdapter Kind: ${adapterKind} \nResource Kind: ${resourceKind} \nCriticality: ${criticality} \nImpact: ${impact} \nHealth: ${health} \nRisk: ${risk} \nEfficiency: ${efficiency} \nType: ${type} \nSubType: ${subType} \nStatus: ${status} \nAlarms info: ${symptomInfo} \n See more: ${alertDetailUrl:-N/A}",
      "caller_id": "${caller_id:-}",
      "category": "${category:-}",
      "subcategory": "${subcategory:-}",
      "business_service": "${business_service:-}",
      "contact_type": "${contact_type:-}",
      "state": "${state:-}",
      "hold_reason": "${hold_reason:-}",
      "impact": "${impact:-}",
      "urgency": "${urgency:-}",
      "priority": "${priority:-}",
      "assignment_group": "${assignment_group:-}",
      "assigned_to": "${assigned_to:-}",
      "severity": "${severity:-}",
      "upon_approval": "${upon_approval:-}",
      "problem_id": "${problem_id:-}",
      "caused_by": "${caused_by:-}",
      "rfc": "${rfc:-}",
      "close_code": "${close_code:-}",
      "close_notes": "${close_notes:-}"
    }
  },
  "updatedAlert": {
    "bindTo": ["get_sys_id"],
    "requestMethod": "PUT",
    "urlPostfix": "api/now/table/incident/${sys_id}",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "payload": {
      "comments": "Alert updated in ${host:-N/A} at ${updateDate}: \nAlert Name: ${alertName} \nResource Id: ${resourceId} \nResource Name: ${resourceName} \nAdapter Kind: ${adapterKind} \nResource Kind: ${resourceKind} \nCriticality: ${criticality} \nImpact: ${impact} \nHealth: ${health} \nRisk: ${risk} \nEfficiency: ${efficiency} \nType: ${type} \nSubType: ${subType} \nStatus: ${status} \nAlarms info: \n${symptomInfo} \n See more: ${alertDetailUrl:-N/A}",
      "caller_id": "${caller_id:-}",
      "category": "${category:-}",
      "subcategory": "${subcategory:-}",
      "business_service": "${business_service:-}",
      "contact_type": "${contact_type:-}",
      "state": "${state:-}",
      "hold_reason": "${hold_reason:-}",
      "impact": "${impact:-}",
      "urgency": "${urgency:-}",
      "priority": "${priority:-}",
      "assignment_group": "${assignment_group:-}",
      "assigned_to": "${assigned_to:-}",
      "severity": "${severity:-}",
      "upon_approval": "${upon_approval:-}",
      "problem_id": "${problem_id:-}",
      "caused_by": "${caused_by:-}",
      "rfc": "${rfc:-}",
      "close_code": "${close_code:-}",
      "close_notes": "${close_notes:-}"
    }
  },
  "canceledAlert": {
    "bindTo": ["get_sys_id"],
    "requestMethod": "PUT",
    "urlPostfix": "api/now/table/incident/${sys_id}",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "payload": {
      "comments": "Alert canceled in ${host:-N/A} at ${updateDate}: \nAlert Name: ${alertName} \nResource Id: ${resourceId} \nResource Name: ${resourceName} \nAdapter Kind: ${adapterKind} \nResource Kind: ${resourceKind} \nCriticality: ${criticality} \nImpact: ${impact} \nHealth: ${health} \nRisk: ${risk} \nEfficiency: ${efficiency} \nType: ${type} \nSubType: ${subType} \nStatus: ${status} \nAlarms info: \n${symptomInfo} \n See more: ${alertDetailUrl:-N/A}",
      "caller_id": "${caller_id:-}",
      "category": "${category:-}",
      "subcategory": "${subcategory:-}",
      "business_service": "${business_service:-}",
      "contact_type": "${contact_type:-}",
      "state": "${state:-}",
      "hold_reason": "${hold_reason:-}",
      "impact": "${impact:-}",
      "urgency": "${urgency:-}",
      "priority": "${priority:-}",
      "assignment_group": "${assignment_group:-}",
      "assigned_to": "${assigned_to:-}",
      "severity": "${severity:-}",
      "upon_approval": "${upon_approval:-}",
      "problem_id": "${problem_id:-}",
      "caused_by": "${caused_by:-}",
      "rfc": "${rfc:-}",
      "close_code": "${close_code:-}",
      "close_notes": "${close_notes:-}"
    }
  },
  "get_sys_id": {
    "requestMethod": "GET",
    "urlPostfix": "api/now/table/incident?short_description=${alertName} - ${alertId}",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "responseMappings": {
      "sys_id": "/result/0/sys_id"
    }
  },
  "properties" : [
    {
      "key": "caller_id",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 10,
      "dispOrder": 10,
      "default": "",
      "enum": false
    },
    {
      "key": "category",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 20,
      "dispOrder": 20,
      "default": "",
      "enum": false
    },
    {
      "key": "subcategory",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 30,
      "dispOrder": 30,
      "default": "",
      "enum": false
    },
    {
      "key": "business_service",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 40,
      "dispOrder": 40,
      "default": "",
      "enum": false
    },
    {
      "key": "contact_type",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 50,
      "dispOrder": 50,
      "default": "",
      "enum": false
    },
    {
      "key": "state",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 60,
      "dispOrder": 60,
      "default": "",
      "enum": false
    },
    {
      "key": "close_code",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 64,
      "dispOrder": 64,
      "default": "",
      "enum": false
    },
    {
      "key": "close_notes",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 67,
      "dispOrder": 67,
      "default": "",
      "enum": false
    },
    {
      "key": "hold_reason",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 70,
      "dispOrder": 70,
      "default": "",
      "enum": false
    },
    {
      "key": "impact",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 80,
      "dispOrder": 80,
      "default": "",
      "enum": false
    },
    {
      "key": "urgency",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 90,
      "dispOrder": 90,
      "default": "",
      "enum": false
    },
    {
      "key": "priority",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 100,
      "dispOrder": 100,
      "default": "",
      "enum": false
    },
    {
      "key": "assignment_group",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 110,
      "dispOrder": 110,
      "default": "",
      "enum": false
    },
    {
      "key": "assigned_to",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 120,
      "dispOrder": 120,
      "default": "",
      "enum": false
    },
    {
      "key": "severity",
      "type": "int",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 130,
      "dispOrder": 130,
      "default": "",
      "enum": false
    },
    {
      "key": "upon_approval",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 140,
      "dispOrder": 140,
      "default": "",
      "enum": false
    },
    {
      "key": "problem_id",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 150,
      "dispOrder": 150,
      "default": "",
      "enum": false
    },
    {
      "key": "caused_by",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 160,
      "dispOrder": 160,
      "default": "",
      "enum": false
    },
    {
      "key": "rfc",
      "type": "string",
      "identType": 0,
      "length": 0,
      "required": false,
      "nameKey": 170,
      "dispOrder": 170,
      "default": "",
      "enum": false
    }
  ]
}

Now to the work around!

So let’s say your stuck with the dreaded 403, have a deadline and need to impress your management types with the #ServiceNow auto ticketing solution from vROPS + your not on good terms with GSS…

You will find the location of config.json @ /usr/lib/vmware-vcops/user/plugins/outbound/vrops-generic-rest-plugin/conf/servicenow which can be modified with your desired structure 🙂

Note if your running HA make sure you change this file on all nodes.!!

Restart the vRops Analytics service and hopefully if you got the structure right it will start generating tickets!! Don’t forget just keep ignoring the 403 error on the “test” click in the outbound plugin.

Below is an outbound rule example for #ServiceNow

Hope you found this helpful.

vMan