SailPoint IIQ and ServiceNow custom integration using ServiceNow REST APIs (ServiceNow Table API) – PART 2

[vc_row][vc_column][vc_column_text]SailPoint IIQ provides many out-of-the-box (OOTB) options to integrate with ServiceNow. Though OOTB integrations are great for most of the common use cases, it lacks the flexibility and provides less control over the entire process.

Many times we come across a requirement where we need total control, and that’s where this custom integration can be very useful.

Note: This is the second part of the series and covers the Service Catalog request management.  Please review Part 1 of this series which covers the pre-reqs, basic configuration, and incident management in detail.

Creating ServiceCatalog request in ServiceNow using REST APIs

ServiceCatalog requests (SR) are different than incidents. Some of the options available for incidents are not available for SR and vice-versa. The process of creating SR is more complicated than the process of creating incidents and involves sending multiple REST calls and useing the output of the first call as the input of subsequent steps.

The example below assumes all the prereqs from Part 1 of this blog series. This example also assumes that Service Catalog is already available on SN (catalog requests will be created under Service Catalog). If the requirement is to set the custom variables in request item, then you must create the variable template in request item before you try to do it through REST APIs.

Process To Create Service Catalog Request

  1.  Create Service Catalog Request (SR).
  2.  Create Request item (RI)
  3.  Create the custom variable and update its value.
  4.  Correlate the custom variable created in step3 with RI created in step2 using sys_id.
  5.  Correlate the RI back to SR using sys_id

SN object to SN Table mapping 

No. ServiceNow Object ServiceNow Table
1 SN Catalog request sc_request
2 SN Catalog request item sc_req_item
3 SN Catalog item variable sc_req_item_option
4 SN variable and request item correlation sc_req_item_mtom

Refer to the table below for SN URIs, operations, and HTTP Verbs.

No. Operation URI HTTP Verb
1 Create new Service Catalog Request https://xxx.service-now.com/api/now/v1/table/sc_request POST
2 Get the existing Service Catalog Request from ServiceNow https://xxx.service-now.com/api/now/v1/table/sc_request/sys_id

Note: sys_id is internal system identifier of SR object created earlier.

Examples:

Sys_id: 1a451fad4ff3130081630fbf9310c7b0

GET
3 Update the Service Catalog Request created earlier https://xxx.service-now.com/api/now/v1/table/sc_request/sys_id

Note: sys_id is internal system identifier of Service Catalog Request object created earlier.

Examples:

Sys_id: 1a451fad4ff3130081630fbf9310c7b0

PUT
4 Create new Request Item https://xxx.service-now.com/api/now/v1/table/sc_req_item POST
5 Update request item https://xxx.service-now.com/api/now/v1/table/sc_req_item/sys_id PUT
6 Create Variable https://xxx.service-now.com/api/now/v1/table/sc_item_option POST
7 Create Variable correlation with request item https://xxx.service-now.com/api/now/v1/table/sc_item_option_mtom POST

ServiceNow exposes multiple ways to authenticate. For this blog, we will use “basic authentication”. You can find more about basic authentication here.

The Pre-Reqs

The following variable values must be gathered before you start the implementation:

No. Variable E.g. Value Comments
1 EndpointURL https://xxx.service-now.com/api/now/v1/table Common portion of the SN URI. The suffix can be added to base URI to generate table specific URI
2 Username admin Username is used in basic authentication. This user must have the appropriate rights to manage incidents in SN
3 Password P#ss&rd134 Password is used in basic authentication.
4 CallerID 1a451fad4ff3130081630fbf933454 Sys_id of the caller, this is the sys_id of the SN user who will be set as a requester of an incident.
5 CatalogID 58d41bad4ff313dfd1630fbf9310cdsfds6 Sys_id of the service catalog
6 variableTemplateID 80sdf4d3ad4ff3130081630fbf9310c7zdf Sys_id of the variable template

This example expects the custom IIQ object which holds the ServiceNow connection details. Please refer Part 1 of this blog series to add the CatalogID, variableTemplateID

The Implementation

Workflow implementation can be anything, for detailed information and variable details, please refer to Part 1 of this blog series.

The following script is responsible for creating the service requests in SN. Code comments should help understanding overall flow of the request.

        import java.io.BufferedReader;
        import java.io.InputStreamReader;
        import java.io.OutputStreamWriter;
        import java.net.URL;
        import java.net.URLConnection;
        import java.util.Base64;
        import java.util.List;
        import java.util.Map;
        import org.json.simple.JSONObject;
        import org.json.simple.parser.JSONParser;
        import java.util.*;
        import sailpoint.object.*;
        log.info("Creating serviceNow Service Request");
        try
        {
			//Get Credentials
			String baseEndPointURL = ServiceNowCredsMap.get("EndpointURL");
			String username = ServiceNowCredsMap.get("Username");
			String encryptedPassword = ServiceNowCredsMap.get("Password");
			String CatalogID = ServiceNowCredsMap.get("CatalogID");
			String CallerID = ServiceNowCredsMap.get("CallerID");
			String variableTemplateID = ServiceNowCredsMap.get("variableTemplateID");
			
			//variable to hold sys_ids
			String sc_req_item_id = "";
			String sc_item_option_id = "";
			String sc_request_id = "";
			String sc_item_option_mtom_id = "";
			String reqNumber = "" ;
			
			
			//SN table names 
			String requested_item_table = "sc_req_item";
			String service_request_table = "sc_request";
			String variable_table = "sc_item_option";
			String variable_requestItem_correlation_table = "sc_item_option_mtom";
			
			//service request json
			String sr_request = "{'short_description':'IAM request for #UserName#','assignment_group':'Service Desk','urgency':'3','impact':'3','contact_type':'email','sys_created_by':'admin','opened_by':'admin','description':'#Description#','approval':'approved','request_state':'in_process','price':'0','opened_by':'#CallerID#'}";
			
			//requested item json
			String sr_requestitem_request = "{'state':'1','sys_created_by':'admin','impact':'3','active':'true','priority':'4','short_description':'TIPS','description':'#Description#','urgency':'3','comments':'','approval':'approved','comments_and_work_notes':'','cat_item':'#CatalogID#','upon_approval':'proceed','upon_reject':'cancel','request':'#ServiceRequestID#','price':'0'}";
			
			//variable request json
			String sr_requestitem_variable_request = "{'item_option_new':'#variableTemplateID#','value':'#WorkNotes#','sys_created_by':'admin','order':'1'}";
			
			//variable to request item correlation json
			String requestItem_variable_correlation_request = "{'request_item':'#RequestedItemID#','sc_item_option':'#VariableID#','sys_created_by':'admin'}";
			
			//check password and decrypt it
			if(encryptedPassword == null || encryptedPassword.isEmpty())
			{
				log.error("ServiceNow Error:Password can not be null or empty");
				return;
			}
			String decryptedPassword ="";
			try{
				decryptedPassword = context.decrypt(encryptedPassword);
			}
			catch(Exception e)
			{
				log.error("Error while decrypting password" + e);
			}
			
			log.error("Creating service request....");
			
			//create ServiceRequest
			URL serviceRequestURL = new URL(baseEndPointURL + "/" + service_request_table);
			URL requestItemURL = new URL(baseEndPointURL + "/" + requested_item_table);
			URL variableURL = new URL(baseEndPointURL + "/" + variable_table);
			URL correlationURL = new URL(baseEndPointURL + "/" + variable_requestItem_correlation_table);
			
			//open connection
			HttpURLConnection c = (HttpURLConnection) serviceRequestURL.openConnection();
			
			//Set Auth information
			String authStr = Base64.getEncoder().encodeToString((username +":"+ decryptedPassword).getBytes());

			//setting Authorization header
			c.setDoOutput(true);
			c.setRequestMethod("POST");
			c.setRequestProperty("Authorization", "Basic " + authStr);
			c.setRequestProperty("Content-Type", "application/json");

			//Create Description
			//Add request details
			//Here you can add as many attributes you want.
			String desc = "This is SN notification for " + requestMap.get("username") + "rn" + "User Information";
			for (String key : UserAttributeToDisplayNameMap.keySet())
			{
				if(key != null)
				{
					if(!desc.isEmpty())
					desc = desc + "rn";

					desc = desc + UserAttributeToDisplayNameMap.get(key) + ": " + requestMap.get(key);
				}
			}

			//Replace tokens
			sr_request = sr_request.replace("#UserName#",requestMap.get("username")).replace("#Description#",desc).replace("#CallerID#", CallerID).replace("rn","n");

			//Write to output stream
			OutputStream os = c.getOutputStream();
			os.write(sr_request.getBytes());
			os.flush();

			//Common variables
			BufferedReader bf;
			StringBuilder stringBuilder;
			String line;
			String response;
			JSONParser parse = new JSONParser();
			JSONObject jobj;
			JSONObject result;
			
			
			//Send Request and get response code
			if (c.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
			throw new RuntimeException("Failed : HTTP error code : " +
			c.getResponseCode());

			} else {
				bf = new BufferedReader(new InputStreamReader(c.getInputStream()));
				stringBuilder = new StringBuilder();
				
				while ((line = bf.readLine()) != null) {
				stringBuilder.append(line);
				}
				response = stringBuilder.toString();

				//return response;
				System.out.println("response : " + response);

				//Type caste the parsed json data in json object
				jobj = (JSONObject) parse.parse(response);
				result = (JSONObject) jobj.get("result");

				//Retrieve service request sys_id
				reqNumber = result.get("number");
				sc_request_id = result.get("sys_id");
				
				log.error("Service Request created Successfully. sys_id:"+ sc_request_id);
				
			}
			
			if(!sc_request_id.isEmpty())
			{
				//setting Authorization header
				HttpURLConnection c1 = (HttpURLConnection) requestItemURL.openConnection();
				
				c1.setDoOutput(true);
				c1.setRequestMethod("POST");
				c1.setRequestProperty("Authorization", "Basic " + authStr);
				c1.setRequestProperty("Content-Type", "application/json");
				
				//replace tokens
				sr_requestitem_request = sr_requestitem_request.replace("#ServiceRequestID#",sc_request_id).replace("#Description#",desc).replace("#CatalogID#", CatalogID).replace("rn","n");
				
				//Write to output stream
				os = c1.getOutputStream();
				os.write(sr_requestitem_request.getBytes());
				os.flush();
				
				//Send Request and get response code
				if (c1.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
				throw new RuntimeException("Failed : HTTP error code : " +
				c1.getResponseCode());

				} else {
					bf = new BufferedReader(new InputStreamReader(c1.getInputStream()));
					stringBuilder = new StringBuilder();

					while ((line = bf.readLine()) != null) {
					stringBuilder.append(line);
					}
					response = stringBuilder.toString();

					//return response;
					System.out.println("response : " + response);

					//Type caste the parsed json data in json object
					jobj = (JSONObject) parse.parse(response);
					result = (JSONObject) jobj.get("result");

					//Retrieve service request sys_id
					sc_req_item_id = result.get("sys_id");
				
					log.error("Service Request item created Successfully. sys_id:"+ sc_req_item_id);
				}
				
			}
			else
			{
				throw new RuntimeException("Error while creating service request, aborting..");
			}
			
			if(!sc_req_item_id.isEmpty())
			{
				//setting Authorization header
				HttpURLConnection c2 = (HttpURLConnection) variableURL.openConnection();
				
				c2.setDoOutput(true);
				c2.setRequestMethod("POST");
				c2.setRequestProperty("Authorization", "Basic " + authStr);
				c2.setRequestProperty("Content-Type", "application/json");
				
				//replacing tokens
				sr_requestitem_variable_request = sr_requestitem_variable_request.replace("#variableTemplateID#",variableTemplateID).replace("rn","n");
				
				//Write to output stream
				os = c2.getOutputStream();
				os.write(sr_requestitem_variable_request.getBytes());
				os.flush();
				
				//Send Request and get response code
				if (c2.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
				throw new RuntimeException("Failed : HTTP error code : " +
				c2.getResponseCode());

				} else {
					bf = new BufferedReader(new InputStreamReader(c2.getInputStream()));
					stringBuilder = new StringBuilder();

					while ((line = bf.readLine()) != null) {
					stringBuilder.append(line);
					}
					response = stringBuilder.toString();

					//return response;
					System.out.println("response : " + response);

					//Type caste the parsed json data in json object
					jobj = (JSONObject) parse.parse(response);
					result = (JSONObject) jobj.get("result");

					//Retrieve service request sys_id
					sc_item_option_id = result.get("sys_id");
				
					log.error("Variable created Successfully. sys_id:"+ sc_item_option_id);
				}
			}
			else
			{
				throw new RuntimeException("Error while creating service request item, aborting..");
			}
			
			if(!sc_item_option_id.isEmpty())
			{
				//setting Authorization header
				HttpURLConnection c3 = (HttpURLConnection) correlationURL.openConnection();
				
				c3.setDoOutput(true);
				c3.setRequestMethod("POST");
				c3.setRequestProperty("Authorization", "Basic " + authStr);
				c3.setRequestProperty("Content-Type", "application/json");
				
				//replacing tokens
				requestItem_variable_correlation_request = requestItem_variable_correlation_request.replace("#RequestedItemID#",sc_req_item_id).replace("#VariableID#", sc_item_option_id).replace("rn","n");
				
				//Write to output stream
				os = c3.getOutputStream();
				os.write(requestItem_variable_correlation_request.getBytes());
				os.flush();
				
				//Send Request and get response code
				if (c3.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
				throw new RuntimeException("Failed : HTTP error code : " +
				c3.getResponseCode());

				} else {
					bf = new BufferedReader(new InputStreamReader(c3.getInputStream()));
					stringBuilder = new StringBuilder();

					while ((line = bf.readLine()) != null) {
					stringBuilder.append(line);
					}
					response = stringBuilder.toString();

					//return response;
					System.out.println("response : " + response);

					//Type caste the parsed json data in json object
					jobj = (JSONObject) parse.parse(response);
					result = (JSONObject) jobj.get("result");

					//Retrieve service request sys_id
					sc_item_option_mtom_id = result.get("sys_id");
				
					log.error("Variable correlation created Successfully. sys_id:"+ sc_item_option_mtom_id);
				}
			}
			else
			{
				throw new RuntimeException("Error while creating variable correlation, aborting..");
			}
			
			workflow.put("incidentid",reqNumber);
			
			//Store incidentid on cube : Optional
			Identity idObj = context.getObjectByName(Identity.class,identityName);
			idObj.setAttribute("IncidentNo",sc_request_id);
			context.saveObject(idObj);
			
			log.info("ServiceNow ServiceRequest created..");
        }
        catch(Exception ex)
        {
			log.error("Unable to create serviceNow ServiceRequest:" + ex);
        }

Closing request is the same as that of closing incidents. Please refer Part 1 of this blog series for the code. Just replace the value of variable “input” with field relevant to your implementation.

Bonus Information: Okta OIN is an enterprise grade identity management service. It connects anyone on any application regardless of the device. It is cloud-based offering security, reliability, and the power to integrate with on premise directories, applications, and identity management systems.[/vc_column_text][/vc_column][/vc_row]