Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions ServiceNow/Private/Get-ServiceNowAuth.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ function Get-ServiceNowAuth {
[CmdletBinding()]
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'requirement of azure automation')]

Param (
param (
[Parameter()]
[Alias('C')]
[hashtable] $Connection,

[Parameter()]
[Alias('N')]
[string] $Namespace = 'now',

[Parameter()]
[Alias('S')]
[hashtable] $ServiceNowSession
Expand All @@ -30,7 +34,11 @@ function Get-ServiceNowAuth {
process {

if ( $ServiceNowSession.Count -gt 0 ) {
$hashOut.Uri = $ServiceNowSession.BaseUri
if ($Namespace -ne 'now') {
$hashOut.Uri = $($ServiceNowSession.BaseUri -split ('api'))[0] + 'api/' + $Namespace
} else {
$hashOut.Uri = $ServiceNowSession.BaseUri
}
Comment on lines +37 to +41
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we're better served by changing the baseuri to end with /api and just append the namespace. this will involve changes here and invoke-service nowrestmethod.


# check if we need a new access token
if ( $ServiceNowSession.ExpiresOn -lt (Get-Date) -and $ServiceNowSession.RefreshToken -and $ServiceNowSession.ClientCredential ) {
Expand All @@ -46,7 +54,7 @@ function Get-ServiceNowAuth {
refresh_token = $ServiceNowSession.RefreshToken.GetNetworkCredential().password
}
}

$response = Invoke-RestMethod @refreshParams

$ServiceNowSession.AccessToken = New-Object System.Management.Automation.PSCredential('AccessToken', ($response.access_token | ConvertTo-SecureString -AsPlainText -Force))
Expand All @@ -64,8 +72,7 @@ function Get-ServiceNowAuth {
$hashOut.Headers = @{
'Authorization' = 'Bearer {0}' -f $ServiceNowSession.AccessToken.GetNetworkCredential().password
}
}
else {
} else {
# issue 248
$pair = '{0}:{1}' -f $ServiceNowSession.Credential.UserName, $ServiceNowSession.Credential.GetNetworkCredential().Password
$hashOut.Headers = @{ Authorization = 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) }
Expand All @@ -75,34 +82,28 @@ function Get-ServiceNowAuth {
$hashOut.Proxy = $ServiceNowSession.Proxy
if ( $ServiceNowSession.ProxyCredential ) {
$hashOut.ProxyCredential = $ServiceNowSession.ProxyCredential
}
else {
} else {
$hashOut.ProxyUseDefaultCredentials = $true
}
}
}
elseif ( $Connection ) {
} elseif ( $Connection ) {
Comment on lines -84 to +89
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please try and keep formatting the same :)

Write-Verbose 'connection'
# issue 248
$pair = '{0}:{1}' -f $Connection.Username, $Connection.Password
$hashOut.Headers = @{ Authorization = 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) }
$hashOut.Uri = 'https://{0}/api/now/v1' -f $Connection.ServiceNowUri
}
elseif ( $env:SNOW_SERVER ) {
$hashOut.Uri = 'https://{0}/api/now' -f $env:SNOW_SERVER
$hashOut.Uri = 'https://{0}/api/{1}/v1' -f $Connection.ServiceNowUri, $Namespace
} elseif ( $env:SNOW_SERVER ) {
$hashOut.Uri = 'https://{0}/api/{1}' -f $env:SNOW_SERVER, $Namespace
if ( $env:SNOW_TOKEN ) {
$hashOut.Headers = @{
'Authorization' = 'Bearer {0}' -f $env:SNOW_TOKEN
}
}
elseif ( $env:SNOW_USER -and $env:SNOW_PASS ) {
} elseif ( $env:SNOW_USER -and $env:SNOW_PASS ) {
$hashOut.Credential = New-Object System.Management.Automation.PSCredential($env:SNOW_USER, ($env:SNOW_PASS | ConvertTo-SecureString -AsPlainText -Force))
}
else {
} else {
throw 'A ServiceNow server environment variable has been set, but authentication via SNOW_TOKEN or SNOW_USER/SNOW_PASS was not found'
}
}
else {
} else {
throw "You must authenticate by either calling the New-ServiceNowSession cmdlet or passing in an Azure Automation connection object"
}
}
Expand Down
48 changes: 23 additions & 25 deletions ServiceNow/Private/Invoke-ServiceNowRestMethod.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function Invoke-ServiceNowRestMethod {
[CmdletBinding(SupportsPaging)]
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseBOMForUnicodeEncodedFile', '', Justification = 'issuees with *nix machines and no benefit')]

Param (
param (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change

[parameter()]
[ValidateSet('Get', 'Post', 'Patch', 'Delete')]
[string] $Method = 'Get',
Expand Down Expand Up @@ -48,6 +48,9 @@ function Invoke-ServiceNowRestMethod {
[parameter()]
[string] $FilterString,

[parameter()]
[string] $Namespace,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default to 'now'?


[parameter()]
[object[]] $Sort = @('opened_at', 'desc'),

Expand All @@ -74,7 +77,11 @@ function Invoke-ServiceNowRestMethod {
)

# get header/body auth values
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession
if ($namespace) {
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession -N $namespace
} else {
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession
}
Comment on lines +80 to +84
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ($namespace) {
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession -N $namespace
} else {
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession
}
$params = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession -N $namespace


$params.Method = $Method
$params.ContentType = 'application/json'
Expand All @@ -93,8 +100,7 @@ function Invoke-ServiceNowRestMethod {
if ( $SysId ) {
$params.Uri += "/$SysId"
}
}
else {
} else {
$params.Uri += $UriLeaf
}

Expand Down Expand Up @@ -153,8 +159,7 @@ function Invoke-ServiceNowRestMethod {
try {
$response = Invoke-WebRequest @params
Write-Debug $response
}
catch {
} catch {
$ProgressPreference = $oldProgressPreference
throw $_
}
Expand All @@ -174,12 +179,10 @@ function Invoke-ServiceNowRestMethod {
$content = $response.content | ConvertFrom-Json
if ( $content.PSobject.Properties.Name -contains "result" ) {
$records = @($content | Select-Object -ExpandProperty result)
}
else {
} else {
$records = @($content)
}
}
else {
} else {
# invoke-webrequest didn't throw an error per se, but we didn't get content back either
throw ('"{0} : {1}' -f $response.StatusCode, $response | Out-String )
}
Expand All @@ -190,8 +193,7 @@ function Invoke-ServiceNowRestMethod {
if ( $response.Headers.'X-Total-Count' ) {
if ($PSVersionTable.PSVersion.Major -lt 6) {
$totalRecordCount = [int]$response.Headers.'X-Total-Count'
}
else {
} else {
$totalRecordCount = [int]($response.Headers.'X-Total-Count'[0])
}
Write-Verbose "Total number of records for this query: $totalRecordCount"
Expand All @@ -215,25 +217,22 @@ function Invoke-ServiceNowRestMethod {

$end = if ( $totalRecordCount -lt $setPoint ) {
$totalRecordCount
}
else {
} else {
$setPoint
}

Write-Verbose ('getting {0}-{1} of {2}' -f ($params.body.sysparm_offset + 1), $end, $totalRecordCount)
try {
$response = Invoke-WebRequest @params -Verbose:$false
}
catch {
} catch {
$ProgressPreference = $oldProgressPreference
throw $_
}

$content = $response.content | ConvertFrom-Json
if ( $content.PSobject.Properties.Name -contains "result" ) {
$records += $content | Select-Object -ExpandProperty result
}
else {
} else {
$records += $content
}
}
Expand All @@ -249,18 +248,17 @@ function Invoke-ServiceNowRestMethod {
switch ($Method) {
'Get' {
$ConvertToDateField = @('closed_at', 'expected_start', 'follow_up', 'opened_at', 'sys_created_on', 'sys_updated_on', 'work_end', 'work_start')
ForEach ($SNResult in $records) {
ForEach ($Property in $ConvertToDateField) {
If (-not [string]::IsNullOrEmpty($SNResult.$Property)) {
Try {
foreach ($SNResult in $records) {
foreach ($Property in $ConvertToDateField) {
if (-not [string]::IsNullOrEmpty($SNResult.$Property)) {
try {
# Extract the default Date/Time formatting from the local computer's "Culture" settings, and then create the format to use when parsing the date/time from Service-Now
$CultureDateTimeFormat = (Get-Culture).DateTimeFormat
$DateFormat = $CultureDateTimeFormat.ShortDatePattern
$TimeFormat = $CultureDateTimeFormat.LongTimePattern
$DateTimeFormat = [string[]]@("$DateFormat $TimeFormat", 'yyyy-MM-dd HH:mm:ss')
$SNResult.$Property = [DateTime]::ParseExact($($SNResult.$Property), $DateTimeFormat, [System.Globalization.DateTimeFormatInfo]::InvariantInfo, [System.Globalization.DateTimeStyles]::None)
}
Catch {
} catch {
# If the local culture and universal formats both fail keep the property as a string (Do nothing)
$null = 'Silencing a PSSA alert with this line'
}
Expand All @@ -283,4 +281,4 @@ function Invoke-ServiceNowRestMethod {
}

$records
}
}
97 changes: 97 additions & 0 deletions ServiceNow/Public/New-ServiceNowCatalogItem.ps1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be good to have a way to control when checkout occurs in case folks want to add multiple (different) items before submitting order

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Submit a catalog request using Service Catalog API
.DESCRIPTION
Create a new catalog item request using Service Catalog API. Reference: https://www.servicenow.com/community/itsm-articles/submit-catalog-request-using-service-catalog-api/ta-p/2305836
.PARAMETER CatalogItemName
Name of the catalog item that will be created
.PARAMETER CatalogItemID
SysID of the catalog item that will be created
.PARAMETER Variables
Key/value pairs of variable names and their values
.PARAMETER PassThru
If provided, the new record will be returned
.PARAMETER Connection
Azure Automation Connection object containing username, password, and URL for the ServiceNow instance
.PARAMETER ServiceNowSession
ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession.
.EXAMPLE
New-ServiceNowRecord -CatalogItemName "Standard Laptop" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
New-ServiceNowRecord -CatalogItemName "Standard Laptop" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }
New-ServiceNowCatalogItem -CatalogItem "Standard Laptop" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }

Raise a new catalog request using Item Name
.EXAMPLE
New-ServiceNowRecord -CatalogItemID "04b7e94b4f7b42000086eeed18110c7fd" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
New-ServiceNowRecord -CatalogItemID "04b7e94b4f7b42000086eeed18110c7fd" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }
New-ServiceNowCatalogItem -CatalogItem "04b7e94b4f7b42000086eeed18110c7fd" -Variables @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' }

Raise a new catalog request using Item ID
.INPUTS
InputData
.OUTPUTS
PSCustomObject if PassThru provided
#>
function New-ServiceNowCatalogItem {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ID')]
param
(
[Parameter(Mandatory, ParameterSetName = 'Name')]
[string]$CatalogItemName,
[Parameter(Mandatory, ParameterSetName = 'ID')]
[string]$CatalogItemID,
Comment on lines +46 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about we combine this into one CatalogItem and check its format to know if its an ID or name?

[Parameter(Mandatory, ParameterSetName = 'Name')]
[Parameter(Mandatory, ParameterSetName = 'ID')]
[Alias('Variables')]
[hashtable]$InputData,
[Parameter()][Hashtable]$Connection,
[Parameter()][hashtable]$ServiceNowSession = $script:ServiceNowSession,
[Parameter()][switch]$PassThru
)

begin {
if ($CatalogItemName) {
#Lookup the sys_id of the Catalog Item name
$CatalogItemID = (Get-ServiceNowRecord -Table sc_cat_item -AsValue -Filter @('name', '-eq', $CatalogItemName )).sys_id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$CatalogItemID = (Get-ServiceNowRecord -Table sc_cat_item -AsValue -Filter @('name', '-eq', $CatalogItemName )).sys_id
$CatalogItemID = Get-ServiceNowRecord -table sc_cat_item -Filter @('name','-eq', $CatalogItemName) -Property sys_id -AsValue

if ([string]::IsNullOrEmpty($CatalogItemID)) { throw "Unable to find catalog item by name '$($catalogitemname)'" } else { Write-Verbose "Found $($catalogitemid) via lookup from '$($CatalogItemName)'" }
}
}
process {

$AddItemToCart = @{
Method = 'Post'
UriLeaf = "/servicecatalog/items/{0}/add_to_cart" -f $CatalogItemID
Values = @{'sysparm_quantity' = 1; 'variables' = $InputData }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make quantity a function parameter along with the other request body parameters, sysparm_also_request_for and sysparm_requested_for. https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_ServiceCatalogAPI.html#title_servicecat-POST-items-add_to_cart. add examples for these new parameters as well.

Namespace = 'sn_sc'
Connection = $Connection
ServiceNowSession = $ServiceNowSession
}

if ( $PSCmdlet.ShouldProcess($CatalogItemID, 'Create new catalog item request') ) {

$AddItemCartResponse = Invoke-ServiceNowRestMethod @AddItemToCart

if ($AddItemCartResponse.cart_id) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why might we not get a value for cart_id that we need this 'if'?

$SubmitOrder = @{
Method = 'Post'
UriLeaf = "/servicecatalog/cart/submit_order"
Namespace = 'sn_sc'
Connection = $Connection
ServiceNowSession = $ServiceNowSession
}

$SubmitOrderResponse = Invoke-ServiceNowRestMethod @SubmitOrder
}
if ( $PassThru ) {
$SubmitOrderResponse
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$SubmitOrderResponse
$SubmitOrderResponse | Select-Object @{'n'='number';'e'={$_.request_number}}, request_id

consider updating the output to something like the above so it can be piped directly into the other functions, eg. Get-ServiceNowRecord

}
Comment on lines +92 to +94
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are going to keep the check for cart_id, this needs to move inside the 'if' block otherwise $SubmitOrderResponse would be null

}
}
}
2 changes: 1 addition & 1 deletion ServiceNow/ServiceNow.psd1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take care of updating this file when I perform the release. Can you please undo these changes?

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'ServiceNow.psm1'

# Version number of this module.
ModuleVersion = '4.1.0'
ModuleVersion = '4.2.0'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down