Running PowerShell scripts programmatically with ServiceNow

I've created an extended and updated version of John J. Andersen's 2013 PowerShellProbe class (opens in a new tab) for ServiceNow. It allows you to run custom PowerShell scripts on a MID Server from a Script Include or Background script. You can get it here (opens in a new tab).

Example usage

var pb = new PowershellProbe("EC2WINMID1", "127.0.0.1")
 
var script =
  "$myVarName = 'Thank you John Andersen'\n" + "Write-Host $myVarName\n"
 
pb.setScript(script)
 
var response = pb.execute(true)
 
gs.info("OUTPUT: " + response.output) // OUTPUT: Thank you John Andersen
gs.info("ERROR: " + response.error) // ERROR: null

Or with an existing script file:

var MIDSERVER = "midserver_name"
var MIDSERVER_IP = "123.456.789.101"
 
var pb = new global.PowershellProbe(MIDSERVER, MIDSERVER_IP)
 
var params = {
  myVarName: "ValueOne",
  myVarNameTwo: "ValueTwo",
}
 
pb.setScriptByFilePath("ExistingScript.ps1", params)
 
var response = pb.execute(true)

Some of the changes I made

John's script has a reference to Packages.com.glide.util.StringUtil.escapeHTML(script) which is no longer available in the latest versions of ServiceNow. I've updated the script to use GlideStringUtil instead.

I've also included a method setScriptByFilePath to run a PowerShell script file that already exists on your MID Server.

John's original also includes a maxWaitTimeForEccQueue property, which you can choose to use or not, but which isn't part of the gist.

Get the gist here (opens in a new tab) or view the source code below:

// Original: https://john-james-andersen.com/blog/service-now/powershell-probe-and-utility-for-servicenow.html
var PowershellProbe = Class.create()
PowershellProbe.prototype = {
  initialize: function (mid, server) {
    this.setMidServer(mid)
    this.setPSServer(server)
  },
 
  _createEccOutputRecord: function () {
    var ecc = new GlideRecord("ecc_queue")
    ecc.initialize()
    ecc.agent = this.mid
    ecc.topic = "Powershell"
    ecc.name = "Windows - Powershell"
    ecc.source = this.psServer
    ecc.queue = "output"
    ecc.state = "ready"
    ecc.payload = this._getPayloadString()
    return ecc.insert()
  },
 
  _getPayloadString: function () {
    var xmldoc = new XMLDocument("<parameters/>")
 
    var el = xmldoc.createElement("parameter")
    xmldoc.setCurrent(el)
    xmldoc.setAttribute("name", "skip_sensor")
    xmldoc.setAttribute("value", "true")
 
    xmldoc.setCurrent(xmldoc.getDocumentElement())
    el = xmldoc.createElement("parameter")
    xmldoc.setCurrent(el)
    xmldoc.setAttribute("name", "probe_name")
    xmldoc.setAttribute("value", "Windows - Powershell")
 
    xmldoc.setCurrent(xmldoc.getDocumentElement())
    el = xmldoc.createElement("parameter")
    xmldoc.setCurrent(el)
    xmldoc.setAttribute("name", "script.ps1")
    xmldoc.setAttribute(
      "value",
      this.prettyScript ? this.prettyScript : this.scriptFileInvocation
    )
 
    return xmldoc.toString()
  },
 
  _getPayload: function (ecc) {
    if (ecc.payload != "<see_attachment/>") {
      return ecc.payload
    }
    var sa = new Packages.com.glide.ui.SysAttachment()
    var payload = sa.get(ecc, "payload")
    return payload
  },
 
  _getResponse: function (eccQueueRecordId) {
    var maxtime = gs.getProperty(
      "com.snc.integration.powershellprobe.maxWaitTimeForEccQueue",
      60
    )
    var counter = 0
    var found = false
    var eccResponse = new GlideRecord("ecc_queue")
    eccResponse.addQuery("queue", "input")
    eccResponse.addQuery("response_to", eccQueueRecordId)
    while (counter < maxtime && found == false) {
      eccResponse.query()
      if (eccResponse.next()) {
        found = true
      }
      gs.sleep(1000)
      counter++
    }
    var payload = this._getPayload(eccResponse)
    var xmldoc = new XMLDocument(payload)
    var retObj = new Object()
    retObj.output = ""
    retObj.error = ""
    if (found) {
      retObj.output = "" + this._getResponseOutputValue(xmldoc)
      retObj.error = "" + this._getResponseErrorValue(xmldoc)
      return retObj
    }
  },
 
  _getResponseErrorValue: function (xmldoc) {
    var error = xmldoc.getNodeText("//results/result/error")
    return error
  },
 
  _getResponseOutputValue: function (xmldoc) {
    var output = xmldoc.getNodeText("//results/result/output")
    return output
  },
 
  execute: function (waitForResponse) {
    var recSysId = this._createEccOutputRecord()
    if (waitForResponse == true) {
      var response = this._getResponse(recSysId)
      return response
    }
  },
 
  setMidServer: function (mid) {
    this.mid = "mid.server." + mid
  },
 
  setPSServer: function (server) {
    this.psServer = server
  },
 
  setScript: function (script) {
    this.prettyScript = script
    this.encodedScript = GlideStringUtil.escapeHTML(script)
  },
 
  setScriptByFilePath: function (filePath, params) {
    this.scriptFileInvocation = 'powershell "scripts\\' + filePath + '"'
 
    if (gs.nil(params)) return
 
    // Add parameters to the script invocation
    for (var key in params) {
      this.scriptFileInvocation += " -" + key + " " + params[key].toString()
    }
  },
 
  type: "PowershellProbe",
}