Many companies are using our vRealize Automation plug-in for ITSM which delivers blueprints to standard ServiceNow catalogs. For the majority of use cases it is working well and many find it useful. And boring as hell. The reason for this is that the plug-in is not giving you everything the vRA portal does. However, just because we are not given something, it doesn’t mean we can’t take it. We can take it, abuse it and make it our own, thanks to ServiceNow’s vast possibilities for extending functionality.
This piece of blog is about making ServiceNow talk to vRO for the simplest scenario I could think of – getting validations for a field in an XaaS blueprint. The concept and scripts in this example can be churned, stirred and sodomized for any other purpose you can think of. Again, your imagination is limited only by your own imagination. Some say these limits can be pushed further by the right mix of substances, but we’re here to talk about vRA.
I need to be as precise as possible and evade any additional noise, so I have made the following assumptions before continuing:
- Integration between ServiceNow and vRA has already been configured
- Services and Catalog Items have been imported from vRA
- There are no issues with the integration between ServiceNow and vRA
- vRA is using the embedded vRO for extensibility
- The personnel implementing this has basic vRA, vRO and ServiceNow skills
- The personnel implementing this has basic Javascript skills
- The personnel implementing this has tenant administrator rights in vRA
- The personnel implementing this has administrator rights in vRO.
- The personnel implementing this has System Administrator rights in ServiceNow
So, the basic idea is to have an XaaS blueprint and modify the constraints of one of the fields so that it validates against a vRO action:
Here’s the procedure to configure and test the blueprint:
- Create an XaaS blueprint
- Create a vRO Workflow with two input parameters called input1 and input2. Set input1 to be of type string and input2 to be of type number:
3. Add a Scriptable task to the Schema of the workflow and set input1 and input2 as its IN parameters.
4. Add the following Javascript code to the Scriptable task:
System.log(input1);System.log(input2);
5. Test the workflow by clicking on the Run button.
6. In vRA click the Design tab and then XaaS -> XaaS Blueprints -> New
7. Choose the workflow that was just created, click Next multiple times, accepting all default settings and click Finish.
8. Publish the XaaS blueprint and add it to a service that is imported into ServiceNow.
9. Request the XaaS blueprint from vRA and check if it runs without issues. Also, try to type some alphabetic characters into input2 and see if they get accepted.
10. It’s time to create validation for the input2 variable in the XaaS blueprint.
11. Open the XaaS blueprint for editing in vRA and select the Blueprint Form tab.
12. Click on input1 and modify the label to “input1 (String)”, click Apply, then click on input2 and modify the label to “input2 (Number)” and click Apply.
13. Open vRO Client and switch to Design view from the top drop down box next to the vRO logo.
14. Select the actions tab from the left pane (gear wheel with play button), create a new module and create an action inside (in this example it has the rather dull name setMaximumforInput2).
15. Open the action for editing and in the Scripting tab type the following code:
return 1000;
16. Set the return type to Number and save the action.
17. Go back to vRA and select input2, then select the Constraints tab and click Maximum value -> External. Select the newly created action from the dialog box and click Apply.
18. Test the blueprint again and enter a number bigger than 1000 in the input2 field. Click submit, then hover your mouse over the field surrounded by red line and check the message.
Okay, we have created an XaaS blueprint with a constraint value coming from vRO. Now, the real deal – ServiceNow syncs the catalog item, but the plug-in ignores the constraint if it’s an external one. So, we end up with a catalog item in SNow that is not having a validation. To go around this problem – or maybe crash through it – we need to configure ServiceNow to vRO integration, so that the request form is aware of the field validation.
Setting vRO Permissions – we want to do things properly, so we will create an integration user just for the vRO connection.
1. Create a custom group in vRA with the name SNowVROUsers by going to the vRA console and clicking Administration -> Users and Groups -> Custom Groups. Do not add any role to the group. Do not add any users to the group yet.
2. Log in to vRA as [email protected] and create a new local administrator (e.g. SNowVROUser).
3. Log back in to vRA as tenant administrator and add the user to the custom group.
4. Open the vRO Client and right click on the action module that is the parent of the setMaximumforInput2 action. At this point, I’m sure you have come up with a much more original name for the action.
5. Choose Edit Access rights…
6. Click Add Access rights…
7. In the Search box type the name of the group (e.g. SNowVROUsers)
8. Ensure that the rights assigned are at least View, Execute, Inspect.
Good. We have achieved basic security level and are now GDPR compliant. Next, we need to create a Script Include for connecting and authenticating ServiceNow to vRO. With Script Includes we govern how ServiceNow communicates with the MID server and they can be used in other scripts as libraries of functions, just like we include files in any other programming language.
1. Connect to a vRA appliance via SSH.
2. Issue the following command and copy the result. This will be the client we will use for OAuth requests to vRO:
grep -i cafe_cli= /etc/vcac/solution-users.properties | sed -e 's/cafe_cli=//'
3. Log in to ServiceNow as System Administrator.
4. Go to System Definition – Script Includes.
5. Click New.
6. Specify a meaningful and distinguishable name for the Script Include, set it to Active and make the Accessible from field set to “All application scopes”.
7. Paste the following code into the Script box and substitute with your own information where it’s marked with comments:
var GeneratevROToken = Class.create(); //Substitute with own script name GeneratevROToken.prototype = Object.extendsObject(AbstractAjaxProcessor, {//Substitute with own VROLogin: function(){ try{ VRASNLogger.debug('Inside GeneratevROToken.VROLogin: used to fetch vRO login token');//Substitute with own var errorHandler = new VRASNErrorHandling(); var midServer = gs.getProperty('vrasn.midserver.name'); //The MID server name. var endPoint = gs.getProperty('vrasn.end.point'); // This is the embedded vRO, which basically is our vRA server. var VRAUsername = "SNowVROUser"; //This is the vRO user we just created. Substitute with your own var VRAPassword = ""; //password, duh var cafeClient = ""; //Substitute with your own - This is the result of the command for getting the OAuth client we just issued over SSH var _endPoint = endPoint+"/SAAS/t/vsphere.local/auth/oauthtoken?grant_type=password"; //This is the URL of the authentication point var request = new sn_ws.RESTMessageV2(); request.setEndpoint(_endPoint); request.setHttpMethod("post"); request.setRequestHeader("Accept", "application/json"); request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); request.setMIDServer(midServer); request.setRequestBody('username='+VRAUsername+'&password='+VRAPassword+'&client_id='+cafeClient+'&domain=vsphere.local'); var response = request.execute(); VRASNLogger.info('Inside GeneratevROToken.VROLogin: REST call for fetching vRO login token'); //Substitute with your own script name if(response.haveError()) { VRASNLogger.error('REST call error found inside GeneratevROToken.VROLogin: '+ response.getErrorMessage()); //Substitute with your own errorHandler.notify("REST call error found inside GeneratevROToken.VROLogin: "+ response.getErrorMessage()); //Substitute with your own } var responseBody = response.getBody(); var parser = new JSONParser(); var parsed = parser.parse(responseBody); var token = parsed.access_token; return token; } catch(e){ var errorHandlerCatch = new VRASNErrorHandling(); VRASNLogger.error('Exception caught inside GeneratevROToken.VROLogin: '+e);//Substitute with own errorHandlerCatch.notify("Exception caught inside GeneratevROToken.VROLogin: "+e);//Substitute with own } }, type: 'GeneratevROToken'//Substitute with own });
8. Click Submit or Update to save the new script.
If you pay attention to the logging lines, you’ll see that I use the ITSM plug-in’s own logging facility. This way, I make this script part of the plug-in, so when you go to vRealize Automation → Logs, you will see the messages from using this custom Script Include.
Next, we have to create another Script Inlcude, that sends requests to vRO and fires the action used for validation.
1. Log in to ServiceNow as System Administrator.
2. Go to System Definition – Script Includes.
3. Click New.
4. Specify a meaningful and distinguishable name for the Script Include, set it to Active and make the Accessible from field set to “All application scopes”.
5. Select the checkbox Client callable. With this option we enable calling this script from the request form.
6. Paste the following code into the Script box and substitute with your own information where it’s marked with comments:
var RESTValidation = Class.create(); //substitue with own RESTValidation.prototype = Object.extendsObject(AbstractAjaxProcessor, { getVarMaximum : function() { //Initialize this.midServer = gs.getProperty('vrasn.midserver.name'); this.endPoint = gs.getProperty('vrasn.end.point'); //embedded vRO this.__orderVar = 0; this.errorHandler = new VRASNErrorHandling(); //Implementation try{ VRASNLogger.debug('Inside RESTValidation.getVarMaximum'); //substitute with own var auth = new GeneratevROToken(); //substitute with own. This is the script include used for vRO authentication var _endPoint = this.endPoint+"/vco/api/actions/a_nikolovn/SetMaximumforInput2/executions"; //substitute with your own path in vRO var request = new sn_ws.RESTMessageV2(); request.setEndpoint(_endPoint); request.setHttpMethod('post'); request.setRequestBody('{}'); var authGen = auth.VROLogin(); request.setRequestHeader("Accept", "application/xml"); request.setRequestHeader("Authorization", "Bearer "+authGen); request.setRequestHeader("Content-Type", "application/json"); request.setMIDServer(this.midServer); var response = request.execute(); if(response.haveError()) { VRASNLogger.error('REST call error found inside RESTValidation.getVarMaximum: '+ response.getErrorMessage());//substitute with your own class this.errorHandler.notify("REST call error found inside RESTValidation.getVarMaximum: "+ response.getErrorMessage());//substitute with your own class return response.getErrorMessage(); } else { var responsedata = response.getBody(); return gs.getXMLText(responsedata, "//number"); //parsing the XML Response from the action result } } catch(e){ VRASNLogger.error('Exception caught inside RESTValidation.getVarMaximum: '+e);//substitute with your own this.errorHandler.notify("Exception caught inside RESTValidation.getVarMaximum: "+e);//substitute with your own } }, type: 'RESTValidation' });
7. Click Submit or Update.
I’m sure you have noticed, that I have hardcoded the path to my action. I did so for simplicity, but the function can actually be modified to accept a path parameter so that the script include becomes more generic.
Time to create ServiceNow client scripts. The client scripts govern our request forms and control the behaviour of the field controls.
1. Ensure the XaaS blueprint has been imported into ServiceNow.
2. Go to Service Catalog – Maintain Items and search for the XaaS blueprint.
3. Click on input2 (Number) and copy the Name property (it should be “input2”).
4. Go to Catalog Administration – Catalog Client Scripts and click New.
5. Specify a meaningful and distinguishable name for the new client script (e.g. SNowValidation onLoad – it’s important to be as descriptive as possible)
6. Set the Applies to field to “A Catalog Item”.
7. Check the Active check box.
8. Set the Type field to onLoad. It will tell Service Now to run our script while loading the form.
9. In the Catalog item field search for the XaaS blueprint.
10. For the Script function enter the following Javascript. Ensure that the “input2” string is substituted with the name from step 3:
var input2Maximum=0; //we need to set a default limit and declare it on top to make the variable global function onLoad() { function ajaxResponse(serverResponse) { //set the input2Maximum global variable to the result from the API call input2Maximum = serverResponse.responseXML.documentElement.getAttribute("answer"); var labelElement = $('label_' + g_form.getControl("input2").id).select('label')[0]; labelElement.innerHTML = g_form.getLabelOf('input2') + " <font color=\"#278EFC\">Maximum:" + input2Maximum + "</font>"; } function hide_error() { g_form.hideErrorBox(g[h].variable); } try { var f = new GlideAjax('RESTValidation'); //substitute with the name of your REST validation Script Include f.addParam('sysparm_name', 'getVarMaximum'); f.getXML(ajaxResponse); //run the getVarMaximum function from the 'VGRRESTValidation' script. This is an async call and its own result is passed to the ajaxResponse function. This way the page loads without waiting for the call to finish }catch(error) { g_form.showErrorBox("input2", 'There was an issue with getting the field validation information' + error); window.setTimeout(hide_error, 5e3); return false; } }
A few words on this script. While the form is loading, ServiceNow runs the getVarMaximum function from the VGRRESTValidation script and doesn’t wait for an answer. This allows for the request to reach the vRO server through the MID server and get back with a response, without freezing the form, so the user can freely interact with it. This is achieved with f.getXML(ajaxResponse). If we don’t do it asynchronously, then the form will stop loading until a response has been received. Afterwards, we use the ajaxResponse function to get the actual value from the VRO action and display it as part of the field label. There is a reason why input2Maximum is a global variable.
We have received the maximum value, but how do we make the validation?
1. Go to Catalog Administration – Catalog Client Scripts and click New.
2. Specify a meaningful and distinguishable name for the new client script (e.g. SNowValidation onSubmit)
3. Set the Applies to field to “A Catalog Item”.
4. Check the Active check box.
5. Set the Type field to onSubmit.
6. In the Catalog item field search for the XaaS blueprint.
7. For the Script function enter the following Javascript. Ensure that the “input2” string is substituted with the correct field name:
function onSubmit() { function hide_error() { g_form.hideErrorBox(g[h].variable); } try { if(Number(g_form.getValue("input2"))> input2Maximum) //compares the result coming from vRO to the value currently in the field { g_form.showErrorBox("input2", "You can't specify a number larger than " + input2Maximum + ". Current value:" + g_form.getValue("input2")); window.setTimeout(hide_error, 5e3); return false; } }catch(error) { g_form.showErrorBox("input2", 'Please specify an integer value (non decimal value) ' + error); window.setTimeout(hide_error, 5e3); return false; } }
What we do here is comparing the input value against the global variable we defined in the previous script. If it’s bigger, then we show an error message in the form. The comparison itself is triggered when the user clicks Submit. You can always change it so that it triggers the comparison during a change in the field by setting the type to onChange.
Test by requesting the blueprint from ServiceNow and wait a few seconds until a blue notice pops up on the page next to the label of the field. When it does, it means the vRO action has been executed and the result has been passed to ServiceNow successfully.
Enter a value bigger than 1000 and click Order Now to test the validation.
The client script can be enhanced further to check if the input is having alphabetic or special characters, etc.
The onload script can be further optimized by creating a mechanism that caches frequently the information from vRO in a ServiceNow table. The onload script can then read the table which is much faster than calling vRO every time the page loads.