From 19ea23be4176b26da1dea52b54c8f84f37353fc3 Mon Sep 17 00:00:00 2001 From: Greg Brownstein Date: Tue, 13 Jan 2026 21:12:35 -0500 Subject: [PATCH 1/5] cart lifecycle --- ServiceNow/Private/Test-ServiceNowSysId.ps1 | 38 +++++ ServiceNow/Public/Get-ServiceNowCart.ps1 | 23 +++ ServiceNow/Public/New-ServiceNowCartItem.ps1 | 154 ++++++++++++++++++ .../Public/New-ServiceNowCatalogItem.ps1 | 105 ------------ .../Public/Remove-ServiceNowCartItem.ps1 | 103 ++++++++++++ ServiceNow/Public/Submit-ServiceNowCart.ps1 | 64 ++++++++ .../Public/Submit-ServiceNowCatalogOrder.ps1 | 63 ------- ServiceNow/ServiceNow.psm1 | 30 ++++ 8 files changed, 412 insertions(+), 168 deletions(-) create mode 100644 ServiceNow/Private/Test-ServiceNowSysId.ps1 create mode 100644 ServiceNow/Public/Get-ServiceNowCart.ps1 create mode 100644 ServiceNow/Public/New-ServiceNowCartItem.ps1 delete mode 100644 ServiceNow/Public/New-ServiceNowCatalogItem.ps1 create mode 100644 ServiceNow/Public/Remove-ServiceNowCartItem.ps1 create mode 100644 ServiceNow/Public/Submit-ServiceNowCart.ps1 delete mode 100644 ServiceNow/Public/Submit-ServiceNowCatalogOrder.ps1 diff --git a/ServiceNow/Private/Test-ServiceNowSysId.ps1 b/ServiceNow/Private/Test-ServiceNowSysId.ps1 new file mode 100644 index 0000000..7ed015f --- /dev/null +++ b/ServiceNow/Private/Test-ServiceNowSysId.ps1 @@ -0,0 +1,38 @@ +<# +.SYNOPSIS + Tests if a string is a valid ServiceNow sys_id format + +.DESCRIPTION + Validates that a string matches the ServiceNow sys_id format of a 32-character alphanumeric string + +.PARAMETER Value + The string value to test + +.EXAMPLE + Test-ServiceNowSysId -Value '9d385017c611228701d22104cc95c371' + Returns $true + +.EXAMPLE + Test-ServiceNowSysId -Value 'INC0010001' + Returns $false + +.OUTPUTS + System.Boolean +#> +function Test-ServiceNowSysId { + [CmdletBinding()] + [OutputType([System.Boolean])] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [AllowEmptyString()] + [string] $Value + ) + + process { + if ([string]::IsNullOrEmpty($Value)) { + return $false + } + + return $Value -match '^[a-zA-Z0-9]{32}$' + } +} diff --git a/ServiceNow/Public/Get-ServiceNowCart.ps1 b/ServiceNow/Public/Get-ServiceNowCart.ps1 new file mode 100644 index 0000000..59cd99d --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowCart.ps1 @@ -0,0 +1,23 @@ +<# +.SYNOPSIS + Get the cart of the current user + +.DESCRIPTION + Get the cart of the current user + +#> +function Get-ServiceNowCart { + param + ( + [Parameter()] + [hashtable]$ServiceNowSession = $script:ServiceNowSession + ) + + $params = @{ + Method = 'Get' + UriLeaf = "/servicecatalog/cart" + Namespace = 'sn_sc' + ServiceNowSession = $ServiceNowSession + } + Invoke-ServiceNowRestMethod @params | Select-Object @{n = 'sys_id'; e = { $_.cart_id } }, * -ExcludeProperty cart_id +} \ No newline at end of file diff --git a/ServiceNow/Public/New-ServiceNowCartItem.ps1 b/ServiceNow/Public/New-ServiceNowCartItem.ps1 new file mode 100644 index 0000000..ea955ed --- /dev/null +++ b/ServiceNow/Public/New-ServiceNowCartItem.ps1 @@ -0,0 +1,154 @@ +<# +.SYNOPSIS + Add item to the cart of the current user + +.DESCRIPTION + Adds a catalog item to the cart of the current user and optionally checks out the cart to create the order. + +.PARAMETER CatalogItem + Name or ID of the catalog item that will be created. + Use Tab or Menu Completion to see available catalog items. + +.PARAMETER Quantity + Quantity of the catalog item to request. Default is 1. + +.PARAMETER ItemValues + Key/value pairs of variable names and their values + +.PARAMETER Checkout + Checkout the cart after adding the item to create the order. + If not checking out, you can use Submit-ServiceNowCatalogOrder to checkout later. + +.PARAMETER PassThru + If provided, the new record, either cart addition or checked out order, will be returned + +.PARAMETER ServiceNowSession + ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession. + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem "Standard Laptop" + + Add 1 of the catalog item to the cart without checking out + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem "Standard Laptop" -Quantity 3 + + Add 3 of the catalog item to the cart without checking out + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem "Standard Laptop" -Quantity 3 -Checkout + + Add 3 of the catalog item to the cart and checkout to create the order + +.EXAMPLE + 'Standard Laptop', 'Adobe Acrobat Pro' | New-ServiceNowCartItem + + Add multiple catalog items to the cart + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem "04b7e94b4f7b42000086eeed18110c7fd" -ItemValues @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' } + + Raise a new catalog request using Item ID + +.INPUTS + CatalogItem + +.OUTPUTS + PSCustomObject if PassThru provided +#> +function New-ServiceNowCartItem { + [CmdletBinding(SupportsShouldProcess)] + param + ( + [Parameter(Mandatory, ValueFromPipeline)] + [string] $CatalogItem, + + [Parameter()] + [ValidateRange(1, [int32]::MaxValue)] + [int32] $Quantity = 1, + + [Parameter()] + [hashtable]$ItemValues, + + [Parameter()] + [switch]$Checkout, + + [Parameter()] + [switch]$PassThru, + + [Parameter()] + [hashtable]$ServiceNowSession = $script:ServiceNowSession + ) + + process { + if ($CatalogItem -match '^[a-zA-Z0-9]{32}$') { + #Verify the sys_id of the Catalog Item + $catalogItemID = Get-ServiceNowRecord -Table sc_cat_item -AsValue -ID $CatalogItem -Property sys_id + } + else { + #Lookup the sys_id of the Catalog Item + $catalogItemID = Get-ServiceNowRecord -Table sc_cat_item -AsValue -Filter @('name', '-eq', $CatalogItem ) -Property sys_id + } + + if ([string]::IsNullOrEmpty($catalogItemID)) { + throw "Unable to find catalog item '$CatalogItem'" + } + else { + Write-Verbose "Found $catalogItemID via lookup from '$CatalogItem'" + } + + $addItemToCart = @{ + Method = 'Post' + UriLeaf = "/servicecatalog/items/{0}/add_to_cart" -f $catalogItemID + Values = @{ + 'sysparm_quantity' = $Quantity + } + Namespace = 'sn_sc' + ServiceNowSession = $ServiceNowSession + } + + if ( $ItemValues ) { + $addItemToCart.Values.variables = $ItemValues + } + + if ( $PSCmdlet.ShouldProcess($catalogItemID, 'Create new catalog item request') ) { + + try { + $addItemCartResponse = Invoke-ServiceNowRestMethod @addItemToCart + } + catch { + if ( ($_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -ExpandProperty error | Select-Object -ExpandProperty message) -match 'Mandatory Variables are required' ) { + $catItem = Invoke-ServiceNowRestMethod -UriLeaf "/servicecatalog/items/$catalogItemID" -Namespace 'sn_sc' + $mandatoryVars = $catItem.variables | Where-Object { $_.mandatory -eq $true } | Select-Object -ExpandProperty name + throw ('Failed to add item to cart. The following mandatory variables must be provided: {0}' -f ($mandatoryVars -join ', ')) + } + else { + throw $_ + } + } + + if ( -not $addItemCartResponse.cart_id ) { + throw ('Failed to add item to cart. Response: {0}' -f ($addItemCartResponse | ConvertTo-Json)) + } + else { + Write-Verbose "Added item to cart with Cart ID: $($addItemCartResponse.cart_id)" + $out = $addItemCartResponse + } + + Write-Verbose ("Current cart items:`n{0}" -f ($addItemCartResponse.items | Select-Object item_name, quantity, price | Out-String)) + Write-Verbose ('Cart Total: {0}' -f $addItemCartResponse.subtotal) + } + } + + end { + if ( $Checkout ) { + $submitResponse = Submit-ServiceNowCart -ServiceNowSession $ServiceNowSession -PassThru + Write-Verbose 'Order submitted successfully.' + $out = $submitResponse + } + + if ( $PassThru ) { + $out + } + } +} \ No newline at end of file diff --git a/ServiceNow/Public/New-ServiceNowCatalogItem.ps1 b/ServiceNow/Public/New-ServiceNowCatalogItem.ps1 deleted file mode 100644 index 94c90c8..0000000 --- a/ServiceNow/Public/New-ServiceNowCatalogItem.ps1 +++ /dev/null @@ -1,105 +0,0 @@ -<# -.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 CatalogItem - Name or ID of the catalog item that will be created - -.PARAMETER Variables - Key/value pairs of variable names and their values - -.PARAMETER CheckoutImmediately - If provided, a second Post for cart checkout to submit_order API Endpoint will be sent - -.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-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-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)] - param - ( - [Parameter(Mandatory)] - [string]$CatalogItem, - [Parameter(Mandatory)] - [Alias('Variables')] - [hashtable]$InputData, - [Parameter()][Hashtable]$Connection, - [Parameter()][hashtable]$ServiceNowSession = $script:ServiceNowSession, - [Parameter()][switch]$CheckoutImmediately, - [Parameter()][switch]$PassThru - ) - - begin { - if (-not $PSBoundParameters.ContainsKey('CheckoutImmediately')) { - $CheckoutImmediately = $false - } - if ($CatalogItem -match '^[a-zA-Z0-9]{32}$') { - #Verify the sys_id of the Catalog Item - $CatalogItemID = Get-ServiceNowRecord -Table sc_cat_item -AsValue -ID $CatalogItem -Property sys_id - if ([string]::IsNullOrEmpty($CatalogItemID)) { throw "Unable to find catalog item by ID '$($CatalogItem)'" } else { Write-Verbose "Found $($catalogitemid) via lookup from '$($CatalogItem)'" } - } else { - #Lookup the sys_id of the Catalog Item - $CatalogItemID = Get-ServiceNowRecord -Table sc_cat_item -AsValue -Filter @('name', '-eq', $CatalogItem ) -Property sys_id - if ([string]::IsNullOrEmpty($CatalogItemID)) { throw "Unable to find catalog item by name '$($CatalogItem)'" } else { Write-Verbose "Found $($catalogitemid) via lookup from '$($CatalogItem)'" } - } - } - process { - - $AddItemToCart = @{ - Method = 'Post' - UriLeaf = "/servicecatalog/items/{0}/add_to_cart" -f $CatalogItemID - Values = @{'sysparm_quantity' = 1; 'variables' = $InputData } - Namespace = 'sn_sc' - Connection = $Connection - ServiceNowSession = $ServiceNowSession - } - - if ( $PSCmdlet.ShouldProcess($CatalogItemID, 'Create new catalog item request') ) { - - $AddItemCartResponse = Invoke-ServiceNowRestMethod @AddItemToCart - - if ($AddItemCartResponse.cart_id -and $CheckoutImmediately) { - $SubmitOrder = @{ - Method = 'Post' - UriLeaf = "/servicecatalog/cart/submit_order" - Namespace = 'sn_sc' - Connection = $Connection - ServiceNowSession = $ServiceNowSession - } - - $SubmitOrderResponse = Invoke-ServiceNowRestMethod @SubmitOrder - - if ($PassThru) { - $SubmitOrderResponse | Select-Object @{'n' = 'number'; 'e' = { $_.request_number } }, request_id - } - } - } else { - $AddItemToCart | Out-String - } - } -} \ No newline at end of file diff --git a/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 b/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 new file mode 100644 index 0000000..fed44da --- /dev/null +++ b/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 @@ -0,0 +1,103 @@ +<# +.SYNOPSIS + Remove cart items + +.DESCRIPTION + Removes a specific catalog item from the cart or empties the entire cart. + +.PARAMETER CartItemId + SysId of the cart item to be removed from the current user. + Must be a 32 character alphanumeric string. + +.PARAMETER All + Remove all items from the cart (empty the cart). + +.PARAMETER CartId + SysId of the cart to be emptied. If not provided, the current user's cart will be looked up. + Must be a 32 character alphanumeric string. + +.PARAMETER ServiceNowSession + ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession. + +.EXAMPLE + Remove-ServiceNowCartItem -CartItemId "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" + + Remove a specific item from the cart + +.EXAMPLE + Remove-ServiceNowCartItem -All + + Remove all items from the current user's cart + +.EXAMPLE + Remove-ServiceNowCartItem -All -CartId "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" + + Remove all items from the specified cart + +.INPUTS + CartItemId + +.OUTPUTS + None +#> +function Remove-ServiceNowCartItem { + [CmdletBinding(SupportsShouldProcess)] + param + ( + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByCatalogItemId')] + [ValidateScript( { + if ($_ -match '^[a-zA-Z0-9]{32}$') { + $true + } + else { + throw 'CartItemId must be a SysId 32 character alphanumeric' + } + })] + [string] $CartItemId, + + [Parameter(Mandatory, ParameterSetName = 'All')] + [switch] $All, + + [Parameter(ParameterSetName = 'All')] + [ValidateScript( { + if ($_ -match '^[a-zA-Z0-9]{32}$') { + $true + } + else { + throw 'CartId must be a SysId 32 character alphanumeric' + } + })] + [string] $CartId, + + [Parameter()] + [hashtable]$ServiceNowSession = $script:ServiceNowSession + ) + + process { + + $params = @{ + Method = 'Delete' + Namespace = 'sn_sc' + ServiceNowSession = $ServiceNowSession + } + + if ( $CartItemId ) { + $params.UriLeaf = "/servicecatalog/cart/{0}" -f $CartItemId + } + else { + # remove a cart and its items + # we need the cart id so if we don't have it, look up the current user's cart + if ( -not $CartId ) { + $cart = Get-ServiceNowCart -ServiceNowSession $ServiceNowSession + $cartId = $cart.sys_id + } + Write-Verbose "Emptying cart with sys_id $cartId" + $params.UriLeaf = "/servicecatalog/cart/$cartId/empty" + } + + $target = if ($CartItemId) { "Remove item $CartItemId from current cart" } else { "Remove cart $cartId completely" } + if ( $PSCmdlet.ShouldProcess($target) ) { + Invoke-ServiceNowRestMethod @params + } + } +} \ No newline at end of file diff --git a/ServiceNow/Public/Submit-ServiceNowCart.ps1 b/ServiceNow/Public/Submit-ServiceNowCart.ps1 new file mode 100644 index 0000000..46091e4 --- /dev/null +++ b/ServiceNow/Public/Submit-ServiceNowCart.ps1 @@ -0,0 +1,64 @@ +<# +.SYNOPSIS + Submit the user cart for checkout + +.DESCRIPTION + Checks out the user cart, based on the current check-out type (one-step or two-step) + +.PARAMETER PassThru + If provided, the new record id will be returned + +.PARAMETER ServiceNowSession + ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession. + +.EXAMPLE + Submit-ServiceNowCart + + Checks out the user cart, based on the current check-out type (one-step or two-step). + +.EXAMPLE + Submit-ServiceNowCart -PassThru + + Checks out the user cart, based on the current check-out type (one-step or two-step) and returns the request number and request ID. + +.LINK + https://developer.servicenow.com/dev.do#!/reference/api/zurich/rest/c_ServiceCatalogAPI#servicecat-POST-cart-sub_order?navFilter=serv + +.OUTPUTS + PSCustomObject if PassThru provided +#> +function Submit-ServiceNowCart { + [CmdletBinding(SupportsShouldProcess)] + param + ( + [Parameter()] + [switch]$PassThru, + + [Parameter()] + [hashtable]$ServiceNowSession = $script:ServiceNowSession + ) + + process { + + if ( $PSCmdlet.ShouldProcess('Checkout current user cart') ) { + $submitOrder = @{ + Method = 'Post' + UriLeaf = "/servicecatalog/cart/submit_order" + Namespace = 'sn_sc' + ServiceNowSession = $ServiceNowSession + } + + $submitOrderResponse = Invoke-ServiceNowRestMethod @submitOrder + + if ($PassThru) { + if ( $submitOrderResponse.request_number ) { + $submitOrderResponse | Select-Object @{'n' = 'number'; 'e' = { $_.request_number } }, request_id + } + else { + $submitOrderResponse + } + } + + } + } +} \ No newline at end of file diff --git a/ServiceNow/Public/Submit-ServiceNowCatalogOrder.ps1 b/ServiceNow/Public/Submit-ServiceNowCatalogOrder.ps1 deleted file mode 100644 index bf45b33..0000000 --- a/ServiceNow/Public/Submit-ServiceNowCatalogOrder.ps1 +++ /dev/null @@ -1,63 +0,0 @@ -<# -.SYNOPSIS - Submit a catalog request using Service Catalog API - -.DESCRIPTION - Checks out the user cart, based on the current check-out type (one-step or two-step). Reference: https://developer.servicenow.com/dev.do#!/reference/api/zurich/rest/c_ServiceCatalogAPI#servicecat-POST-cart-sub_order?navFilter=serv - -.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 - Submit-ServiceNowCatalogOrder - - Checks out the user cart, based on the current check-out type (one-step or two-step). - -.EXAMPLE - Submit-ServiceNowCatalogOrder -PassThru - - Checks out the user cart, based on the current check-out type (one-step or two-step) and returns the request numbers as an object. - -.INPUTS - InputData - -.OUTPUTS - PSCustomObject if PassThru provided -#> -function Submit-ServiceNowCatalogOrder { - [CmdletBinding(SupportsShouldProcess)] - param - ( - [Parameter()][Hashtable]$Connection, - [Parameter()][hashtable]$ServiceNowSession = $script:ServiceNowSession, - [Parameter()][switch]$PassThru - ) - - process { - - if ( $PSCmdlet.ShouldProcess('POST cart to Submit_Order API') ) { - $SubmitOrder = @{ - Method = 'Post' - UriLeaf = "/servicecatalog/cart/submit_order" - Namespace = 'sn_sc' - Connection = $Connection - ServiceNowSession = $ServiceNowSession - } - - $SubmitOrderResponse = Invoke-ServiceNowRestMethod @SubmitOrder - - if ($PassThru) { - $SubmitOrderResponse | Select-Object @{'n' = 'number'; 'e' = { $_.request_number } }, request_id - } - - } else { - Write-Output "Checks out the user cart, based on the current check-out type (one-step or two-step).`n`nIf one-step checkout, the method checks out (saves) the cart and returns the request number and the request order ID. If two-step checkout, the method returns the cart order status and all the information required for two-step checkout." - } - } -} \ No newline at end of file diff --git a/ServiceNow/ServiceNow.psm1 b/ServiceNow/ServiceNow.psm1 index 97fe40b..3cbf1fe 100644 --- a/ServiceNow/ServiceNow.psm1 +++ b/ServiceNow/ServiceNow.psm1 @@ -9,6 +9,36 @@ $Script:ServiceNowOperator = $config.FilterOperators Export-ModuleMember -Variable ServiceNowOperator, ServiceNowTable +$script:catalogItems = [System.Collections.Generic.List[object]]::new() + +$tableLookupArgCompleterSb = { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + + switch ($parameterName) { + 'CatalogItem' { + if ( $script:catalogItems.Count -eq 0 ) { + $allItems = Get-ServiceNowRecord -Table sc_cat_item -Property sys_id, name, short_description -IncludeTotalCount -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -ServiceNowSession $script:ServiceNowSession + $script:catalogItems.AddRange($allItems) + } + + $out = $script:catalogItems + if ( $wordToComplete ) { + $out = $script:catalogItems | Where-Object { + ($_.sys_id -like ('{0}*' -f $wordToComplete.Trim("'"))) -or + ($_.name -like ('{0}*' -f $wordToComplete.Trim("'"))) + } + } + $out | ForEach-Object { + $itemText = "'{0}'" -f $_.name + $itemDescription = if ($_.short_description) { $_.short_description } else { ' ' } + [System.Management.Automation.CompletionResult]::new($itemText, $_.name, 'ParameterValue', $itemDescription) + } + } + } +} + +Register-ArgumentCompleter -CommandName 'New-ServiceNowCatalogItem' -ParameterName 'CatalogItem' -ScriptBlock $tableLookupArgCompleterSb + $tableArgCompleterSb = { $ServiceNowTable | ForEach-Object { if ( $_.ClassName ) { From 218189deec9d48086770a99a5a580297c4ffd829 Mon Sep 17 00:00:00 2001 From: Greg Brownstein Date: Wed, 14 Jan 2026 08:23:36 -0500 Subject: [PATCH 2/5] help updates --- ServiceNow/Private/Test-ServiceNowSysId.ps1 | 6 +++--- ServiceNow/Public/Get-ServiceNowCart.ps1 | 11 ++++++++-- ServiceNow/Public/New-ServiceNowCartItem.ps1 | 21 ++++++++++++++++--- .../Public/Remove-ServiceNowCartItem.ps1 | 4 ++-- ServiceNow/ServiceNow.psd1 | 4 +++- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/ServiceNow/Private/Test-ServiceNowSysId.ps1 b/ServiceNow/Private/Test-ServiceNowSysId.ps1 index 7ed015f..df8206a 100644 --- a/ServiceNow/Private/Test-ServiceNowSysId.ps1 +++ b/ServiceNow/Private/Test-ServiceNowSysId.ps1 @@ -25,14 +25,14 @@ function Test-ServiceNowSysId { param ( [Parameter(Mandatory, ValueFromPipeline)] [AllowEmptyString()] - [string] $Value + [string] $InputObject ) process { - if ([string]::IsNullOrEmpty($Value)) { + if ([string]::IsNullOrEmpty($InputObject)) { return $false } - return $Value -match '^[a-zA-Z0-9]{32}$' + return $InputObject -match '^[a-zA-Z0-9]{32}$' } } diff --git a/ServiceNow/Public/Get-ServiceNowCart.ps1 b/ServiceNow/Public/Get-ServiceNowCart.ps1 index 59cd99d..a99584a 100644 --- a/ServiceNow/Public/Get-ServiceNowCart.ps1 +++ b/ServiceNow/Public/Get-ServiceNowCart.ps1 @@ -1,10 +1,17 @@ <# .SYNOPSIS Get the cart of the current user - .DESCRIPTION Get the cart of the current user - +.PARAMETER ServiceNowSession + ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession. +.EXAMPLE + Get-ServiceNowCart + Get the cart of the current user +.INPUTS + None +.OUTPUTS + PSCustomObject representing the cart #> function Get-ServiceNowCart { param diff --git a/ServiceNow/Public/New-ServiceNowCartItem.ps1 b/ServiceNow/Public/New-ServiceNowCartItem.ps1 index ea955ed..dbcecb8 100644 --- a/ServiceNow/Public/New-ServiceNowCartItem.ps1 +++ b/ServiceNow/Public/New-ServiceNowCartItem.ps1 @@ -46,9 +46,24 @@ Add multiple catalog items to the cart .EXAMPLE - New-ServiceNowCartItem -CatalogItem "04b7e94b4f7b42000086eeed18110c7fd" -ItemValues @{'acrobat' = 'true'; 'photoshop' = 'true'; ' Additional_software_requirements' = 'Testing Service catalog API' } + New-ServiceNowCartItem -CatalogItem "Standard Laptop" -PassThru - Raise a new catalog request using Item ID + Add a catalog item to the cart and return the cart details + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem "Standard Laptop" -PassThru -Checkout + + Add a catalog item to the cart, checkout to create the order, and return the order details + +.EXAMPLE + 'Packaging and Shipping' | New-ServiceNowCartItem -ItemValues @{'type'='Inter-office';'parcel_details'='fragile'} + + Add a catalog item to the cart with mandatory values + +.EXAMPLE + New-ServiceNowCartItem -CatalogItem 'ce40793b53d6ba10295d38e0a0490e86' + + Add a catalog item to the cart using its sys_id .INPUTS CatalogItem @@ -81,7 +96,7 @@ function New-ServiceNowCartItem { ) process { - if ($CatalogItem -match '^[a-zA-Z0-9]{32}$') { + if ($CatalogItem | Test-ServiceNowSysId ) { #Verify the sys_id of the Catalog Item $catalogItemID = Get-ServiceNowRecord -Table sc_cat_item -AsValue -ID $CatalogItem -Property sys_id } diff --git a/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 b/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 index fed44da..91558a3 100644 --- a/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 +++ b/ServiceNow/Public/Remove-ServiceNowCartItem.ps1 @@ -46,7 +46,7 @@ function Remove-ServiceNowCartItem { ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByCatalogItemId')] [ValidateScript( { - if ($_ -match '^[a-zA-Z0-9]{32}$') { + if ($_ | Test-ServiceNowSysId) { $true } else { @@ -60,7 +60,7 @@ function Remove-ServiceNowCartItem { [Parameter(ParameterSetName = 'All')] [ValidateScript( { - if ($_ -match '^[a-zA-Z0-9]{32}$') { + if ($_ | Test-ServiceNowSysId) { $true } else { diff --git a/ServiceNow/ServiceNow.psd1 b/ServiceNow/ServiceNow.psd1 index afd8a89..31e679a 100644 --- a/ServiceNow/ServiceNow.psd1 +++ b/ServiceNow/ServiceNow.psd1 @@ -76,7 +76,9 @@ FunctionsToExport = 'New-ServiceNowConfigurationItem', 'Get-ServiceNowRecord', 'New-ServiceNowQuery', 'New-ServiceNowRecord', 'Remove-ServiceNowAttachment', 'Remove-ServiceNowRecord', 'Update-ServiceNowRecord', 'Export-ServiceNowRecord', - 'Invoke-ServiceNowGraphQL', 'New-ServiceNowChangeTask' + 'Invoke-ServiceNowGraphQL', 'New-ServiceNowChangeTask', + 'Get-ServiceNowCart', 'New-ServiceNowCartItem', + 'Remove-ServiceNowCartItem', 'Submit-ServiceNowCart' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() From 23fcc7f91d458fb2c5169c5d3452239d8438e6f5 Mon Sep 17 00:00:00 2001 From: Greg Brownstein Date: Wed, 14 Jan 2026 08:38:55 -0500 Subject: [PATCH 3/5] fix function name --- ServiceNow/ServiceNow.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ServiceNow/ServiceNow.psm1 b/ServiceNow/ServiceNow.psm1 index 3cbf1fe..fffb197 100644 --- a/ServiceNow/ServiceNow.psm1 +++ b/ServiceNow/ServiceNow.psm1 @@ -37,7 +37,7 @@ $tableLookupArgCompleterSb = { } } -Register-ArgumentCompleter -CommandName 'New-ServiceNowCatalogItem' -ParameterName 'CatalogItem' -ScriptBlock $tableLookupArgCompleterSb +Register-ArgumentCompleter -CommandName 'New-ServiceNowCartItem' -ParameterName 'CatalogItem' -ScriptBlock $tableLookupArgCompleterSb $tableArgCompleterSb = { $ServiceNowTable | ForEach-Object { From 395fa3538d1f47e4909e51ece3b20932a4221c2f Mon Sep 17 00:00:00 2001 From: Greg Brownstein Date: Wed, 14 Jan 2026 08:57:28 -0500 Subject: [PATCH 4/5] help fix --- ServiceNow/Private/Test-ServiceNowSysId.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ServiceNow/Private/Test-ServiceNowSysId.ps1 b/ServiceNow/Private/Test-ServiceNowSysId.ps1 index df8206a..f9246ae 100644 --- a/ServiceNow/Private/Test-ServiceNowSysId.ps1 +++ b/ServiceNow/Private/Test-ServiceNowSysId.ps1 @@ -5,15 +5,15 @@ .DESCRIPTION Validates that a string matches the ServiceNow sys_id format of a 32-character alphanumeric string -.PARAMETER Value +.PARAMETER InputObject The string value to test .EXAMPLE - Test-ServiceNowSysId -Value '9d385017c611228701d22104cc95c371' + Test-ServiceNowSysId -InputObject '9d385017c611228701d22104cc95c371' Returns $true .EXAMPLE - Test-ServiceNowSysId -Value 'INC0010001' + Test-ServiceNowSysId -InputObject 'INC0010001' Returns $false .OUTPUTS From ce87bd9de93e031d2c4237e1d9c5746c61b2a0af Mon Sep 17 00:00:00 2001 From: Greg Brownstein Date: Wed, 14 Jan 2026 09:06:53 -0500 Subject: [PATCH 5/5] new cart item alias --- ServiceNow/Public/New-ServiceNowCartItem.ps1 | 2 ++ ServiceNow/ServiceNow.psd1 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ServiceNow/Public/New-ServiceNowCartItem.ps1 b/ServiceNow/Public/New-ServiceNowCartItem.ps1 index dbcecb8..25a883f 100644 --- a/ServiceNow/Public/New-ServiceNowCartItem.ps1 +++ b/ServiceNow/Public/New-ServiceNowCartItem.ps1 @@ -73,6 +73,8 @@ #> function New-ServiceNowCartItem { [CmdletBinding(SupportsShouldProcess)] + [Alias('Add-ServiceNowCartItem')] + param ( [Parameter(Mandatory, ValueFromPipeline)] diff --git a/ServiceNow/ServiceNow.psd1 b/ServiceNow/ServiceNow.psd1 index 31e679a..5f8252a 100644 --- a/ServiceNow/ServiceNow.psd1 +++ b/ServiceNow/ServiceNow.psd1 @@ -87,7 +87,7 @@ CmdletsToExport = @() VariablesToExport = 'ServiceNowSession', 'ServiceNowOperator', 'ServiceNowTable' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = 'gsnr' +AliasesToExport = 'gsnr', 'Add-ServiceNowCartItem' # DSC resources to export from this module # DscResourcesToExport = @()