Leverage ServiceNow REST API with Powershell: Local admin user authentication with Oauth and MFA
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-08-2023 04:51 AM
This is my two cents to the community, as I struggled to find an explanatory answer to this. I would have posted this in the articles section, but it seems to be limited to specific users only.
In this post I will demonstrate how use Powershell to connect to ServiceNow instance as local user with MFA enforced using an Oauth token. The instructions should be newbie-friendly.
Recent use cases where I have used Powershell scripts to administer a ServiceNow instance:
- (Email) Inbound actions with script template. I needed to create dozens of Inbound Actions with different properties, and the ServiceNow JS just did not cut it. For example, Template Literals (Powershell: Here Strings) did not work for background scripts in my Utah-environment.
- Creating bunch of test emails to see that the inbound actions really work.
- Gathering and comparing CMDB-information from systems such as ConfigMgr, Active Directory and Airwatch.
- …And plenty more, I really like Powershell 😊
There is also a Powershell module to do automation: Snow-Shell/servicenow-powershell: PowerShell module to automate ServiceNow service and asset managem...
Here are the steps to get you up and running with powershell, MFA and Oauth.
Creating the Oauth application
- Use the Application navigator and navigate to “Application Registry”
- Create a new application registry endpoint with the option “Create an Oauth API endpoint for external clients”
- Give the endpoint a name and submit it.
You will need the Client ID and Client Secret later.
Note: You can authenticate with Powershell to the REST API without OAuth token, but you will probably end up doing it later. Typing MFA authenticator code each time you run a script will become tiring.
Next up: Powershell
Powershell connection preparations
Because MFA is enabled it is easiest to create script to retrieve the authentication token. I will provide the script template below. For the script you will need (1) The address of your environment (2) Your user credentials and (3) The OAuth Client ID and Secret from last step.
First we will save the credentials in an encrypted xml for easier use. You will need to do this for both your own credentials as well as oauth credentials. Security-wise here’s a short description on the saving of credentials: “The Export-Clixml cmdlet encrypts credential objects by using the Windows Data Protection API. The encryption ensures that only your user account on only that computer can decrypt the contents of the credential object. The exported CLIXML file can't be used on a different computer or by a different user.” Export-Clixml (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn
In my example, I will use filenames sn-oauth-mycomputer.xml for the Oauth credentials and sn-admin-mycomputer.xml for the user credentials. Run both commands and give the correct credentials.
get-credential | export-clixml sn-oauth-mycomputer.xml
get-credential | export-clixml sn-admin-mycomputer.xml
Requesting the authorization token
Now that you have the two credential files from the last step save the script below to the same folder and modify the first three variable lines in the script below. I have saved the file as oauth_creds.ps1.
## EDIT THESE
$oauthcred = import-clixml sn-oauth-mycomputer.xml # Oauth Id and Secret XML
$mycred = import-clixml sn-admin-mycomputer.xml # Admin user credentials XML
$myenv = "myenvironment.service-now.com" # The address to your environment
####
$clientid = $oauthcred.UserName
$clientsecret = $oauthcred.getNetworkCredential().Password
$username = $mycred.UserName
$password = $mycred.getNetworkCredential().Password
$authkey = read-host -prompt "AuthKey" # Prompt for the authenticator authentication key to environment
$password = $password + $authkey #Password + authkey in authentication body. If password = password and authkey = 123123, then the password in request body will be password123123
$RestEndpoint = "https://$($myenv)/oauth_token.do"
$restbody = @{
'grant_type' = 'password'
'client_id' = $clientid
'client_secret' = $clientsecret
'username' = $username
'password' = $password
}
write-host "Contacting $($restendpoint) for Oauth token"
$result = Invoke-RestMethod -Uri $RestEndpoint -Body $restbody -Method Post
if ($result) {
$global:oauth_response = $result # We will use global variable $oauth_response for it to be available outside script
$global:authheaders = @{
Authorization = "Bearer $($global:oauth_response.access_token)"
}
write-host 'Token response was saved to $oauth_response and authorization headers to $authheaders'
}
Now run the script. The script will prompt you for AuthKey which is the current MFA authorization key in your Authenticator app. If everything went smoothly your should end up with the following.
Using the token for queries
Now that you have authenticated you can use the authentication header to make REST API queries to your servicenow instance. The ServiceNow Table API lets you access much of stuff, so basically to GET things you will use this with your params:
Invoke-RestMethod -uri https://<myenv>.service-now.com/api/now/table/<tablename> -method GET -headers $authheaders
Invoke-RestMethod returns an object with an array “result”.
For example, the below query would return a result object with array containing users with email like example limited to 15 results:
invoke-restmethod -uri "https://myenvironment.service-now.com/api/now/table/sys_user?sysparm_query=emailLIKEexample&sysparm_limit=15" -method GET -headers $authheaders
You can easily save the array to a variable with
$result = (invoke-restmethod -uri "https://myenvironment.service-now.com/api/now/table/sys_user?sysparm_query=emailLIKEexample&sysparm_limit=15" -method GET -headers $authheaders).result
And then open it with the command out-gridview for more user friendly view of data.
Example: Get user_name of users belonging to group "Database" and save it to powershell variable
$group = invoke-restmethod (invoke-restmethod -uri "https://myenvironment.service-now.com/api/now/table/sys_user_group?sysparm_query=nameLIKEdatabase&sysparm_limit=1" -method GET -headers $authheaders).result
$groupmembers = (invoke-restmethod -uri "https://myenvironment.service-now.com/api/now/table/sys_user_grmember?group=$($group.sys_id)&sysparm_fields=user.user_name" -method GET -Headers $authheaders).result
PS U:\ServiceNow> $groupmembers
user.user_name
--------------
beth.anglin
fred.luddy
At some point the token will expire and you will get the following message:
Invoke-RestMethod: {"error":{"message":"User Not Authenticated","detail":"Required to provide Auth information"},"status":"failure"}
You can get a new token by running the oauth_creds.ps1 script again. There is also an option to use the refresh token, but I will keep it out of scope for this post not to be infinitely long.
Hopefully this post was helpful to someone. I intended to keep it simple and for that reason this post was long. There are plenty of things to improve in the above things, for example there is no security scope defined for the Oauth Endpoint and the scripts lack error handling.
- 7,020 Views