During my travels over the past year, one of the most common solutions I hear in projects is ServiceNow (SNOW). Some of the biggest companies in the world are either already using it, planning on switching to it, or need to integrate more into it.
Solutions like Rubrik do a great job of pre-building backup protection, monitoring, and recovery workflows into SNOW so you can protect VMs and recover files all within the ticket workflow. Read more here.
But, what if you need to integrate something that isn’t pre-built? This is where PowerShell and the SNOW REST APIs come into play. In this post, I’m going to show you how to authenticate, query a list of open incidents, and even create an incident all from PowerShell! To browse the REST API endpoints available use:
You can download the pre-written examples from the link below or continue reading through the post to copy/paste and build your own script as you go along. All examples were written and tested as working 07/23/18 using PowerShell 5.1 and PowerShell 6.0.2 on Windows:
Authentication is always the trickiest part with REST APIs and PowerShell. Thankfully SNOW offers 2 methods. 1. Username and password passed to every REST API call in the header. 2. Create a REST API application client ID to authenticate and use token-based access for subsequent queries.
I’ll show you both, starting with a basic username and password on every API call along with creating a list of active incidents:
############################################### # Configure variable below, you will be prompted for your SNOW login ############################################### $SNOWURL = "https://ven00001.service-now.com/" ################################################################################ # Nothing to configure below this line - Starting the main function ################################################################################ ############################################### # Prompting & saving SNOW credentials, delete the XML file created to reset ############################################### # Setting credential file $SNOWCredentialsFile = ".\SNOWCredentials.xml" # Testing if file exists $SNOWCredentialsFileTest = Test-Path $SNOWCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($SNOWCredentialsFileTest -eq $False) { $SNOWCredentials = Get-Credential -Message "Enter SNOW login credentials" $SNOWCredentials | EXPORT-CLIXML $SNOWCredentialsFile -Force } # Importing credentials $SNOWCredentials = IMPORT-CLIXML $SNOWCredentialsFile # Setting the username and password from the credential file (run at the start of each script) $SNOWUsername = $SNOWCredentials.UserName $SNOWPassword = $SNOWCredentials.GetNetworkCredential().Password ################################## # Building Authentication Header & setting content type ################################## $HeaderAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $SNOWUsername, $SNOWPassword))) $SNOWSessionHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $SNOWSessionHeader.Add('Authorization',('Basic {0}' -f $HeaderAuth)) $SNOWSessionHeader.Add('Accept','application/json') $Type = "application/json" ############################################### # Getting list of Incidents ############################################### $IncidentListURL = $SNOWURL+"api/now/table/incident" Try { $IncidentListJSON = Invoke-RestMethod -Method GET -Uri $IncidentListURL -TimeoutSec 100 -Headers $SNOWSessionHeader -ContentType $Type $IncidentList = $IncidentListJSON.result } Catch { Write-Host $_.Exception.ToString() $error[0] | Format-List -Force } ############################################### # Host output of the data ############################################### $IncidentCount = $IncidentList.count $ActiveIncidentCount = $IncidentList | Where-Object {$_.active -eq "true"} | Measure | Select -ExpandProperty Count "Open Incidents:" $IncidentList | Where-Object {$_.active -eq "true"} | Select number,short_description,opened_at,impact,priority | Sort-Object opened_at -Descending | Format-Table "ActiveIncidents:$ActiveIncidentCount" "TotalIncidents:$IncidentCount"
Be sure to change the $SNOWURL variable to yours! For token-based access you need to enable a REST API application by following the below instructions:
It should look like this:
With your client ID and secret, you can now run the below script. The script will prompt you for both your credentials, the client ID and secret, then save them securely in an XML file for subsequent runs. To change the credentials, delete the XML files:
############################################### # Configure variable below, you will be prompted for SNOW login & ClientID etc ############################################### $SNOWURL = "https://ven00001.service-now.com/" ################################################################################ # Nothing to configure below this line - Starting the main function of the script ################################################################################ ############################################### # Prompting & saving SNOW credentials, delete the XML file created to reset ############################################### # Setting credential file $SNOWCredentialsFile = ".\SNOWCredentials.xml" # Testing if file exists $SNOWCredentialsFileTest = Test-Path $SNOWCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($SNOWCredentialsFileTest -eq $False) { $SNOWCredentials = Get-Credential -Message "Enter SNOW login credentials" $SNOWCredentials | EXPORT-CLIXML $SNOWCredentialsFile -Force } # Importing credentials $SNOWCredentials = IMPORT-CLIXML $SNOWCredentialsFile # Setting the username and password from the credential file (run at the start of each script) $SNOWUsername = $SNOWCredentials.UserName $SNOWPassword = $SNOWCredentials.GetNetworkCredential().Password ############################################### # Prompting & saving ClientID and ClientSecret, delete the XML file to reset ############################################### # Setting credential file $SNOWClientCredentialsFile = ".\SNOWClientCredentials.xml" # Testing if file exists $SNOWClientCredentialsFileTest = Test-Path $SNOWClientCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($SNOWClientCredentialsFileTest -eq $False) { $SNOWClientCredentials = Get-Credential -Message "Enter SNOW ClientID and Client Secret" $SNOWClientCredentials | EXPORT-CLIXML $SNOWClientCredentialsFile -Force } # Importing credentials $SNOWClientCredentials = IMPORT-CLIXML $SNOWClientCredentialsFile # Setting the username and password from the credential file (run at the start of each script) $SNOWClientID = $SNOWClientCredentials.UserName $SNOWClientSecret = $SNOWClientCredentials.GetNetworkCredential().Password ################################## # Building SNOW API string & invoking REST API ################################## $SNOWSessionURL = $SNOWURL + "oauth_token.do" $AuthBody = [System.Text.Encoding]::UTF8.GetBytes("grant_type=password&username="+$Username+"&password="+$Password+"&client_id="+$SNOWClientID+"&client_secret="+$SNOWClientSecret) $Type = "application/json" # Authenticating with API Try { $SNOWSessionResponse = Invoke-RestMethod -Method POST -Uri $SNOWSessionURL -Body $AuthBody -ContentType "application/x-www-form-urlencoded" } Catch { $_.Exception.ToString() $error[0] | Format-List -Force } # Extracting the token from the JSON response $SNOWSessionHeader = @{'Authorization' = "Bearer $($SNOWSessionResponse.access_token)"} ############################################### # Getting list of Incidents ############################################### $IncidentListURL = $SNOWURL+"api/now/table/incident" Try { $IncidentListJSON = Invoke-RestMethod -Method GET -Uri $IncidentListURL -TimeoutSec 100 -Headers $SNOWSessionHeader -ContentType $Type $IncidentList = $IncidentListJSON.result } Catch { Write-Host $_.Exception.ToString() $error[0] | Format-List -Force } ############################################### # Host output of the data ############################################### $IncidentCount = $IncidentList.count $ActiveIncidentCount = $IncidentList | Where-Object {$_.active -eq "true"} | Measure | Select -ExpandProperty Count "Open Incidents:" $IncidentList | Where-Object {$_.active -eq "true"} | Select number,short_description,opened_at,impact,priority | Sort-Object opened_at -Descending | Format-Table "ActiveIncidents:$ActiveIncidentCount" "TotalIncidents:$IncidentCount"
Now that we’ve authenticated with SNOW and obtained a list of active incidents, what else can you do? The world is your oyster at this point, but to give you a little taste of the possibilities, paste the below into either script to create an incident:
############################################### # Creating Incident ############################################### $IncidentURL = $SNOWURL+"api/now/table/incident" # Creating JSON body $CreateIncidetJSON = "{ ""impact"": ""1"", ""notify"": ""1"", ""short_description"": ""Incident created by PowerShell"", ""contact_type"": ""email"", ""comments"": ""This incident was created using PowerShell and the SNOW REST APIs"" }" # POST to API Try { $IncidentPOSTResponse = Invoke-RestMethod -Method POST -Uri $IncidentURL -Body $CreateIncidetJSON -TimeoutSec 100 -Headers $SNOWSessionHeader -ContentType $Type } Catch { Write-Host $_.Exception.ToString() $error[0] | Format-List -Force } # Pulling ticket ID from response $IncidentID = $IncidentPOSTResponse.result.number ############################################### # Verifying Incident created and show ID ############################################### IF ($IncidentID -ne $null) { "Created Incident With ID:$IncidentID" } ELSE { "Incident Not Created" } ############################################## # End of script ##############################################
There you go! You’ve now authenticated with SNOW REST APIs, queried a list of open incidents and even created an incident automatically. You can now use this script to integrate SNOW into whatever you need.
Be sure to also check out the SNOW REST API explorer by searching for REST API in your SNOW search bar. If you found this useful please like, share, and follow me on twitter @joshuastenhouse. Happy scripting,
Joshua