Skip to content

Commit ccc346a

Browse files
authored
Merge pull request #79 from Sam-Martin/development
v1.6.0 - Add Attachment Functions
2 parents 7b2a48e + d3bc24f commit ccc346a

12 files changed

+1189
-34
lines changed

Build/psake.ps1

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ Properties {
77
$ProjectRoot = Resolve-Path "$PSScriptRoot\.."
88
}
99

10-
#
11-
$StepVersionBy = 'Build'
10+
$StepVersionBy = $null
1211

1312
$Timestamp = Get-date -uformat "%Y%m%d-%H%M%S"
1413
$PSVersion = $PSVersionTable.PSVersion.Major
@@ -60,7 +59,21 @@ Task Test -Depends UnitTests {
6059
$CodeFiles = Get-ChildItem $ENV:BHModulePath -Recurse -Include "*.psm1","*.ps1"
6160
$CodeCoverage = New-Object System.Collections.ArrayList
6261
$CodeCoverage.AddRange($CodeFiles.FullName)
63-
$Script:TestResults = Invoke-Pester -Path $ProjectRoot\Tests -CodeCoverage $CodeCoverage -PassThru -OutputFormat NUnitXml -OutputFile $TestFilePath
62+
$Credential = Get-Credential
63+
$invokePesterScript = @{
64+
Path = "$ProjectRoot\Tests"
65+
Parameters = @{
66+
Credential = $Credential
67+
}
68+
}
69+
$invokePesterSplat = @{
70+
Script = $invokePesterScript
71+
CodeCoverage = $CodeCoverage
72+
OutputFile = $TestFilePath
73+
OutputFormat = 'NUnitXml'
74+
PassThru = $true
75+
}
76+
$Script:TestResults = Invoke-Pester @invokePesterSplat
6477

6578
[xml]$content = Get-Content $TestFilePath
6679
$content.'test-results'.'test-suite'.type = "Powershell"
@@ -94,7 +107,13 @@ Task Build -Depends Test {
94107
Set-ModuleFunctions -Name $env:BHPSModuleManifest -FunctionsToExport $functions
95108

96109
# Bump the module version
97-
$version = [version] (Step-Version -Version (Get-Metadata -Path $env:BHPSModuleManifest) -By $StepVersionBy)
110+
$stepVersionSplat = @{
111+
Version = (Get-Metadata -Path $env:BHPSModuleManifest)
112+
}
113+
If ($null -ne $StepVersionBy) {
114+
$stepVersionSplat.Add('By',$StepVersionBy)
115+
}
116+
$version = [version](Step-Version @stepVersionSplat)
98117
$galleryVersion = Get-NextPSGalleryVersion -Name $env:BHProjectName
99118
if($version -lt $galleryVersion)
100119
{
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
Function Update-ServiceNowDateTimeField {
2+
<#
3+
.SYNOPSIS
4+
Attempt to update statically set ServiceNow result fields from string to DateTime fields
5+
6+
.DESCRIPTION
7+
Attempt to update statically set ServiceNow result fields from string to DateTime fields
8+
9+
.EXAMPLE
10+
Update-ServiceNowDateTimeField -Result $Result
11+
12+
.OUTPUTS
13+
System.Management.Automation.PSCustomObject
14+
15+
.NOTES
16+
17+
#>
18+
19+
[OutputType([PSCustomObject[]])]
20+
[CmdletBinding(SupportsShouldProcess)]
21+
param (
22+
# Pipeline variable
23+
[Parameter(
24+
Mandatory = $true,
25+
ValueFromPipeline = $true
26+
)]
27+
[ValidateNotNullOrEmpty()]
28+
[PSCustomObject[]]$Result
29+
)
30+
31+
begin {}
32+
process {
33+
# Convert specific fields to DateTime format
34+
$ConvertToDateField = @('closed_at', 'expected_start', 'follow_up', 'opened_at', 'sys_created_on', 'sys_updated_on', 'work_end', 'work_start')
35+
36+
If ($PSCmdlet.ShouldProcess($SearchBase,$MyInvocation.MyCommand)) {
37+
ForEach ($SNResult in $Result) {
38+
ForEach ($Property in $ConvertToDateField) {
39+
If (-not [string]::IsNullOrEmpty($SNResult.$Property)) {
40+
Try {
41+
# 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
42+
$CultureDateTimeFormat = (Get-Culture).DateTimeFormat
43+
$DateFormat = $CultureDateTimeFormat.ShortDatePattern
44+
$TimeFormat = $CultureDateTimeFormat.LongTimePattern
45+
$DateTimeFormat = "$DateFormat $TimeFormat"
46+
$SNResult.$Property = [DateTime]::ParseExact($($SNResult.$Property), $DateTimeFormat, [System.Globalization.DateTimeFormatInfo]::InvariantInfo, [System.Globalization.DateTimeStyles]::None)
47+
}
48+
Catch {
49+
Try {
50+
# Universal Format
51+
$DateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
52+
$SNResult.$Property = [DateTime]::ParseExact($($SNResult.$Property), $DateTimeFormat, [System.Globalization.DateTimeFormatInfo]::InvariantInfo, [System.Globalization.DateTimeStyles]::None)
53+
}
54+
Catch {
55+
# If the local culture and universal formats both fail keep the property as a string (Do nothing)
56+
$null = 'Code to make PSSA happy when we just want to suppress errors'
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
64+
$Result
65+
}
66+
end {}
67+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
Function Add-ServiceNowAttachment {
2+
<#
3+
.SYNOPSIS
4+
Attaches a file to an existing ticket.
5+
6+
.DESCRIPTION
7+
Attaches a file to an existing ticket.
8+
9+
.PARAMETER Number
10+
ServiceNow ticket number
11+
12+
.PARAMETER Table
13+
ServiceNow ticket table name
14+
15+
.PARAMETER File
16+
A valid path to the file to attach
17+
18+
.EXAMPLE
19+
Add-ServiceNowAttachment -Number $Number -Table $Table -File .\File01.txt, .\File02.txt
20+
21+
Upload one or more files to a ServiceNow ticket by specifing the number and table
22+
23+
.EXAMPLE
24+
Add-ServiceNowAttachment -Number $Number -Table $Table -File .\File01.txt -ContentType 'text/plain'
25+
26+
Upload a file and specify the MIME type (content type). Should only be required if the function cannot automatically determine the type.
27+
28+
.EXAMPLE
29+
Add-ServiceNowAttachment -Number $Number -Table $Table -File .\File01.txt -PassThru
30+
31+
Upload a file and receive back the file details.
32+
33+
.OUTPUTS
34+
System.Management.Automation.PSCustomObject
35+
36+
.NOTES
37+
38+
#>
39+
40+
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText','')]
41+
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidGlobalVars','')]
42+
43+
[OutputType([PSCustomObject[]])]
44+
[CmdletBinding(DefaultParameterSetName,SupportsShouldProcess=$true)]
45+
Param(
46+
# Object number
47+
[Parameter(Mandatory=$true)]
48+
[string]$Number,
49+
50+
# Table containing the entry
51+
[Parameter(Mandatory=$true)]
52+
[string]$Table,
53+
54+
# Filter results by file name
55+
[parameter(Mandatory=$true)]
56+
[ValidateScript({
57+
Test-Path $_
58+
})]
59+
[string[]]$File,
60+
61+
# Content (MIME) type - if not automatically determined
62+
[Parameter(Mandatory=$false)]
63+
[string]$ContentType,
64+
65+
# Credential used to authenticate to ServiceNow
66+
[Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$true)]
67+
[ValidateNotNullOrEmpty()]
68+
[Alias('ServiceNowCredential')]
69+
[PSCredential]$Credential,
70+
71+
# The URL for the ServiceNow instance being used
72+
[Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$true)]
73+
[ValidateScript({Test-ServiceNowURL -Url $_})]
74+
[ValidateNotNullOrEmpty()]
75+
[Alias('Url')]
76+
[string]$ServiceNowURL,
77+
78+
# Azure Automation Connection object containing username, password, and URL for the ServiceNow instance
79+
[Parameter(ParameterSetName='UseConnectionObject', Mandatory=$true)]
80+
[ValidateNotNullOrEmpty()]
81+
[Hashtable]$Connection,
82+
83+
# Allow the results to be shown
84+
[Parameter()]
85+
[switch]$PassThru
86+
)
87+
88+
begin {}
89+
process {
90+
Try {
91+
# Use the number and table to determine the sys_id
92+
$getServiceNowTableEntry = @{
93+
Table = $Table
94+
MatchExact = @{number = $number}
95+
ErrorAction = 'Stop'
96+
}
97+
98+
# Update the Table Splat if an applicable parameter set name is in use
99+
Switch ($PSCmdlet.ParameterSetName) {
100+
'SpecifyConnectionFields' {
101+
$getServiceNowTableEntry.Add('Credential', $Credential)
102+
$getServiceNowTableEntry.Add('ServiceNowURL', $ServiceNowURL)
103+
}
104+
'UseConnectionObject' {
105+
$getServiceNowTableEntry.Add('Connection', $Connection)
106+
}
107+
Default {
108+
If (-not (Test-ServiceNowAuthIsSet)) {
109+
Throw "Exception: You must do one of the following to authenticate: `n 1. Call the Set-ServiceNowAuth cmdlet `n 2. Pass in an Azure Automation connection object `n 3. Pass in an endpoint and credential"
110+
}
111+
}
112+
}
113+
114+
$TableSysID = Get-ServiceNowTableEntry @getServiceNowTableEntry | Select-Object -Expand sys_id
115+
116+
# Process credential steps based on parameter set name
117+
Switch ($PSCmdlet.ParameterSetName) {
118+
'SpecifyConnectionFields' {
119+
$ApiUrl = 'https://' + $ServiceNowURL + '/api/now/v1/attachment'
120+
}
121+
'UseConnectionObject' {
122+
$SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force
123+
$Credential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword)
124+
$ApiUrl = 'https://' + $Connection.ServiceNowUri + '/api/now/v1/attachment'
125+
}
126+
Default {
127+
If ((Test-ServiceNowAuthIsSet)) {
128+
$Credential = $Global:ServiceNowCredentials
129+
$ApiUrl = $Global:ServiceNowRESTURL + '/attachment'
130+
}
131+
Else {
132+
Throw "Exception: You must do one of the following to authenticate: `n 1. Call the Set-ServiceNowAuth cmdlet `n 2. Pass in an Azure Automation connection object `n 3. Pass in an endpoint and credential"
133+
}
134+
}
135+
}
136+
137+
ForEach ($Object in $File) {
138+
$FileData = Get-ChildItem $Object -ErrorAction Stop
139+
If (-not $ContentType) {
140+
Add-Type -AssemblyName 'System.Web'
141+
$ContentType = [System.Web.MimeMapping]::GetMimeMapping($FileData.FullName)
142+
}
143+
144+
# POST: https://instance.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=d71f7935c0a8016700802b64c67c11c6&file_name=Issue_screenshot
145+
$Uri = "{0}/file?table_name={1}&table_sys_id={2}&file_name={3}" -f $ApiUrl,$Table,$TableSysID,$FileData.Name
146+
147+
$invokeRestMethodSplat = @{
148+
Uri = $Uri
149+
Headers = @{'Content-Type' = $ContentType}
150+
Method = 'POST'
151+
InFile = $FileData.FullName
152+
Credential = $Credential
153+
}
154+
155+
If ($PSCmdlet.ShouldProcess($Uri,$MyInvocation.MyCommand)) {
156+
$Result = (Invoke-RestMethod @invokeRestMethodSplat).Result
157+
158+
If ($PassThru) {
159+
$Result | Update-ServiceNowDateTimeField
160+
}
161+
}
162+
}
163+
}
164+
Catch {
165+
Write-Error $PSItem
166+
}
167+
}
168+
end {}
169+
}

0 commit comments

Comments
 (0)