Skip to content
4 changes: 2 additions & 2 deletions ServiceNow/Private/Invoke-ServiceNowRestMethod.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ function Invoke-ServiceNowRestMethod {
[hashtable] $Values,

[parameter()]
[System.Collections.ArrayList] $Filter,
[object[]] $Filter,

[parameter()]
[System.Collections.ArrayList] $Sort = @('opened_at', 'desc'),
[object[]] $Sort = @('opened_at', 'desc'),

# sysparm_query param in the format of a ServiceNow encoded query string (see http://wiki.servicenow.com/index.php?title=Encoded_Query_Strings)
[Parameter()]
Expand Down
108 changes: 108 additions & 0 deletions ServiceNow/Private/Invoke-TableIdLookup.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<#
.SYNOPSIS
Lookup table and id info
.DESCRIPTION
Lookup table and id info from module config.
Get sys_id if needed.
#>
function Invoke-TableIdLookup {

[OutputType([Array])]
[CmdletBinding()]

Param (
[Parameter(ParameterSetName = 'Table', Mandatory)]
[Parameter(ParameterSetName = 'TableID', Mandatory)]
[Parameter(ParameterSetName = 'TableIdSysId', Mandatory)]
[AllowEmptyString()]
[AllowNull()]
[Alias('T')]
[string] $Table,

[Parameter(ParameterSetName = 'ID', Mandatory)]
[Parameter(ParameterSetName = 'TableID', Mandatory)]
[Parameter(ParameterSetName = 'IdSysId', Mandatory)]
[Parameter(ParameterSetName = 'TableIdSysId', Mandatory)]
[AllowEmptyString()]
[AllowNull()]
[Alias('I')]
[string] $ID,

[Parameter(ParameterSetName = 'IdSysId', Mandatory)]
[Parameter(ParameterSetName = 'TableIdSysId', Mandatory)]
[Alias('AS')]
[switch] $AsSysId,

[Parameter(ParameterSetName = 'IdSysId')]
[Parameter(ParameterSetName = 'TableIdSysId')]
[Alias('C')]
[hashtable] $Connection,

[Parameter(ParameterSetName = 'IdSysId')]
[Parameter(ParameterSetName = 'TableIdSysId')]
[Alias('S')]
[hashtable] $ServiceNowSession

)

$thisTable = $thisID = $null

if ( $Table ) {
$thisTable = $script:ServiceNowTable | Where-Object { $_.Name.ToLower() -eq $Table.ToLower() -or $_.ClassName.ToLower() -eq $Table.ToLower() }
if ( -not $thisTable ) {
# we aren't aware of this table, create default config
$thisTable = @{
Name = $Table
ClassName = $null
Type = $null
NumberPrefix = $null
DescriptionField = $null
}
}
}

if ( $ID ) {
if ( $ID -match '^[a-zA-Z0-9]{32}$' ) {
if ( -not $thisTable ) {
throw 'Providing sys_id for -ID requires a value for -Table. Alternatively, provide an ID with a prefix, eg. INC1234567, and the table will be automatically determined.'
}

$thisID = $ID
}
else {
if ( -not $thisTable ) {
# get table name from prefix if only Id was provided
$idPrefix = ($ID | Select-String -Pattern '^([a-zA-Z]+)([0-9]+$)').Matches.Groups[1].Value.ToLower()

$thisTable = $script:ServiceNowTable | Where-Object { $_.NumberPrefix -and $idPrefix -eq $_.NumberPrefix }
if ( -not $thisTable ) {
throw ('The prefix for Id ''{0}'' was not found and the appropriate table cannot be determined. Known prefixes are {1}. Please provide a value for -Table.' -f $ID, ($ServiceNowTable.NumberPrefix.Where( { $_ }) -join ', '))
}
}

if ( $AsSysId ) {
$getParams = @{
Table = $thisTable.Name
Filter = @('number', '-eq', $ID)
Property = 'sys_class_name', 'sys_id', 'number'
Connection = $Connection
ServiceNowSession = $ServiceNowSession
}

$thisRecord = Invoke-ServiceNowRestMethod @getParams

if ( -not $thisRecord ) {
throw ('Table: {0}, ID: {1} not found' -f $thisTable.Name, $ID)
}
else {
$thisID = $thisRecord.sys_id
}
}
else {
$thisID = $ID
}
}
}

$thisTable, $thisID
}
93 changes: 29 additions & 64 deletions ServiceNow/Public/Add-ServiceNowAttachment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ Function Add-ServiceNowAttachment {

Upload one or more files by record sys_id

.EXAMPLE
Get-ServiceNowRecord inc0000010 | Add-ServiceNowAttachment -File '.\File01.txt'

Use Get-ServiceNowRecord for record details, one or more, to add an attachment to

.EXAMPLE
New-ServiceNowIncident @params -PassThru | Add-ServiceNowAttachment -File file01.txt

Expand Down Expand Up @@ -69,26 +74,23 @@ Function Add-ServiceNowAttachment {
[CmdletBinding(SupportsShouldProcess)]

Param(
[Parameter(ValueFromPipelineByPropertyName)]
[Parameter(ParameterSetName = 'Table', Mandatory)]
[Parameter(ParameterSetName = 'TableId', Mandatory, ValueFromPipelineByPropertyName)]
[Alias('sys_class_name')]
[string] $Table,

[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateScript( {
if ($_ -match '^[a-zA-Z0-9]{32}$' -or $_ -match '^([a-zA-Z]+)[0-9]+$') {
$true
} else {
throw 'Id must be either a 32 character alphanumeric, ServiceNow sysid, or prefix/id, ServiceNow number.'
}
})]
# validation not needed as Invoke-TableIdLookup will handle it with -AsSysId
[Parameter(ParameterSetName = 'Id', Mandatory, ValueFromPipeline, Position = 0)]
[Parameter(ParameterSetName = 'TableId', Mandatory, ValueFromPipelineByPropertyName)]
[Alias('sys_id', 'SysId', 'number')]
[string] $ID,

[Parameter(Mandatory)]
[ValidateScript( {
if ( Test-Path $_ ) {
$true
} else {
}
else {
throw 'One or more files do not exist'
}
})]
Expand All @@ -111,62 +113,24 @@ Function Add-ServiceNowAttachment {

begin {
$auth = Get-ServiceNowAuth -C $Connection -S $ServiceNowSession
$invokeRestMethodSplat = $auth
$invokeRestMethodSplat.UseBasicParsing = $true
$invokeRestMethodSplat.Method = 'POST'
$params = $auth
$params.UseBasicParsing = $true
$params.Method = 'POST'
}

process {

if ( $Table ) {
$thisTableName = $ServiceNowTable.Where{ $_.ClassName -eq $Table } | Select-Object -ExpandProperty Name
if ( -not $thisTableName ) {
$thisTableName = $Table
}
}

if ( $ID -match '^[a-zA-Z0-9]{32}$' ) {
if ( -not $thisTableName ) {
Write-Error 'Providing sys_id for -Id requires a value for -Table. Alternatively, provide an -Id with a prefix, eg. INC1234567, and the table will be automatically determined.'
Continue
}

$thisSysId = $ID

} else {
if ( -not $thisTableName ) {
$thisTable = $ServiceNowTable.Where{ $_.NumberPrefix -and $ID.ToLower().StartsWith($_.NumberPrefix) }
if ( $thisTable ) {
$thisTableName = $thisTable.Name
} else {
Write-Error ('The prefix for Id ''{0}'' was not found and the appropriate table cannot be determined. Known prefixes are {1}. Please provide a value for -Table.' -f $ID, ($ServiceNowTable.NumberPrefix.Where( { $_ }) -join ', '))
Continue
}
}

$getParams = @{
Table = $thisTableName
Id = $ID
Property = 'sys_id'
Connection = $Connection
ServiceNowSession = $ServiceNowSession
}

$tableRecord = Get-ServiceNowRecord @getParams

if ( -not $tableRecord ) {
Write-Error "Record not found for Id '$ID'"
continue
}

$thisSysId = $tableRecord.sys_id
}

$thisTable, $thisID = Invoke-TableIdLookup -T $Table -I $ID -AsSysId -C $Connection -S $ServiceNowSession

foreach ($thisFile in $File) {

$thisFileObject = Get-ChildItem $thisFile

if ( $thisFileObject.Size -eq 0 ) {
Write-Warning ('{0} is a 0 byte file and will not be uploaded' -f $thisFileObject.FullName)
Continue
}

If ( -not $PSBoundParameters.ContainsKey('ContentType') ) {
# Thanks to https://github.com/samuelneff/MimeTypeMap/blob/master/MimeTypeMap.cs from which
# MimeTypeMap.json was adapted
Expand All @@ -182,22 +146,23 @@ Function Add-ServiceNowAttachment {
}

# POST: https://instance.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=d71f7935c0a8016700802b64c67c11c6&file_name=Issue_screenshot
$invokeRestMethodSplat.Uri = '{0}/attachment/file?table_name={1}&table_sys_id={2}&file_name={3}' -f $auth.Uri, $thisTableName, $thisSysId, $thisFileObject.Name
$invokeRestMethodSplat.ContentType = $ContentType
$invokeRestMethodSplat.InFile = $thisFileObject.FullName
$params.Uri = '{0}/attachment/file?table_name={1}&table_sys_id={2}&file_name={3}' -f $auth.Uri, $thisTable.Name, $thisID, $thisFileObject.Name
$params.ContentType = $ContentType
$params.InFile = $thisFileObject.FullName

If ($PSCmdlet.ShouldProcess(('{0} {1}' -f $thisTableName, $thisSysId), ('Add attachment {0}' -f $thisFileObject.FullName))) {
If ($PSCmdlet.ShouldProcess(('{0} {1}' -f $thisTable.Name, $ID), ('Add attachment {0}' -f $thisFileObject.FullName))) {

Write-Verbose ($invokeRestMethodSplat | ConvertTo-Json)
Write-Verbose ($params | ConvertTo-Json)

$response = Invoke-WebRequest @invokeRestMethodSplat
$response = Invoke-WebRequest @params

if ( $response.Content ) {
if ( $PassThru ) {
$content = $response.content | ConvertFrom-Json
$content.result
}
} else {
}
else {
# invoke-webrequest didn't throw an error, but we didn't get content back either
throw ('"{0} : {1}' -f $response.StatusCode, $response | Out-String )
}
Expand Down
50 changes: 31 additions & 19 deletions ServiceNow/Public/Export-ServiceNowAttachment.ps1
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<#
.SYNOPSIS
Save a ServiceNow attachment identified by its sys_id property and saved as the filename specified.
Export an attachment

.DESCRIPTION
Save a ServiceNow attachment identified by its sys_id property and saved as the filename specified.
Export an attachment identified by its attachment table sys_id.
The contents will be saved to a file by default, but can also be outputted directly.

.PARAMETER SysID
The ServiceNow sys_id of the file
.PARAMETER ID
The attachment table sys_id of the file

.PARAMETER FileName
File name the file is saved as. Do not include the path.
Expand All @@ -24,27 +25,30 @@ Adds the SysID to the file name. Intended for use when a ticket has multiple fi
Instead of writing to a file, return the attachment contents

.EXAMPLE
Export-ServiceNowAttachment -SysID $SysID -FileName 'mynewfile.txt'
Export-ServiceNowAttachment -ID $SysID -FileName 'mynewfile.txt'

Save the attachment with the specified sys_id with a name of 'mynewfile.txt'

.EXAMPLE
Get-ServiceNowAttachment -Id INC1234567 | Export-ServiceNowAttachment
Get-ServiceNowAttachment -ID INC1234567 | Export-ServiceNowAttachment

Save all attachments from the ticket. Filenames will be assigned from the attachment name.
Save all attachments from the ticket.
Filenames will be assigned from the attachment name.

.EXAMPLE
Get-ServiceNowAttachment -Id INC1234567 | Export-ServiceNowAttachment -AppendNameWithSysID
Get-ServiceNowAttachment -ID INC1234567 | Export-ServiceNowAttachment -AppendNameWithSysID

Save all attachments from the ticket. Filenames will be assigned from the attachment name and appended with the sys_id.
Save all attachments from the ticket.
Filenames will be assigned from the attachment name and appended with the sys_id.

.EXAMPLE
Get-ServiceNowAttachment -Id INC1234567 | Export-ServiceNowAttachment -Destination $path -AllowOverwrite
Get-ServiceNowAttachment -ID INC1234567 | Export-ServiceNowAttachment -Destination $path -AllowOverwrite

Save all attachments from the ticket to the destination allowing for overwriting the destination file.

.EXAMPLE
Export-ServiceNowAttachment -SysId $SysId -AsValue
Export-ServiceNowAttachment -ID $ID -AsValue

Return the contents of the attachment instead of writing to a file

#>
Expand All @@ -55,8 +59,16 @@ Function Export-ServiceNowAttachment {
Param(

[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('sys_id')]
[string] $SysId,
[ValidateScript( {
if ( $_ -match '^[a-zA-Z0-9]{32}$' ) {
$true
}
else {
throw '-ID must be a sys_id 32 character alphanumeric'
}
})]
[Alias('sys_id', 'SysID')]
[string] $ID,

[Parameter(ParameterSetName = 'ToFile', ValueFromPipelineByPropertyName)]
[Alias('file_name')]
Expand Down Expand Up @@ -95,25 +107,25 @@ Function Export-ServiceNowAttachment {

$params = $authParams.Clone()

$params.Uri += '/attachment/' + $SysId + '/file'
$params.Uri += '/attachment/' + $ID + '/file'

# if not to file, attachment contents to output
if ( $PSCmdlet.ParameterSetName -eq 'ToFile' ) {
$thisFileName = $FileName
If ( $AppendNameWithSysId.IsPresent ) {
$thisFileName = "{0}_{1}{2}" -f [io.path]::GetFileNameWithoutExtension($thisFileName), $SysId, [io.path]::GetExtension($thisFileName)
If ( $AppendNameWithSysId ) {
$thisFileName = "{0}_{1}{2}" -f [io.path]::GetFileNameWithoutExtension($thisFileName), $ID, [io.path]::GetExtension($thisFileName)
}
$outFile = Join-Path $Destination $thisFileName

If ((Test-Path $outFile) -and -not $AllowOverwrite.IsPresent) {
If ((Test-Path $outFile) -and -not $AllowOverwrite ) {
throw ('The file ''{0}'' already exists. Please choose a different name, use the -AppendNameWithSysID switch parameter, or use the -AllowOverwrite switch parameter to overwrite the file.' -f $OutFile)
}

$params.OutFile = $outFile
}

If ($PSCmdlet.ShouldProcess($outFile, "Save attachment")) {
If ($PSCmdlet.ShouldProcess($ID, "Export attachment")) {
Invoke-RestMethod @params
}
}
end {}
}
Loading