
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
08-03-2018 04:54 AM - edited 12-30-2023 01:27 PM
NOTE: MY POSTINGS REFLECT MY OWN VIEWS AND DO NOT NECESSARILY REPRESENT THE VIEWS OF MY EMPLOYER, ACCENTURE.
DIFFICULTY LEVEL: ADVANCED
Assumes having taken the class SSNF and has good advanced level of knowledge and/or familiarity with Scripting in ServiceNow. Assumes good knowledge and/or familiarity of Orchestration, Workflows, and Scripting in ServiceNow.
Creating a lot of input fields for a Custom Activity can be tedious. I will introduce a couple of techniques here that alleviate the pain of having to do this; along with other nice side benefits. As a rule I look at using this technique when the parameters I have to pass to my PowerShell script get above four.
What I will cover:
- Passing through JSON as a scripted parameter
- Custom Activity Script Includes
- Custom Activity Logging
- JSON string passing through a Custom Activity
- PowerShell JSON parsing and object usage
- PowerShell JSON boxing
- JSON Result Parsing
I highly recommend reading the following articles as they will give context to what I present here, and I will be using scripts and techniques from these as well.
- Mini-Lab: Orchestration: Grabbing the Workflow Context Logs
- Mini-Lab: Workflow Script Includes
- Mini-Lab: Creating a Custom PowerShell Command Activity
Pre-Requisites
- This lab will work fine on your Personal Developer Instance (PDI).
- You will need to activate the Orchestration plugins to be able to create Custom Activities.
Mini-Lab: Activating Orchestration - Some Notes - You will need to install your own local MID Server if you do not have one already.
ServiceNow Discovery 101: Setting Up a Local MID Server - The Script Include library from this article:
Mini-Lab: Creating a Custom PowerShell Script Activity - we will be using the Script Include Libraries from here
TIPS
We will build this example Globally scoped. If you want to put this into the actual baseline PowerShell folder then you will need to change the application scope to PowerShell, create the new Custom Activity, and clear out the Category. The new activity will then appear in the PowerShell folder, and you can still use it in your Globally scoped workflows.
You might want to create an update set to capture your work. You can click on the Publish button on the new Custom Activity and the Test Workflow to save your work in the update set.
Lab 1.1: Modify the Script Include Library
We will modify the Script Include from the following article: Mini-Lab: Creating a Custom PowerShell Script Activity and tweak the processJSONParameters method to accept a JSON string, but NOT parse it. We want it to be passed through to the PowerShell script as a single scripted parameter. BTW, once you have created this activity it will be generic enough that you should never have to modify it again, AND it will be reusable for most any PowerShell script.
- Navigate to System Definitions > Script Includes
- Search for and edit the WorkflowActivityUtils Script Include created in the previous article.
- Modify the script to be the following:
// parse the incoming JSON containing the parameters and push it into the
// powershell activity parameters list
processJSONParameters: function(activityInput, executionParam) {
try {
var keepAsString = activityInput.Keep_As_String;
gs.info('---> keepAsString: ' + keepAsString); // debug
if (JSUtil.nil(keepAsString)) {
keepAsString = false;
}
if (!keepAsString && JSUtil.notNil(activityInput.Parameters)) {
// got this idea from the uncallable Script Include: ExchangeUtil
gs.info('---> Parameters: ' + activityInput.Parameters); // debug
// convert the incoming JSON string to an Object array
var params = JSON.parse(activityInput.Parameters);
gs.info('---> Parameters Parsed: ' + JSON.stringify(params)); // debug
// loop through the array and pull out the key/value pairs
for (var key in params) {
var parameter = {};
parameter.name = key;
parameter.type = "plain";
parameter.value = params[key];
// this is how you actually place the variables into the execution call
executionParam.powershellVariables.push(parameter);
}
} else if (keepAsString) {
var parameterJSON = {};
parameterJSON.name = 'parameterJSON';
parameterJSON.type = "plain";
parameterJSON.value = activityInput.Parameters;
executionParam.powershellVariables.push(parameterJSON);
}
gs.info('---> Parameters: ' + JSON.stringify(executionParam)); // debug
} catch (err) {
gs.info('---> [{1}] ERROR: {0}',
[err, this.location + '.processJSONParameters']);
}
},
4. Click on the Update button to save your work.
NOTES
We have modified the processJSONParameters with the following.
1. Added a new parameter: keepAsString (boolean) to control whether we want the JSON string to be parsed or just passed through to the PowerShell script.
2. Added a check to see if keepAsString is present. If not, then set it to a default of false (parse the JSON string)
3. Added keepAsString == false to the parse "If" statement.
4. Added "If" keepAsString to the end of the method to just add the JSON string to the parameter stream to be passed straight through unmodified to the PowrShell script; where the actual parsing and usage will occur.
5. I also added a LOT of debug statements to show you how the variables transition. Look for the results in the System Log. Obviously, you will need to remove such debug statements from your code prior to going to production.
5. From the Script Includes list view search for and edit the ML_WorkflowUtils Script Include (Mini-Lab: Workflow Script Includes).
6. Modify the script to add the following new method:
grabWorkflowContextLogs : function(context, scratchpad) {
// This is where our context logs are kept
// We use the context object in our running workflow to retrieve these logs
// Make sure you grab errors (we could pull in everything is we so choose)
var wfContextLogs = new GlideRecord('wf_log');
wfContextLogs.addQuery('context', context.sys_id);
wfContextLogs.addQuery('level', '2'); // errors
// we may not care about these if they occur - comment out if needed
wfContextLogs.addQuery('message','NOT LIKE','%ReferenceError%');
// Order by most recent descending
wfContextLogs.orderByDesc('sys_update_on');
wfContextLogs.query();
// Print them out into the System log to view them
message = '';
while (wfContextLogs.next()) {
message += wfContextLogs.message + '\n';
}
gs.info('---> Context logs:\n' + message + ''); // debug
// now push the errors out onto the data stream!
scratchpad.context_errors = message;
},
7. Click on the Update button to save your work.
This method will allow us to capture any problems that occur within the activity that would normally not be placed onto the workflow data bus.
Next, we will be modifying the Run PowerShell Script activity from the previous article, and creating a new workflow to demonstrate usage.
Lab 1.2: Modify the Custom Run PowerShell Script Activity
- Navigate to Orchestration > Workflow Editor, and open the Workflow Editor.
- In the Workflow Editor navigate to the Custom > PowerShell
- Right-click Run PowerShell Script activity in the navigator and click: Copy Into Global Scope as New Activity
4. Fill in the Activity Name field with the following: Generic Run PowerShell Script
5. Open your new Generic Run PowerShell Script Activity.
6. Click on the Inputs button.
7. Add a new parameter:
Name: Keep_As_String
Type: Boolean
Mandatory: No
Default: false
8. Click on the Continue button.
9. Now click on the Save button to save your work.
That is it for the new activity. Remember everything will be handled by our Script Include function, and all the JSON parsing and boxing will be accomplished in our PowerShell script.
So now onto creating our PowerShell script.
Lab 1.3 PowerShell - JSON Parsing and Boxing
To parse a JSON string into an object is pretty straightforward.
1. From your instance navigate to MID Server > Script Files.
2. Click on the New button to create a new script.
3. Fill in the form with the following:
Name: echobackJSON.ps1
Parent: PowerShell
Active: checked
Description: Echo back all passed parameters
Script:
#Write-Host $parameterJSON
# parse the JSON parameter string
$jsonObj = $parameterJSON | ConvertFrom-Json
#Write-Host $jsonObj.test_1
#Write-Host $jsonObj.test_2
#Write-Host $jsonObj.test_3
# box up the JSON object and echo it back to the ECC Queue for processing
$output = @{}
$output.test_1= $jsonObj.test_1 + " --> completed!"
$output.test_2 = $jsonObj.test_2 + " --> completed!"
$output.test_3 = $jsonObj.test_3 + " --> completed!"
$objectJSON = New-Object -TypeName PSObject -Prop $output | ConvertTo-Json
WRITE-HOST $objectJSON
NOTES
This script uses the ConvertFrom-Json commandlet to parse a JSON string into a PowerShell object variable. Boom!
The commented out Write-Host lines are in case you need to debug the result. Note how the various object elements are addressed.
To box up the resultant variables into a JSON string is a bit more complicated. You have to specify that you want the object turned into a new object (which at the same time will be converted to JSON using the ConvertTo-Json commandlet). This, btw, is an MIND-BOGGLINGLY USEFUL TECHNIQUE to move large amounts of data easily back to the calling workflow via the ECC queue!
4. Click on the Submit button to save your work.
Now it is time to create our workflow to test everything.
Lab 1.4: Creating the Workflow and Testing
We need to now create a workflow that uses our new Custom Activity, and parses the returned JSON.
1. From the Workflow Editor create a new Workflow:
Name: Generic Run PowerShell Script Test
Table: Global [global]
Description: Test the Generic Run PowerShell Script activity, and parse the JSON returned results
2. Click on the Submit button.
3. Navigate to Core > Utilities and drag out a Run Script activity. Place it between the Begin and End activities.
Name: Intialize
Script:
var location = context.name + '.' + activity.name;
// Place current.variables into workflow.scratchpad (best practice)
var parameters = {};
parameters.test_1 = 'Hello world!';
parameters.test_2 = 'Yet another test.';
parameters.test_3 = 'Hi there!';
workflow.scratchpad.parameters = JSON.stringify(parameters);
gs.info('---> [{1}] Parameters were: {0}', [workflow.scratchpad.parameters, location]);
workflow.scratchpad.host = '127.0.0.1';
workflow.scratchpad.script = 'echobackJSON.ps1';
4. Click on the Submit button.
5. Navigate to Custom > Global > PowerShell and drag out our new custom Generic Run PowerShell Script activity.
6. Fill in the form with the following:
Name: Call the Echoback Script
Host: ${workflow.scratchpad.host}
Script Name: ${workflow.scratchpad.script}
Parameters: ${workflow.scratchpad.parameters}
Keep As String: checked
7. Click the Submit button to save your activity to the canvas.
8. Drag out a Run Script activity.
Name: Handle Success
Script:
var location = context.name + '.' + activity.name;
var echoBack = data.get(4);
// make sure that the output contains json...maybe.
if (JSUtil.notNil(echoBack.output) && echoBack.output.indexOf('{') > -1 ) {
var outputJSON = JSON.parse(echoBack.output);
message = '---> [{0}] Results: \n\t{1}\n\t{2}\n\t{3}\n';
// verify that everything came back as expected
gs.info(message, [location, outputJSON.test_1, outputJSON.test_2, outputJSON.test_3]);
}
9. Click the Submit button to save your activity to the canvas.
10. Drag out another Run Script activity.
Name: Handle Failure
Script:
var location = context.name + '.' + activity.name;
new ML_WorkflowUtils(location).grabWorkflowContextLogs(context, workflow.scratchpad);
gs.error('---> [{0}] Something bad happened!\n{1}\n',
[location, workflow.scratchpad.context_errors]);
11. Wire up the workflow to match the following diagram:
12. Run the workflow!
13. You should have a successful run that looks like this:
14. From your instance navigate to System Logs > System Log > All. The System Logs list view will be displayed.
15. Filter for messages starting with "--->", and order by Message descending. You should see entries like the following:
You should see the "completed" message which shows that we had a complete round-trip of the data! The PowerShell script is the real secret here. It is worth studying carefully.
There you go! You now have a way to work with JSON from the workflow directly to the PowerShell script, and pass back JSON to be parsed and used in the workflow! Cool!
Enjoy!
Steven Bell.
If you find this article helps you, don't forget to log in and mark it as "Helpful"!
Originally published on: 08-03-2018 06:54 AM
I updated the code, fixed broken links, and brought the article into alignment with my new formatting standard.
- 2,692 Views