diff --git a/.gitignore b/.gitignore index 0778a37..3031fa8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -ServiceNow-Module.Pester.Defaults.json +*.Pester.Defaults.json *.zip -PSServiceNow.Pester.Defaults.json diff --git a/Build/build.ps1 b/Build/build.ps1 new file mode 100644 index 0000000..930afe3 --- /dev/null +++ b/Build/build.ps1 @@ -0,0 +1,30 @@ +<# +.Description +Installs and loads all the required modules for the build. +.Author +Warren F. (RamblingCookieMonster) +#> + +[cmdletbinding()] +param ($Task = 'Default') + +# Grab nuget bits, install modules, set build variables, start build. +Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null + +$Modules = @("Psake", "PSDeploy","BuildHelpers","PSScriptAnalyzer", "Pester") + +ForEach ($Module in $Modules) { + If (-not (Get-Module -Name $Module -ListAvailable)) { + Switch ($Module) { + Pester {Install-Module $Module -Force -SkipPublisherCheck} + Default {Install-Module $Module -Force} + } + } + Import-Module $Module +} + +$Path = (Resolve-Path $PSScriptRoot\..).Path +Set-BuildEnvironment -Path $Path + +Invoke-psake -buildFile $PSScriptRoot\psake.ps1 -taskList $Task -nologo +exit ([int](-not $psake.build_success)) \ No newline at end of file diff --git a/Build/psake.ps1 b/Build/psake.ps1 new file mode 100644 index 0000000..e967ec5 --- /dev/null +++ b/Build/psake.ps1 @@ -0,0 +1,194 @@ +# PSake makes variables declared here available in other scriptblocks +Properties { + # Find the build folder based on build system + $ProjectRoot = Resolve-Path $ENV:BHProjectPath + if(-not $ProjectRoot) + { + $ProjectRoot = Resolve-Path "$PSScriptRoot\.." + } + + $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" + $PSVersion = $PSVersionTable.PSVersion.Major + $TestFile = "TestResults_PS$PSVersion`_$TimeStamp.xml" + $lines = '----------------------------------------------------------------------' + + $Verbose = @{} + if($ENV:BHCommitMessage -match "!verbose") + { + $Verbose = @{Verbose = $True} + } +} + +Task Default -Depends Deploy + +# Init some things +Task Init { + $lines + Set-Location $ProjectRoot + "Build System Details:" + Get-Item ENV:BH* | Format-List + "`n" +} + + +Task Analyze -Depends Init { + $saResults = Invoke-ScriptAnalyzer -Path $ENV:BHModulePath -Severity @('Error', 'Warning') -Recurse -Verbose:$false + if ($saResults) { + $saResults | Format-Table + #Write-Error -Message 'One or more Script Analyzer errors/warnings where found. Build cannot continue!' + } +} + + +Task UnitTests -Depends Init { + $lines + 'Running quick unit tests to fail early if there is an error' + $TestResults = Invoke-Pester -Path $ProjectRoot\Tests\*unit* -PassThru -Tag Build + + if($TestResults.FailedCount -gt 0) + { + Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" + } + "`n" +} + +Task Test -Depends UnitTests { + $lines + "`nSTATUS: Testing with PowerShell $PSVersion" + + # Gather test results. Store them in a variable and file + $TestFilePath = Join-Path $ProjectRoot $TestFile + $CodeFiles = Get-ChildItem $ENV:BHModulePath -Recurse -Include "*.psm1","*.ps1" + $CodeCoverage = New-Object System.Collections.ArrayList + $CodeCoverage.AddRange($CodeFiles.FullName) + $Script:TestResults = Invoke-Pester -Path $ProjectRoot\Tests -CodeCoverage $CodeCoverage -PassThru -OutputFormat NUnitXml -OutputFile $TestFilePath + + # In Appveyor? Upload our tests! #Abstract this into a function? + If($ENV:BHBuildSystem -eq 'AppVeyor') + { + [xml]$content = Get-Content $TestFilePath + $content.'test-results'.'test-suite'.type = "Powershell" + $content.Save($TestFilePath) + + "Uploading $ProjectRoot\$TestFile to AppVeyor" + "JobID: $env:APPVEYOR_JOB_ID" + (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $TestFilePath)) + } + + Remove-Item $TestFilePath -Force -ErrorAction SilentlyContinue + + # Failed tests? + # Need to tell psake or it will proceed to the deployment. Danger! + if($TestResults.FailedCount -gt 0) + { + Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" + } + "`n" +} + +Task Build -Depends Test { + $lines + + $functions = Get-ChildItem "$ENV:BHModulePath\Public\*.ps1" | + Where-Object{ $_.name -notmatch 'Tests'} | + Select-Object -ExpandProperty basename + + # Load the module, read the exported functions, update the psd1 FunctionsToExport + Set-ModuleFunctions -Name $env:BHPSModuleManifest -FunctionsToExport $functions + + # Bump the module version + $version = [version] (Step-Version (Get-Metadata -Path $env:BHPSModuleManifest)) + $galleryVersion = Get-NextPSGalleryVersion -Name $env:BHProjectName + if($version -lt $galleryVersion) + { + $version = $galleryVersion + } + $Script:version = [version]::New($version.Major,$version.Minor,$version.Build) + Write-Host "Using version: $version" + + Update-Metadata -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -Value $version + + # Update Code Coverage + Function Update-CodeCoveragePercent{ + param( + [int]$CodeCoverage=0, + [string]$TextFilePath="$Env:BHProjectPath\Readme.md" + ) + + $BadgeColor = Switch ($CodeCoverage) { + 100 {"brightgreen"} + {95..99 -contains $_} {"green"} + {85..94 -contains $_} {"yellowgreengreen"} + {75..84 -contains $_} {"yellow"} + {65..74 -contains $_} {"orange"} + default {"red"} + } + + $ReadmeContent = Get-Content $TextFilePath + $ReadmeContent = $ReadmeContent | ForEach-Object {$_-replace "!\[Test Coverage\].+\)", "![Test Coverage](https://img.shields.io/badge/coverage-$CodeCoverage%25-$BadgeColor.svg)"} + Set-Content -Path $TextFilePath -Value $ReadmeContent + } + + $CoveragePercent = 100-(($Script:TestResults.CodeCoverage.NumberOfCommandsMissed/$Script:TestResults.CodeCoverage.NumberOfCommandsAnalyzed)*100) + "Running Update-CodeCoveragePercent with percentage $CoveragePercent" + Update-CodeCoveragePercent -CodeCoverage $CoveragePercent + "`n" +} + +Task MakePackage -Depends Build,Test { + $lines + + function ZipFiles + { + param( $zipfilename, $sourcedir ) + Add-Type -Assembly System.IO.Compression.FileSystem + $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal + [System.IO.Compression.ZipFile]::CreateFromDirectory($sourcedir, + $zipfilename, $compressionLevel, $true) + } + + function New-MakePackage{ + param( + [string]$PackageName, + [string]$PackagePath, + [string]$ModuleName + ) + + $ZipFile = "$PackagePath\$PackageName" + Remove-Item $ZipFile -Force -ErrorAction SilentlyContinue | Out-Null + ZipFiles $ZipFile $ModuleName + } + + # Update/Create the package + $PackageName = "$($Env:BHProjectName)-v$($Script:version).zip" + "Creating package $PackageName" + New-MakePackage -PackageName $PackageName -PackagePath $ProjectRoot -ModuleName $ENV:BHModulePath + + "`n" +} + +Task Deploy -Depends Build,MakePackage { + $lines + + # Gate deployment + if( + $ENV:BHBuildSystem -ne 'Unknown' -and + $ENV:BHBranchName -eq "master" -and + $ENV:BHCommitMessage -match '!deploy' + ) + { + $Params = @{ + Path = $ProjectRoot + Force = $true + } + + Invoke-PSDeploy @Verbose @Params + } + else + { + "Skipping deployment: To deploy, ensure that...`n" + + "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + + "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + + "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage)" + } +} \ No newline at end of file diff --git a/MAKE.ps1 b/MAKE.ps1 deleted file mode 100644 index 7f70d6c..0000000 --- a/MAKE.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -function ZipFiles -{ - param( $zipfilename, $sourcedir ) - Add-Type -Assembly System.IO.Compression.FileSystem - $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal - [System.IO.Compression.ZipFile]::CreateFromDirectory($sourcedir, - $zipfilename, $compressionLevel, $true) -} - -function New-MakePackage{ - param( - [string[]]$PackageFilePatternExclusions, - [string]$PackageName, - [string]$ModuleName - ) - @($FilePatternExclusions | %{"MAKE.zip" -match $_}).contains($true) - - $FilesToInclude = Get-ChildItem -Path $here | ?{ - $File=$_; - !$_.PSIsContainer -and - !($PackageFilePatternExclusions | %{$File.Name -match $_}).contains($true) - } - - # Create temporary folder and copy the files we want into it - New-Item $here\$ModuleName -ItemType Container -Force | Out-Null - $FilesToInclude | %{Copy-Item -Path $_.FullName -Destination $here\$ModuleName\$_ -Force} - - # Create a zip based on that folder (overwriting it if it already exists) - $ZipFile = "$here\$PackageName" - Remove-Item $ZipFile -Force -ErrorAction SilentlyContinue | Out-Null - ZipFiles $ZipFile $here\$ModuleName - Remove-Item $here\$ModuleName -Recurse| Out-Null -} - -Function Update-CodeCoveragePercent{ - param( - [int]$CodeCoverage=0, - [string]$TextFilePath="$here\Readme.md" - ) - $ReadmeContent = Get-Content $TextFilePath - $ReadmeContent = $ReadmeContent | %{$_-replace "!\[Test Coverage\].+\)", "![Test Coverage](https://img.shields.io/badge/coverage-$CodeCoverage%25-yellowgreen.svg)"} - Set-Content -Path $TextFilePath -Value $ReadmeContent -} - -Function UpdateManifest{ - param( - [string]$ManifestPath, - [string]$Version - ) - - Write-Verbose "Updating $ManifestPath to version $Version" - $ManifestContent = Get-Content $ManifestPath - $ManifestContent = $ManifestContent | %{$_ -replace "ModuleVersion = '(\d|\.)+'", "ModuleVersion = '$Version'"} - Set-Content -path $ManifestPath -Value $ManifestContent -} - -$PackageFilePatternExclusions = @( - "MAKE\.ps1", - ".+\.zip", - ".+\.md" - ".+\.Tests\.ps1", - "\.gitignore", - "LICENSE", - ".+\.Pester.Defaults.json" -) - -$here = Split-Path -Parent $MyInvocation.MyCommand.Path - -$Version = "0.1.13" -$ModuleName = "PSServiceNow" -$PackageName = "$ModuleName-v$($version).zip"; - -# Perform Pester tests -$TestResult = Invoke-Pester -CodeCoverage '*.psm1' -PassThru -$CoveragePercent = 100-(($testResult.CodeCoverage.NumberOfCommandsMissed/$testResult.CodeCoverage.NumberOfCommandsAnalyzed)*100) - -# Update/Create the package and -if($TestResult.FailedCount -eq 0){ - New-MakePackage -PackageFilePatternExclusions $PackageFilePatternExclusions -PackageName $PackageName -ModuleName $ModuleName - Update-CodeCoveragePercent -CodeCoverage $CoveragePercent - UpdateManifest -ManifestPath "$here\$ModuleName.psd1" -Version $Version -} - \ No newline at end of file diff --git a/PSServiceNow-Automation.json b/PSServiceNow-Automation.json deleted file mode 100644 index 04fdc45..0000000 Binary files a/PSServiceNow-Automation.json and /dev/null differ diff --git a/PSServiceNow-Changes.psm1 b/PSServiceNow-Changes.psm1 deleted file mode 100644 index 8d129a2..0000000 --- a/PSServiceNow-Changes.psm1 +++ /dev/null @@ -1,131 +0,0 @@ -function Get-ServiceNowChangeRequest { - param( - # Machine name of the field to order by - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$OrderBy='opened_at', - - # Direction of ordering (Desc/Asc) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("Desc", "Asc")] - [string]$OrderDirection='Desc', - - # Maximum number of records to return - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [int]$Limit=10, - - # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchExact=@{}, - - # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchContains=@{}, - - # Whether or not to show human readable display values instead of machine values - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("true","false", "all")] - [string]$DisplayValues='true', - - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - $private:Query = New-ServiceNowQuery -OrderBy $private:OrderBy -OrderDirection $private:OrderDirection -MatchExact $private:MatchExact -MatchContains $private:MatchContains - - - if ($Connection -ne $null) { - $private:result = Get-ServiceNowTable -Table 'change_request' -Query $private:Query -Limit $private:Limit -DisplayValues $private:DisplayValues -Connection $Connection - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) { - $private:result = Get-ServiceNowTable -Table 'change_request' -Query $private:Query -Limit $private:Limit -DisplayValues $private:DisplayValues -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL - } - else { - $private:result = Get-ServiceNowTable -Table 'change_request' -Query $private:Query -Limit $private:Limit -DisplayValues $private:DisplayValues - } - - # Add the custom type to the change request to enable a view - $private:result | %{$_.psobject.TypeNames.Insert(0, "PSServiceNow.ChangeRequest")} - return $private:result -} - - -<# -.EXAMPLE - Update-ServiceNowChangeRequest -Values @{ 'state' = 3 } -SysId -#> -function Update-ServiceNowChangeRequest -{ - Param( # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$true)] - [parameter(ParameterSetName='UseConnectionObject', mandatory=$true)] - [parameter(ParameterSetName='SetGlobalAuth', mandatory=$true)] - [string]$SysId, - - # Hashtable of values to use as the record's properties - [parameter(mandatory=$true)] - [hashtable]$Values, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - if ($Connection -ne $null) - { - Update-ServiceNowTableEntry -Table 'change_request' -Values $Values -Connection $Connection -SysId $SysId - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - Update-ServiceNowTableEntry -Table 'change_request' -Values $Values -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL -SysId $SysId - } - else - { - Update-ServiceNowTableEntry -Table 'change_request' -Values $Values -SysId $SysId - } -} - diff --git a/PSServiceNow-Incidents.psm1 b/PSServiceNow-Incidents.psm1 deleted file mode 100644 index 8315202..0000000 --- a/PSServiceNow-Incidents.psm1 +++ /dev/null @@ -1,277 +0,0 @@ -<# -.Synopsis - Returns incidents from the connected ServiceNow instance based (optionally based on criteria) -.NOTES - You must have invoked Set-ServiceNowAuth prior to executing this cmdlet -.EXAMPLE - Return the incident whose number is exactly INC0010683 - Get-ServiceNowIncident -MatchExact @{number='INC0010683'} -.EXAMPLE - Return all incidents where the short description contains the word 'user' - Get-ServiceNowIncident -MatchContains @{short_description='user'} -#> - -function Get-ServiceNowIncident{ - param( - # Machine name of the field to order by - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$OrderBy='opened_at', - - # Direction of ordering (Desc/Asc) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("Desc", "Asc")] - [string]$OrderDirection='Desc', - - # Maximum number of records to return - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [int]$Limit=10, - - # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchExact=@{}, - - # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchContains=@{}, - - # Whether or not to show human readable display values instead of machine values - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("true","false", "all")] - [string]$DisplayValues='true', - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - $Query = New-ServiceNowQuery -OrderBy $OrderBy -OrderDirection $OrderDirection -MatchExact $MatchExact -MatchContains $MatchContains - - if ($Connection -ne $null) - { - $result = Get-ServiceNowTable -Table 'incident' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -Connection $Connection - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $result = Get-ServiceNowTable -Table 'incident' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL - } - else - { - $result = Get-ServiceNowTable -Table 'incident' -Query $Query -Limit $Limit -DisplayValues $DisplayValues - } - - # Set the default property set for the table view - $DefaultProperties = @('number', 'short_description', 'opened_at') - $DefaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$DefaultProperties) - $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($DefaultDisplayPropertySet) - $Result | Add-Member MemberSet PSStandardMembers $PSStandardMembers - - # Return that result! - return $result -} - -<# -.EXAMPLE - New-ServiceNowIncident -ShortDescription "Testing with Pester" ` - -Description "Long description" -AssignmentGroup "e9e9a2406f4c35001855fa0dba3ee4f3" ` - -Category "Internal" -SubCategory "Task" ` - -Comment "Comment" -ConfigurationItem "bee8e0ed6f8475001855fa0dba3ee4ea" ` - -Caller "7a4b573a6f3725001855fa0dba3ee485" ` -#> -function New-ServiceNowIncident{ - Param( - - # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$true)] - [parameter(ParameterSetName='UseConnectionObject', mandatory=$true)] - [parameter(ParameterSetName='SetGlobalAuth', mandatory=$true)] - [string]$Caller, - - # Short description of the incident - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$true)] - [parameter(ParameterSetName='UseConnectionObject', mandatory=$true)] - [parameter(ParameterSetName='SetGlobalAuth', mandatory=$true)] - [string]$ShortDescription, - - # Long description of the incident - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Description, - - # sys_id of the assignment group (use Get-ServiceNowUserGroup to retrieve this) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$AssignmentGroup, - - # Comment to include in the ticket - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Comment, - - # Category of the incident (e.g. 'Network') - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Category, - - # Subcategory of the incident (e.g. 'Network') - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Subcategory, - - # sys_id of the configuration item of the incident - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$ConfigurationItem, - - # custom fields as hashtable - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$CustomFields, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - - $Values = @{ - 'caller_id' = $Caller - 'short_description' = $ShortDescription - 'description' = $Description - 'assignment_group' = $AssignmentGroup - 'comments' = $Comment - 'category' = $Category - 'subcategory' = $Subcategory - 'cmdb_ci' = $ConfigurationItem - } - - if($CustomFields) - { - $Values += $CustomFields - } - - if ($Connection -ne $null) - { - New-ServiceNowTableEntry -Table 'incident' -Values $Values -Connection $Connection - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - New-ServiceNowTableEntry -Table 'incident' -Values $Values -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL - } - else - { - New-ServiceNowTableEntry -Table 'incident' -Values $Values - } - -} - - -<# -.EXAMPLE - Update-ServiceNowIncident-Values @{ 'short_description' = 'updated description'} -SysId -#> -function Update-ServiceNowIncident -{ - Param( # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$true)] - [parameter(ParameterSetName='UseConnectionObject', mandatory=$true)] - [parameter(ParameterSetName='SetGlobalAuth', mandatory=$true)] - [string]$SysId, - - # Hashtable of values to use as the record's properties - [parameter(mandatory=$true)] - [hashtable]$Values, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - if ($Connection -ne $null) - { - Update-ServiceNowTableEntry -Table 'incident' -Values $Values -Connection $Connection -SysId $SysId - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - Update-ServiceNowTableEntry -Table 'incident' -Values $Values -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL -SysId $SysId - } - else - { - Update-ServiceNowTableEntry -Table 'incident' -Values $Values -SysId $SysId - } -} diff --git a/PSServiceNow-Tables.psm1 b/PSServiceNow-Tables.psm1 deleted file mode 100644 index 7df4b37..0000000 --- a/PSServiceNow-Tables.psm1 +++ /dev/null @@ -1,296 +0,0 @@ -function Get-ServiceNowTable -{ - [OutputType([Array])] - Param - ( - # Name of the table we're querying (e.g. incidents) - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$false)] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Table, - - # sysparm_query param in the format of a ServiceNow encoded query string (see http://wiki.servicenow.com/index.php?title=Encoded_Query_Strings) - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$false)] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Query, - - # Maximum number of records to return - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$false)] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [int]$Limit=10, - - # Whether or not to show human readable display values instead of machine values - [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$false)] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("true","false", "all")] - [string]$DisplayValues='false', - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - #Get credential and ServiceNow REST URL - if ($Connection -ne $null) - { - $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force - $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) - $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' - - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' - } - elseif((Test-ServiceNowAuthIsSet)) - { - $ServiceNowCredential = $Global:ServiceNowCredentials - $ServiceNowURL = $global:ServiceNowRESTURL - } - else - { - 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" - } - - # Populate the query - $Body = @{'sysparm_limit'=$Limit;'sysparm_display_value'=$DisplayValues} - if($Query){ - $Body.sysparm_query = $Query - } - - # Fire and return - $Uri = $ServiceNowURL + "/table/$Table" - - return (Invoke-RestMethod -Uri $Uri -Credential $ServiceNowCredential -Body $Body -ContentType "application/json").result -} - -function New-ServiceNowTableEntry{ - Param - ( - # Name of the table we're inserting into (e.g. incidents) - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Table, - - # Hashtable of values to use as the record's properties - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$Values, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - - #Get credential and ServiceNow REST URL - if ($Connection -ne $null) - { - $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force - $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) - $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' - - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' - } - elseif((Test-ServiceNowAuthIsSet)) - { - $ServiceNowCredential = $Global:ServiceNowCredentials - $ServiceNowURL = $global:ServiceNowRESTURL - } - else - { - 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" - } - - - $Body = $Values | ConvertTo-Json; - - #Convert to UTF8 array to support special chars such as the danish "�","�","�" - $utf8Bytes = [System.Text.Encoding]::UTf8.GetBytes($Body) - - # Fire and return - $Uri = $ServiceNowURL + "/table/$Table" - return (Invoke-RestMethod -Uri $uri -Method Post -Credential $ServiceNowCredential -Body $utf8Bytes -ContentType "application/json" -UseBasicParsing).result -} - -<# -.COMMENT - Untested -#> -function Remove-ServiceNowTableEntry{ -[CmdletBinding(ConfirmImpact='High')] - Param( - # sys_id of the entry we're deleting - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$SysId, - - # Table containing the entry we're deleting - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Table, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - #Get credential and ServiceNow REST URL - if ($Connection -ne $null) - { - $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force - $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) - $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' - - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' - } - elseif((Test-ServiceNowAuthIsSet)) - { - $ServiceNowCredential = $Global:ServiceNowCredentials - $ServiceNowURL = $global:ServiceNowRESTURL - } - else - { - 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" - } - - # Fire and return - $Uri = $ServiceNowURL + "/table/$Table/$SysID" - return (Invoke-RestMethod -Uri $uri -Method Delete -Credential $ServiceNowCredential -Body $Body -ContentType "application/json").result -} - - -function Update-ServiceNowTableEntry{ -[CmdletBinding(ConfirmImpact='High')] - Param( - # sys_id of the entry we're deleting - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$SysId, - - # Table containing the entry we're deleting - [parameter(mandatory=$true)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$Table, - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection, - - # Hashtable of values to use as the record's properties - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$Values - - ) - - #Get credential and ServiceNow REST URL - if ($Connection -ne $null) - { - $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force - $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) - $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' - - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' - } - elseif((Test-ServiceNowAuthIsSet)) - { - $ServiceNowCredential = $Global:ServiceNowCredentials - $ServiceNowURL = $global:ServiceNowRESTURL - } - else - { - 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" - } - - $Body = $Values | ConvertTo-Json; - - #Convert to UTF8 array to support special chars such as the danish "�","�","�" - $utf8Bytes = [System.Text.Encoding]::UTf8.GetBytes($Body) - - # Fire and return - $Uri = $ServiceNowURL + "/table/$Table/$SysID" - return (Invoke-RestMethod -Uri $uri -Method Patch -Credential $ServiceNowCredential -Body $utf8Bytes -ContentType "application/json").result -} \ No newline at end of file diff --git a/PSServiceNow-Users.psm1 b/PSServiceNow-Users.psm1 deleted file mode 100644 index 934afcc..0000000 --- a/PSServiceNow-Users.psm1 +++ /dev/null @@ -1,188 +0,0 @@ - -<# -.EXAMPLE - Get-ServiceNowUserGroup -MatchContains @{'name'='Architect'} -#> - -function Get-ServiceNowUserGroup{ - param( - # Machine name of the field to order by - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$OrderBy='name', - - # Direction of ordering (Desc/Asc) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("Desc", "Asc")] - [string]$OrderDirection='Desc', - - # Maximum number of records to return - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [int]$Limit=10, - - # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchExact=@{}, - - # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchContains=@{}, - - # Whether or not to show human readable display values instead of machine values - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("true","false", "all")] - [string]$DisplayValues='true', - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - - $Query = New-ServiceNowQuery -OrderBy $OrderBy -OrderDirection $OrderDirection -MatchExact $MatchExact -MatchContains $MatchContains - - - if ($Connection -ne $null) - { - $result = Get-ServiceNowTable -Table 'sys_user_group' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -Connection $Connection - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - - $result = Get-ServiceNowTable -Table 'sys_user_group' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL - } - else - { - $result = Get-ServiceNowTable -Table 'sys_user_group' -Query $Query -Limit $Limit -DisplayValues $DisplayValues - } - - # Set the default property set for the table view - $DefaultProperties = @('name', 'email', 'sys_id') - $DefaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$DefaultProperties) - $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($DefaultDisplayPropertySet) - $Result | Add-Member MemberSet PSStandardMembers $PSStandardMembers - return $result -} - -<# -.EXAMPLE - Get-ServiceNowUser -MatchExact @{'name'='Sam Martin'} -#> -function Get-ServiceNowUser{ - param( - # Machine name of the field to order by - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [string]$OrderBy='name', - - # Direction of ordering (Desc/Asc) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("Desc", "Asc")] - [string]$OrderDirection='Desc', - - # Maximum number of records to return - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [int]$Limit=10, - - # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchExact=@{}, - - # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [hashtable]$MatchContains=@{}, - - # Whether or not to show human readable display values instead of machine values - [parameter(mandatory=$false)] - [parameter(ParameterSetName='SpecifyConnectionFields')] - [parameter(ParameterSetName='UseConnectionObject')] - [parameter(ParameterSetName='SetGlobalAuth')] - [ValidateSet("true","false", "all")] - [string]$DisplayValues='true', - - # Credential used to authenticate to ServiceNow - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [PSCredential] - $ServiceNowCredential, - - # The URL for the ServiceNow instance being used - [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [string] - $ServiceNowURL, - - #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance - [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] - [ValidateNotNullOrEmpty()] - [Hashtable] - $Connection - ) - - $Query = New-ServiceNowQuery -OrderBy $OrderBy -OrderDirection $OrderDirection -MatchExact $MatchExact -MatchContains $MatchContains - - if ($Connection -ne $null) - { - $result = Get-ServiceNowTable -Table 'sys_user' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -Connection $Connection - } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) - { - $result = Get-ServiceNowTable -Table 'sys_user' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL - } - else - { - $result = Get-ServiceNowTable -Table 'sys_user' -Query $Query -Limit $Limit -DisplayValues $DisplayValues - } - - # Set the default property set for the table view - $DefaultProperties = @('name', 'email', 'sys_id') - $DefaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$DefaultProperties) - $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($DefaultDisplayPropertySet) - $Result | Add-Member MemberSet PSStandardMembers $PSStandardMembers - return $result -} \ No newline at end of file diff --git a/PSServiceNow.Tests.ps1 b/PSServiceNow.Tests.ps1 deleted file mode 100644 index 0db3b06..0000000 --- a/PSServiceNow.Tests.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -$here = Split-Path -Parent $MyInvocation.MyCommand.Path -$DefaultsFile = "$here\PSServiceNow.Pester.Defaults.json" - -# Load defaults from file (merging into $global:ServiceNowPesterTestDefaults -if(Test-Path $DefaultsFile){ - $defaults = if($global:ServiceNowPesterTestDefaults){$global:ServiceNowPesterTestDefaults}else{@{}}; - (Get-Content $DefaultsFile | Out-String | ConvertFrom-Json).psobject.properties | %{$defaults."$($_.Name)" = $_.Value} - - # Prompt for credentials - $defaults.Creds = if($defaults.Creds){$defaults.Creds}else{Get-Credential} - - $global:ServiceNowPesterTestDefaults = $defaults -}else{ - Write-Error "$DefaultsFile does not exist. Created example file. Please populate with your values"; - - # Write example file - @{ - ServiceNowURL = 'testingurl.service-now.com' - TestCategory = 'Internal' - TestUserGroup = 'e9e9a2406f4c35001855fa0dba3ee4f3' - TestUser = "7a4b573a6f3725001855fa0dba3ee485" - } | ConvertTo-Json | Set-Content $DefaultsFile - return; -} - -# Load the module (unload it first in case we've made changes since loading it previously) -Remove-Module PSServiceNow -ErrorAction SilentlyContinue -Import-Module $here\PSServiceNow.psd1 - -Describe "ServiceNow-Module" { - - It "Set-ServiceNowAuth works" { - Set-ServiceNowAuth -url $defaults.ServiceNowURL -Credentials $defaults.Creds | Should be $true - } - - It "New-ServiceNowIncident (and by extension New-ServiceNowTableEntry) works" { - $TestTicket = New-ServiceNowIncident -ShortDescription "Testing with Pester" ` - -Description "Long description" -AssignmentGroup $defaults.TestUserGroup ` - -Category $defaults.TestCategory -SubCategory $Defaults.TestSubcategory ` - -Comment "Comment" -ConfigurationItem $defaults.TestConfigurationItem ` - -Caller $defaults.TestUser ` - - $TestTicket.short_description | Should be "Testing with Pester" - } - - It "Get-ServiceNowTable works" { - # There should be one or more incidents returned - (Get-ServiceNowTable -Table 'incident' -Query 'ORDERBYDESCopened_at').Count -gt 0 | Should Match $true - } - - It "Get-ServiceNowIncident works" { - # There should be one or more incidents returned - (Get-ServiceNowIncident).Count -gt 0 | Should Match $true - } - - It "Update-ServiceNowIncident works" { - $TestTicket = New-ServiceNowIncident -ShortDescription "Testing Ticket Update with Pester" ` - -Description "Long description" -AssignmentGroup $defaults.TestUserGroup ` - -Category $defaults.TestCategory -SubCategory $Defaults.TestSubcategory ` - -Comment "Comment" -ConfigurationItem $defaults.TestConfigurationItem ` - -Caller $defaults.TestUser ` - - $TestTicket.short_description | Should be "Testing Ticket Update with Pester" - - $Values = - @{ - 'short_description' = 'Ticket Updated with Pester' - 'description' = 'Even Longer Description' - } - - Update-ServiceNowIncident -SysId $TestTicket.sys_id -Values $Values - - $TestTicket = Get-ServiceNowIncident -MatchExact @{sys_id=$TestTicket.sys_id} - $TestTicket.short_description | Should be "Ticket Updated with Pester" - $TestTicket.description | Should be "Even Longer Description" - } - - It "Get-ServiceNowUserGroup works" { - # There should be one or more user groups returned - (Get-ServiceNowUserGroup).Count -gt 0 | Should Match $true - } - - It "Get-ServiceNowUser works" { - # There should be one or more user groups returned - (Get-ServiceNowUser).Count -gt 0 | Should Match $true - } - - It "Get-ServiceNowConfigurationItem works" { - # There should be one or more configuration items returned - (Get-ServiceNowConfigurationItem).Count -gt 0 | Should Match $true - } - - It "Get-ServiceNowChangeRequest works" { - (Get-ServiceNowChangeRequest).Count -gt 0 | Should Match $true - } -} \ No newline at end of file diff --git a/PSServiceNow.format.ps1xml b/PSServiceNow.format.ps1xml deleted file mode 100644 index 667c8c9..0000000 --- a/PSServiceNow.format.ps1xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - PSServiceNow.ChangeRequest - - PSServiceNow.ChangeRequest - - - - - 20 - - - - 20 - - - - 20 - - - 20 - - - - 30 - - - 20 - - - - - - - number - - - short_description - - - state - - - - $_.assigned_to.display_value - - - - approval - - - - $_.cmdb_ci.display_value - - - - opened_at - - - - - - - - - \ No newline at end of file diff --git a/Readme.md b/Readme.md index 8ae7a55..6daa751 100644 --- a/Readme.md +++ b/Readme.md @@ -1,41 +1,81 @@ -# PSServiceNow -[![GitHub release](https://img.shields.io/github/release/Sam-Martin/servicenow-powershell.svg)](https://github.com/Sam-Martin/servicenow-powershell/releases/latest) [![GitHub license](https://img.shields.io/github/license/Sam-Martin/servicenow-powershell.svg)](LICENSE) ![Test Coverage](https://img.shields.io/badge/coverage-68%25-yellowgreen.svg) -This PowerShell module provides a series of cmdlets for interacting with the [ServiceNow REST API](http://wiki.servicenow.com/index.php?title=REST_API), performed by wrapping `Invoke-RestMethod` for the API calls. +# ServiceNow + +[![GitHub release](https://img.shields.io/github/release/Sam-Martin/servicenow-powershell.svg)](https://github.com/Sam-Martin/servicenow-powershell/releases/latest) [![GitHub license](https://img.shields.io/github/license/Sam-Martin/servicenow-powershell.svg)](LICENSE) ![Test Coverage](https://img.shields.io/badge/coverage-73%25-orange.svg) + +This PowerShell module provides a series of cmdlets for interacting with the [ServiceNow REST API](http://wiki.servicenow.com/index.php?title=REST_API), performed by wrapping `Invoke-RestMethod` for the API calls. + **IMPORTANT:** Neither this module nor its creator are in any way affiliated with ServiceNow. +## Version 1 + +The module has been renamed from PSServiceNow to ServiceNow for version 1. This change moves us away from the reserved "PS" prefix. Since the name change is a major change for the user base and the project was never incremented to v1 we've taken the opportunity to label it such. + +In addition to the name change the following high level changes have been made: + +Back End: + +* The module structure has been updated to individual files for each function. +* The build process has been migrated from MAKE to psake with support of the BuildHelpers module. +* Pester testing has been expanded to cover more scenarios. +* Improved code formatting, removed aliases, fixed file encoding. + +The gains are marginal in some aspects, but intended to allow for better management in the future. + +Front End: + +* The following fields are now returned in the DateTime format instead of string: 'closed_at','expected_start','follow_up','opened_at','sys_created_on','sys_updated_on','work_end','work_start' +* The formatting of returned data has been updated across all the `Get` functions except `Get-ServiceNowTable`. This means you'll see a handful of default properties returned and can use `Format-List` or `Select-Object` to view all other properties associated with the object. + +These changes should improve your ability to filter on the right, especially by DateTime, as well as return more information in general. + ## Requirements + Requires PowerShell 3.0 or above as this is when `Invoke-RestMethod` was introduced. ## Usage -Download the [latest release](https://github.com/Sam-Martin/servicenow-powershell/releases/latest) and extract the .psm1 and .psd1 files to your PowerShell profile directory (i.e. the `Modules` directory under wherever `$profile` points to in your PS console) and run: -`Import-Module PSServiceNow` -Once you've done this, all the cmdlets will be at your disposal, you can see a full list using `Get-Command -Module PSServiceNow`. -### Example - Retrieving an Incident Containing the Word 'PowerShell' +Download the [latest release](https://github.com/Sam-Martin/servicenow-powershell/releases/latest) and extract the .psm1 and .psd1 files to your PowerShell profile directory (i.e. the `Modules` directory under wherever `$profile` points to in your PS console) and run: +`Import-Module ServiceNow` +Once you've done this, all the cmdlets will be at your disposal, you can see a full list using `Get-Command -Module ServiceNow`. + +### Example - Using Set-ServiceNowAuth + +```PowerShell +Set-ServiceNowAuth -url InstanceName.service-now.com -Credentials (Get-Credential) ``` -Import-Module PSServiceNow -Set-ServiceNowAuth -Get-ServiceNowIncident -MatchContains @{short_description='PowerShell'} + +The URL should be the instance name portion of the FQDN for your instance. For if you browse to `https://myinstance.service-now.com` the URL required for the module is `myinstance.service-now.com`. + +### Example - Retrieving an Incident Containing the Word 'PowerShell' + +```PowerShell +Import-Module ServiceNow +Set-ServiceNowAuth +Get-ServiceNowIncident -MatchContains @{short_description='PowerShell'} ``` ### Example - Retrieving an Incident Containing the Word 'PowerShell' While Passing Authentication -``` -Import-Module PSServiceNow + +```PowerShell +Import-Module ServiceNow Get-ServiceNowIncident -MatchContains @{short_description='PowerShell'} -ServiceNowCredential $PSCredential -ServiceNowURL $ServiceNowURL ``` ### Example - Update a Ticket -``` + +```PowerShell $Incident = Get-ServiceNowIncident -Limit 1 -MatchContains @{short_description='PowerShell'} Update-ServiceNowIncident -SysID $Incident.Sys_ID -Values @{comments='Updated via PowerShell'} ``` ### Azure Connection Object (Automation Integration Module Support) -The module can use the `Connection` parameter in conjunction with the included `PSServiceNow-Automation.json` file for use as an Azure automation integration module. Details of the process is available at [Authoring Integration Modules for Azure Automation](https://azure.microsoft.com/en-us/blog/authoring-integration-modules-for-azure-automation). + +The module can use the `Connection` parameter in conjunction with the included `ServiceNow-Automation.json` file for use as an Azure automation integration module. Details of the process is available at [Authoring Integration Modules for Azure Automation](https://azure.microsoft.com/en-us/blog/authoring-integration-modules-for-azure-automation). The `Connection` parameter accepts a hashtable object that requires a username, password, and ServiceNowURL. -## Cmdlets +## Cmdlets + * Get-ServiceNowChangeRequest * Get-ServiceNowConfigurationItem * Get-ServiceNowIncident @@ -54,11 +94,13 @@ The `Connection` parameter accepts a hashtable object that requires a username, * Update-ServiceNowTableEntry ## Tests + This module comes with [Pester](https://github.com/pester/Pester/) tests for unit testing. ## Scope & Contributing + This module has been created as an abstraction layer to suit my immediate requirements. Contributions are gratefully received however, so please feel free to submit a pull request with additional features or amendments. ## Author -Author:: Sam Martin () +Author:: Sam Martin () diff --git a/ServiceNow/Public/Get-ServiceNowChangeRequest.ps1 b/ServiceNow/Public/Get-ServiceNowChangeRequest.ps1 new file mode 100644 index 0000000..28329f8 --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowChangeRequest.ps1 @@ -0,0 +1,95 @@ +function Get-ServiceNowChangeRequest { + param( + # Machine name of the field to order by + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$OrderBy='opened_at', + + # Direction of ordering (Desc/Asc) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("Desc", "Asc")] + [string]$OrderDirection='Desc', + + # Maximum number of records to return + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [int]$Limit=10, + + # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchExact=@{}, + + # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchContains=@{}, + + # Whether or not to show human readable display values instead of machine values + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("true","false", "all")] + [string]$DisplayValues='true', + + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + # Query Splat + $newServiceNowQuerySplat = @{ + OrderBy = $OrderBy + MatchExact = $MatchExact + OrderDirection = $OrderDirection + MatchContains = $MatchContains + } + $Query = New-ServiceNowQuery @newServiceNowQuerySplat + + # Table Splat + $getServiceNowTableSplat = @{ + Table = 'change_request' + Query = $Query + Limit = $Limit + DisplayValues = $DisplayValues + } + + # Update the Table Splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $getServiceNowTableSplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $getServiceNowTableSplat.Add('ServiceNowCredential',$ServiceNowCredential) + $getServiceNowTableSplat.Add('ServiceNowURL',$ServiceNowURL) + } + + # Perform query and return each object in the format.ps1xml format + $Result = Get-ServiceNowTable @getServiceNowTableSplat + $Result | ForEach-Object{$_.PSObject.TypeNames.Insert(0,"ServiceNow.ChangeRequest")} + $Result +} \ No newline at end of file diff --git a/PSServiceNow-ConfigurationManagement.psm1 b/ServiceNow/Public/Get-ServiceNowConfigurationItem.ps1 similarity index 69% rename from PSServiceNow-ConfigurationManagement.psm1 rename to ServiceNow/Public/Get-ServiceNowConfigurationItem.ps1 index 427c5c1..780645a 100644 --- a/PSServiceNow-ConfigurationManagement.psm1 +++ b/ServiceNow/Public/Get-ServiceNowConfigurationItem.ps1 @@ -60,23 +60,36 @@ function Get-ServiceNowConfigurationItem { $Connection ) - $Query = New-ServiceNowQuery -OrderBy $OrderBy -OrderDirection $OrderDirection -MatchExact $MatchExact -MatchContains $MatchContains + # Query Splat + $newServiceNowQuerySplat = @{ + OrderBy = $OrderBy + MatchExact = $MatchExact + OrderDirection = $OrderDirection + MatchContains = $MatchContains + } + $Query = New-ServiceNowQuery @newServiceNowQuerySplat - if ($Connection -ne $null) { - $result = Get-ServiceNowTable -Table 'cmdb_ci' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -Connection $Connection + # Table Splat + $getServiceNowTableSplat = @{ + Table = 'cmdb_ci' + Query = $Query + Limit = $Limit + DisplayValues = $DisplayValues } - elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) { - $result = Get-ServiceNowTable -Table 'cmdb_ci' -Query $Query -Limit $Limit -DisplayValues $DisplayValues -ServiceNowCredential $ServiceNowCredential -ServiceNowURL $ServiceNowURL + + # Update the Table Splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $getServiceNowTableSplat.Add('Connection',$Connection) } - else { - $result = Get-ServiceNowTable -Table 'cmdb_ci' -Query $Query -Limit $Limit -DisplayValues $DisplayValues + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $getServiceNowTableSplat.Add('ServiceNowCredential',$ServiceNowCredential) + $getServiceNowTableSplat.Add('ServiceNowURL',$ServiceNowURL) } - - # Set the default property set for the table view - $DefaultProperties = @('name', 'category', 'subcategory') - $DefaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’,[string[]]$DefaultProperties) - $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($DefaultDisplayPropertySet) - $Result | Add-Member MemberSet PSStandardMembers $PSStandardMembers - return $result -} \ No newline at end of file + # Perform query and return each object in the format.ps1xml format + $Result = Get-ServiceNowTable @getServiceNowTableSplat + $Result | ForEach-Object{$_.PSObject.TypeNames.Insert(0,"ServiceNow.ConfigurationItem")} + $Result +} diff --git a/ServiceNow/Public/Get-ServiceNowIncident.ps1 b/ServiceNow/Public/Get-ServiceNowIncident.ps1 new file mode 100644 index 0000000..1823a27 --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowIncident.ps1 @@ -0,0 +1,98 @@ +function Get-ServiceNowIncident{ + param( + # Machine name of the field to order by + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$OrderBy='opened_at', + + # Direction of ordering (Desc/Asc) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("Desc", "Asc")] + [string]$OrderDirection='Desc', + + # Maximum number of records to return + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [int]$Limit=10, + + # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchExact=@{}, + + # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchContains=@{}, + + # Whether or not to show human readable display values instead of machine values + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("true","false", "all")] + [string]$DisplayValues='true', + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + # Query Splat + $newServiceNowQuerySplat = @{ + OrderBy = $OrderBy + OrderDirection = $OrderDirection + MatchExact = $MatchExact + MatchContains = $MatchContains + } + $Query = New-ServiceNowQuery @newServiceNowQuerySplat + + # Table Splat + $getServiceNowTableSplat = @{ + Table = 'incident' + Query = $Query + Limit = $Limit + DisplayValues = $DisplayValues + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $getServiceNowTableSplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $getServiceNowTableSplat.Add('ServiceNowCredential',$ServiceNowCredential) + $getServiceNowTableSplat.Add('ServiceNowURL',$ServiceNowURL) + } + + # Perform query and return each object in the format.ps1xml format + $Result = Get-ServiceNowTable @getServiceNowTableSplat + $Result | ForEach-Object{$_.PSObject.TypeNames.Insert(0,"ServiceNow.Incident")} + $Result +} diff --git a/ServiceNow/Public/Get-ServiceNowTable.ps1 b/ServiceNow/Public/Get-ServiceNowTable.ps1 new file mode 100644 index 0000000..7c14c6d --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowTable.ps1 @@ -0,0 +1,95 @@ +function Get-ServiceNowTable +{ + [OutputType([Array])] + Param + ( + # Name of the table we're querying (e.g. incidents) + [parameter(Mandatory)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateNotNullOrEmpty()] + [string]$Table, + + # sysparm_query param in the format of a ServiceNow encoded query string (see http://wiki.servicenow.com/index.php?title=Encoded_Query_Strings) + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Query, + + # Maximum number of records to return + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [int]$Limit=10, + + # Whether or not to show human readable display values instead of machine values + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("true","false","all")] + [string]$DisplayValues='false', + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields')] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + # Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + # Get credential and ServiceNow REST URL + if ($null -ne $Connection) + { + $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force + $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) + $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' + } + elseif ($null -ne $ServiceNowCredential -and $null -ne $ServiceNowURL) + { + $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' + } + elseif((Test-ServiceNowAuthIsSet)) + { + $ServiceNowCredential = $Global:ServiceNowCredentials + $ServiceNowURL = $global:ServiceNowRESTURL + } + else + { + 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" + } + + # Populate the query + $Body = @{'sysparm_limit'=$Limit;'sysparm_display_value'=$DisplayValues} + if($Query){ + $Body.sysparm_query = $Query + } + + # Perform table query and capture results + $Uri = $ServiceNowURL + "/table/$Table" + $Result = (Invoke-RestMethod -Uri $Uri -Credential $ServiceNowCredential -Body $Body -ContentType "application/json").Result + + # Convert specific fields to DateTime format + $ConvertToDateField = @('closed_at','expected_start','follow_up','opened_at','sys_created_on','sys_updated_on','work_end','work_start') + ForEach ($SNResult in $Result) { + ForEach ($Property in $ConvertToDateField) { + If (-not [string]::IsNullOrEmpty($SNResult.$Property)) { + $SNResult.$Property = [datetime]$SNResult.$Property + } + } + } + + # Return the results + $Result +} \ No newline at end of file diff --git a/ServiceNow/Public/Get-ServiceNowUser.ps1 b/ServiceNow/Public/Get-ServiceNowUser.ps1 new file mode 100644 index 0000000..68113cd --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowUser.ps1 @@ -0,0 +1,98 @@ +function Get-ServiceNowUser{ + param( + # Machine name of the field to order by + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$OrderBy='name', + + # Direction of ordering (Desc/Asc) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("Desc", "Asc")] + [string]$OrderDirection='Desc', + + # Maximum number of records to return + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [int]$Limit=10, + + # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchExact=@{}, + + # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchContains=@{}, + + # Whether or not to show human readable display values instead of machine values + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("true","false", "all")] + [string]$DisplayValues='true', + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + # Query Splat + $newServiceNowQuerySplat = @{ + OrderBy = $OrderBy + OrderDirection = $OrderDirection + MatchExact = $MatchExact + MatchContains = $MatchContains + } + $Query = New-ServiceNowQuery @newServiceNowQuerySplat + + # Table Splat + $getServiceNowTableSplat = @{ + Table = 'sys_user' + Query = $Query + Limit = $Limit + DisplayValues = $DisplayValues + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $getServiceNowTableSplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $getServiceNowTableSplat.Add('ServiceNowCredential',$ServiceNowCredential) + $getServiceNowTableSplat.Add('ServiceNowURL',$ServiceNowURL) + } + + # Perform query and return each object in the format.ps1xml format + $Result = Get-ServiceNowTable @getServiceNowTableSplat + $Result | ForEach-Object{$_.PSObject.TypeNames.Insert(0,"ServiceNow.UserAndUserGroup")} + $Result +} diff --git a/ServiceNow/Public/Get-ServiceNowUserGroup.ps1 b/ServiceNow/Public/Get-ServiceNowUserGroup.ps1 new file mode 100644 index 0000000..24f7171 --- /dev/null +++ b/ServiceNow/Public/Get-ServiceNowUserGroup.ps1 @@ -0,0 +1,98 @@ +function Get-ServiceNowUserGroup{ + param( + # Machine name of the field to order by + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$OrderBy='name', + + # Direction of ordering (Desc/Asc) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("Desc", "Asc")] + [string]$OrderDirection='Desc', + + # Maximum number of records to return + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [int]$Limit=10, + + # Hashtable containing machine field names and values returned must match exactly (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchExact=@{}, + + # Hashtable containing machine field names and values returned rows must contain (will be combined with AND) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$MatchContains=@{}, + + # Whether or not to show human readable display values instead of machine values + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [ValidateSet("true","false", "all")] + [string]$DisplayValues='true', + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + # Query Splat + $newServiceNowQuerySplat = @{ + OrderBy = $OrderBy + OrderDirection = $OrderDirection + MatchExact = $MatchExact + MatchContains = $MatchContains + } + $Query = New-ServiceNowQuery @newServiceNowQuerySplat + + # Table Splat + $getServiceNowTableSplat = @{ + Table = 'sys_user_group' + Query = $Query + Limit = $Limit + DisplayValues = $DisplayValues + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $getServiceNowTableSplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $getServiceNowTableSplat.Add('ServiceNowCredential',$ServiceNowCredential) + $getServiceNowTableSplat.Add('ServiceNowURL',$ServiceNowURL) + } + + # Perform query and return each object in the format.ps1xml format + $Result = Get-ServiceNowTable @getServiceNowTableSplat + $Result | ForEach-Object{$_.PSObject.TypeNames.Insert(0,"ServiceNow.UserAndUserGroup")} + $Result +} diff --git a/ServiceNow/Public/New-ServiceNowIncident.ps1 b/ServiceNow/Public/New-ServiceNowIncident.ps1 new file mode 100644 index 0000000..1e6a3aa --- /dev/null +++ b/ServiceNow/Public/New-ServiceNowIncident.ps1 @@ -0,0 +1,142 @@ +function New-ServiceNowIncident{ + Param( + + # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) + [parameter(Mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Caller, + + # Short description of the incident + [parameter(Mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$ShortDescription, + + # Long description of the incident + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Description, + + # sys_id of the assignment group (use Get-ServiceNowUserGroup to retrieve this) + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$AssignmentGroup, + + # Comment to include in the ticket + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Comment, + + # Category of the incident (e.g. 'Network') + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Category, + + # Subcategory of the incident (e.g. 'Network') + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Subcategory, + + # sys_id of the configuration item of the incident + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$ConfigurationItem, + + # custom fields as hashtable + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$CustomFields, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential]$ServiceNowCredential, + + # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string]$ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable]$Connection + ) + + # Create a hash table of any defined parameters (not CustomFields) that have values + $DefinedIncidentParameters = @('AssignmentGroup','Caller','Category','Comment','ConfigurationItem','Description','ShortDescription','Subcategory') + $TableEntryValues = @{} + ForEach ($Parameter in $DefinedIncidentParameters) { + If ($null -ne $PSBoundParameters.$Parameter) { + # Turn the defined parameter name into the ServiceNow attribute name + $KeyToAdd = Switch ($Parameter) { + AssignmentGroup {'assignment_group'} + Caller {'caller_id'} + Category {'category'} + Comment {'comments'} + ConfigurationItem {'cmdb_ci'} + Description {'description'} + ShortDescription {'short_description'} + Subcategory {'subcategory'} + } + $TableEntryValues.Add($KeyToAdd,$PSBoundParameters.$Parameter) + } + } + + # Add CustomFields hash pairs to the Table Entry Values hash table + If ($null -ne $PSBoundParameters.CustomFields) { + $DuplicateTableEntryValues = ForEach ($Key in $CustomFields.Keys) { + If (($TableEntryValues.ContainsKey($Key) -eq $False)) { + # Add the unique entry to the table entry values hash table + $TableEntryValues.Add($Key,$CustomFields[$Key]) + } + Else { + # Capture the duplicate key name + $Key + } + } + } + + # Throw an error if duplicate fields were provided + If ($null -ne $DuplicateTableEntryValues) { + $DuplicateKeyList = $DuplicateTableEntryValues -join "," + Throw "Ticket fields may only be used once: $DuplicateKeyList" + } + + # Table Entry Splat + $newServiceNowTableEntrySplat = @{ + Table = 'incident' + Values = $TableEntryValues + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $newServiceNowTableEntrySplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $newServiceNowTableEntrySplat.Add('ServiceNowCredential',$ServiceNowCredential) + $newServiceNowTableEntrySplat.Add('ServiceNowURL',$ServiceNowURL) + } + + # Create the table entry + New-ServiceNowTableEntry @newServiceNowTableEntrySplat +} diff --git a/PSServiceNow.psm1 b/ServiceNow/Public/New-ServiceNowQuery.ps1 similarity index 60% rename from PSServiceNow.psm1 rename to ServiceNow/Public/New-ServiceNowQuery.ps1 index e037d46..66e4ca8 100644 --- a/PSServiceNow.psm1 +++ b/ServiceNow/Public/New-ServiceNowQuery.ps1 @@ -1,11 +1,3 @@ -function Test-ServiceNowAuthIsSet{ - if($Global:ServiceNowCredentials){ - return $true; - }else{ - return $false; - } -} - function New-ServiceNowQuery{ param( @@ -51,30 +43,3 @@ function New-ServiceNowQuery{ return $Query } - -function Set-ServiceNowAuth{ - param( - [parameter(mandatory=$true)] - [string]$url, - - [parameter(mandatory=$true)] - [System.Management.Automation.PSCredential]$Credentials - ) - $Global:ServiceNowURL = 'https://' + $url - $Global:ServiceNowRESTURL = $ServiceNowURL + '/api/now/v1' - $Global:ServiceNowCredentials = $credentials - return $true; -} - -<# -.SYNOPSIS - Cleans up the variables containing your authentication information from your PowerShell session -#> -function Remove-ServiceNowAuth{ - - Remove-Variable -Name ServiceNowURL -Scope Global - Remove-Variable -Name ServiceNowRESTURL -Scope Global - Remove-Variable -Name ServiceNowCredentials -Scope Global - - return $true; -} diff --git a/ServiceNow/Public/New-ServiceNowTableEntry.ps1 b/ServiceNow/Public/New-ServiceNowTableEntry.ps1 new file mode 100644 index 0000000..1cc721d --- /dev/null +++ b/ServiceNow/Public/New-ServiceNowTableEntry.ps1 @@ -0,0 +1,69 @@ +function New-ServiceNowTableEntry{ + Param + ( + # Name of the table we're inserting into (e.g. incidents) + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Table, + + # Hashtable of values to use as the record's properties + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$Values, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + + #Get credential and ServiceNow REST URL + if ($Connection -ne $null) + { + $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force + $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) + $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' + + } + elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) + { + $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' + } + elseif((Test-ServiceNowAuthIsSet)) + { + $ServiceNowCredential = $Global:ServiceNowCredentials + $ServiceNowURL = $global:ServiceNowRESTURL + } + else + { + 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" + } + + + $Body = $Values | ConvertTo-Json; + + #Convert to UTF8 array to support special chars such as the danish "�","�","�" + $utf8Bytes = [System.Text.Encoding]::UTf8.GetBytes($Body) + + # Fire and return + $Uri = $ServiceNowURL + "/table/$Table" + return (Invoke-RestMethod -Uri $uri -Method Post -Credential $ServiceNowCredential -Body $utf8Bytes -ContentType "application/json" -UseBasicParsing).result +} diff --git a/ServiceNow/Public/Remove-ServiceNowAuth.ps1 b/ServiceNow/Public/Remove-ServiceNowAuth.ps1 new file mode 100644 index 0000000..35ecbac --- /dev/null +++ b/ServiceNow/Public/Remove-ServiceNowAuth.ps1 @@ -0,0 +1,18 @@ +function Remove-ServiceNowAuth{ + + If (-not (Test-ServiceNowAuthIsSet)) { + Return $true + } + + Try { + Remove-Variable -Name ServiceNowURL -Scope Global -ErrorAction Stop + Remove-Variable -Name ServiceNowRESTURL -Scope Global -ErrorAction Stop + Remove-Variable -Name ServiceNowCredentials -Scope Global -ErrorAction Stop + } + Catch { + Write-Error $_ + Return $false + } + + Return $true +} diff --git a/ServiceNow/Public/Remove-ServiceNowTableEntry.ps1 b/ServiceNow/Public/Remove-ServiceNowTableEntry.ps1 new file mode 100644 index 0000000..573bbcf --- /dev/null +++ b/ServiceNow/Public/Remove-ServiceNowTableEntry.ps1 @@ -0,0 +1,62 @@ +function Remove-ServiceNowTableEntry{ +[CmdletBinding(ConfirmImpact='High')] + Param( + # sys_id of the entry we're deleting + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$SysId, + + # Table containing the entry we're deleting + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Table, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential] + $ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string] + $ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable] + $Connection + ) + + #Get credential and ServiceNow REST URL + if ($Connection -ne $null) + { + $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force + $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) + $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' + + } + elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) + { + $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' + } + elseif((Test-ServiceNowAuthIsSet)) + { + $ServiceNowCredential = $Global:ServiceNowCredentials + $ServiceNowURL = $global:ServiceNowRESTURL + } + else + { + 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" + } + + # Fire and return + $Uri = $ServiceNowURL + "/table/$Table/$SysID" + return (Invoke-RestMethod -Uri $uri -Method Delete -Credential $ServiceNowCredential -Body $Body -ContentType "application/json").result +} diff --git a/ServiceNow/Public/Set-ServiceNowAuth.ps1 b/ServiceNow/Public/Set-ServiceNowAuth.ps1 new file mode 100644 index 0000000..08f3a74 --- /dev/null +++ b/ServiceNow/Public/Set-ServiceNowAuth.ps1 @@ -0,0 +1,13 @@ +function Set-ServiceNowAuth{ + param( + [parameter(mandatory=$true)] + [string]$url, + + [parameter(mandatory=$true)] + [System.Management.Automation.PSCredential]$Credentials + ) + $Global:ServiceNowURL = 'https://' + $url + $Global:ServiceNowRESTURL = $ServiceNowURL + '/api/now/v1' + $Global:ServiceNowCredentials = $credentials + return $true; +} diff --git a/ServiceNow/Public/Test-ServiceNowAuthIsSet.ps1 b/ServiceNow/Public/Test-ServiceNowAuthIsSet.ps1 new file mode 100644 index 0000000..c388819 --- /dev/null +++ b/ServiceNow/Public/Test-ServiceNowAuthIsSet.ps1 @@ -0,0 +1,7 @@ +function Test-ServiceNowAuthIsSet{ + if($Global:ServiceNowCredentials){ + return $true; + }else{ + return $false; + } +} diff --git a/ServiceNow/Public/Update-ServiceNowChangeRequest.ps1 b/ServiceNow/Public/Update-ServiceNowChangeRequest.ps1 new file mode 100644 index 0000000..ef733b2 --- /dev/null +++ b/ServiceNow/Public/Update-ServiceNowChangeRequest.ps1 @@ -0,0 +1,53 @@ +<# +.EXAMPLE + Update-ServiceNowChangeRequest -Values @{ 'state' = 3 } -SysId +#> +function Update-ServiceNowChangeRequest +{ + Param( + # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) + [parameter(Mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$SysId, + + # Hashtable of values to use as the record's properties + [parameter(Mandatory=$true)] + [hashtable]$Values, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [PSCredential]$ServiceNowCredential, + + # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$ServiceNowURL, + + # Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [Hashtable]$Connection + ) + + $updateServiceNowTableEntrySplat = @{ + SysId = $SysId + Table = 'change_request' + Values = $Values + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $updateServiceNowTableEntrySplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $updateServiceNowTableEntrySplat.Add('ServiceNowCredential',$ServiceNowCredential) + $updateServiceNowTableEntrySplat.Add('ServiceNowURL',$ServiceNowURL) + } + + Update-ServiceNowTableEntry @updateServiceNowTableEntrySplat +} \ No newline at end of file diff --git a/ServiceNow/Public/Update-ServiceNowIncident.ps1 b/ServiceNow/Public/Update-ServiceNowIncident.ps1 new file mode 100644 index 0000000..9ed4f43 --- /dev/null +++ b/ServiceNow/Public/Update-ServiceNowIncident.ps1 @@ -0,0 +1,49 @@ +function Update-ServiceNowIncident { + Param + ( # sys_id of the caller of the incident (user Get-ServiceNowUser to retrieve this) + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields', mandatory=$true)] + [parameter(ParameterSetName='UseConnectionObject', mandatory=$true)] + [parameter(ParameterSetName='SetGlobalAuth', mandatory=$true)] + [string]$SysId, + + # Hashtable of values to use as the record's properties + [parameter(mandatory=$true)] + [hashtable]$Values, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential]$ServiceNowCredential, + + # The URL for the ServiceNow instance being used (eg: instancename.service-now.com) + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string]$ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable]$Connection + ) + + $updateServiceNowTableEntrySplat = @{ + SysId = $SysId + Table = 'incident' + Values = $Values + } + + # Update the splat if the parameters have values + if ($null -ne $PSBoundParameters.Connection) + { + $updateServiceNowTableEntrySplat.Add('Connection',$Connection) + } + elseif ($null -ne $PSBoundParameters.ServiceNowCredential -and $null -ne $PSBoundParameters.ServiceNowURL) + { + $updateServiceNowTableEntrySplat.Add('ServiceNowCredential',$ServiceNowCredential) + $updateServiceNowTableEntrySplat.Add('ServiceNowURL',$ServiceNowURL) + } + + Update-ServiceNowTableEntry @updateServiceNowTableEntrySplat +} + diff --git a/ServiceNow/Public/Update-ServiceNowTableEntry.ps1 b/ServiceNow/Public/Update-ServiceNowTableEntry.ps1 new file mode 100644 index 0000000..c38e433 --- /dev/null +++ b/ServiceNow/Public/Update-ServiceNowTableEntry.ps1 @@ -0,0 +1,71 @@ +function Update-ServiceNowTableEntry{ +[CmdletBinding(ConfirmImpact='High')] + Param( + # sys_id of the entry we're deleting + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$SysId, + + # Table containing the entry we're deleting + [parameter(mandatory=$true)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [string]$Table, + + # Credential used to authenticate to ServiceNow + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [PSCredential]$ServiceNowCredential, + + # The URL for the ServiceNow instance being used + [Parameter(ParameterSetName='SpecifyConnectionFields', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [string]$ServiceNowURL, + + #Azure Automation Connection object containing username, password, and URL for the ServiceNow instance + [Parameter(ParameterSetName='UseConnectionObject', Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [Hashtable]$Connection, + + # Hashtable of values to use as the record's properties + [parameter(mandatory=$false)] + [parameter(ParameterSetName='SpecifyConnectionFields')] + [parameter(ParameterSetName='UseConnectionObject')] + [parameter(ParameterSetName='SetGlobalAuth')] + [hashtable]$Values + ) + + #Get credential and ServiceNow REST URL + if ($Connection -ne $null) + { + $SecurePassword = ConvertTo-SecureString $Connection.Password -AsPlainText -Force + $ServiceNowCredential = New-Object System.Management.Automation.PSCredential ($Connection.Username, $SecurePassword) + $ServiceNowURL = 'https://' + $Connection.ServiceNowUri + '/api/now/v1' + + } + elseif ($ServiceNowCredential -ne $null -and $ServiceNowURL -ne $null) + { + $ServiceNowURL = 'https://' + $ServiceNowURL + '/api/now/v1' + } + elseif((Test-ServiceNowAuthIsSet)) + { + $ServiceNowCredential = $Global:ServiceNowCredentials + $ServiceNowURL = $global:ServiceNowRESTURL + } + else + { + 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" + } + + $Body = $Values | ConvertTo-Json + + #Convert to UTF8 array to support special chars such as the danish "�","�","�" + $utf8Bytes = [System.Text.Encoding]::UTf8.GetBytes($Body) + + # Fire and return + $Uri = $ServiceNowURL + "/table/$Table/$SysID" + return (Invoke-RestMethod -Uri $uri -Method Patch -Credential $ServiceNowCredential -Body $utf8Bytes -ContentType "application/json").result +} \ No newline at end of file diff --git a/ServiceNow/ServiceNow-Automation.json b/ServiceNow/ServiceNow-Automation.json new file mode 100644 index 0000000..ceabcdf --- /dev/null +++ b/ServiceNow/ServiceNow-Automation.json @@ -0,0 +1,32 @@ +{ + "ConnectionFields": [ + { + "IsEncrypted": false, + "IsOptional": false, + "Name": "Username", + "TypeName": "System.String" + }, + { + "IsEncrypted": true, + "IsOptional": false, + "Name": "Password", + "TypeName": "System.String" + }, + { + "IsEncrypted": false, + "IsOptional": false, + "Name": "ServiceNowUri", + "TypeName": "System.String" + }, + { + "IsEncrypted": false, + "IsOptional": true, + "Name": "APIVersion", + "TypeName": "System.String" + } + + ], + + "ConnectionTypeName": "ServiceNow", + "IntegrationModuleName": "ServiceNow" +} diff --git a/ServiceNow/ServiceNow.format.ps1xml b/ServiceNow/ServiceNow.format.ps1xml new file mode 100644 index 0000000..06d8572 --- /dev/null +++ b/ServiceNow/ServiceNow.format.ps1xml @@ -0,0 +1,192 @@ + + + + + ServiceNow.ChangeRequest + + ServiceNow.ChangeRequest + + + + + + 10 + + + + 25 + + + + 8 + + + + 15 + + + + 10 + + + + 20 + + + + 21 + + + + + + + number + + + short_description + + + state + + + + $_.assigned_to.display_value + + + + approval + + + + $_.cmdb_ci.display_value + + + + opened_at + + + + + + + + ServiceNow.ConfigurationItem + + ServiceNow.ConfigurationItem + + + + + + 60 + + + + 20 + + + + 20 + + + + + + + name + + + category + + + subcategory + + + + + + + + ServiceNow.Incident + + ServiceNow.Incident + + + + + + 10 + + + + 20 + + + + 15 + + + + 60 + + + + + + + number + + + opened_at + + + state + + + short_description + + + + + + + + ServiceNow.UserAndUserGroup + + ServiceNow.UserAndUserGroup + + + + + + 40 + + + + 40 + + + + 40 + + + + + + + name + + + email + + + sys_id + + + + + + + + \ No newline at end of file diff --git a/PSServiceNow.psd1 b/ServiceNow/ServiceNow.psd1 similarity index 82% rename from PSServiceNow.psd1 rename to ServiceNow/ServiceNow.psd1 index af7de64..93c4a36 100644 --- a/PSServiceNow.psd1 +++ b/ServiceNow/ServiceNow.psd1 @@ -1,5 +1,5 @@ # -# Module manifest for module 'PSServiceNow' +# Module manifest for module 'ServiceNow' # # Generated by: Sam Martin # @@ -9,10 +9,10 @@ @{ # Script module or binary module file associated with this manifest. -RootModule = 'PSServiceNow.psm1' +RootModule = 'ServiceNow.psm1' # Version number of this module. -ModuleVersion = '0.1.14' +ModuleVersion = '1.0.0' # ID used to uniquely identify this module GUID = 'b90d67da-f8d0-4406-ad74-89d169cd0633' @@ -60,19 +60,13 @@ Description = 'This module provides cmdlets allowing you to retrieve information # TypesToProcess = @() # Format files (.ps1xml) to be loaded when importing this module -FormatsToProcess = @('PSServiceNow.format.ps1xml') +FormatsToProcess = @('ServiceNow.format.ps1xml') # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -NestedModules = @( - 'PSServiceNow-Tables.psm1', - 'PSServiceNow-Incidents.psm1', - 'PSServiceNow-Users.psm1', - 'PSServiceNow-ConfigurationManagement.psm1', - 'PSServiceNow-Changes.psm1' -) +NestedModules = @() # Functions to export from this module -FunctionsToExport = '*' +FunctionsToExport = @('Get-ServiceNowChangeRequest','Get-ServiceNowConfigurationItem','Get-ServiceNowIncident','Get-ServiceNowTable','Get-ServiceNowUser','Get-ServiceNowUserGroup','New-ServiceNowIncident','New-ServiceNowQuery','New-ServiceNowTableEntry','Remove-ServiceNowAuth','Remove-ServiceNowTableEntry','Set-ServiceNowAuth','Test-ServiceNowAuthIsSet','Update-ServiceNowChangeRequest','Update-ServiceNowIncident','Update-ServiceNowTableEntry') # List of all modules packaged with this module # ModuleList = @() @@ -105,4 +99,3 @@ PrivateData = @{ # DefaultCommandPrefix = '' } - diff --git a/ServiceNow/ServiceNow.psm1 b/ServiceNow/ServiceNow.psm1 new file mode 100644 index 0000000..c666d6e --- /dev/null +++ b/ServiceNow/ServiceNow.psm1 @@ -0,0 +1,22 @@ +#Requires -Version 3.0 +[cmdletbinding()] +param() + +Write-Verbose $PSScriptRoot + +Write-Verbose 'Import everything in sub folders folder' +foreach($Folder in @('Private', 'Public')) +{ + $Root = Join-Path -Path $PSScriptRoot -ChildPath $Folder + if(Test-Path -Path $Root) + { + Write-Verbose "processing folder $Root" + $Files = Get-ChildItem -Path $Root -Filter *.ps1 -Recurse + + # dot source each file + $Files | Where-Object{ $_.name -NotLike '*.Tests.ps1'} | + ForEach-Object {Write-Verbose $_.basename; . $PSItem.FullName} + } +} + +Export-ModuleMember -Function (Get-ChildItem -Path "$PSScriptRoot\Public\*.ps1").BaseName \ No newline at end of file diff --git a/Tests/ServiceNow.Tests.ps1 b/Tests/ServiceNow.Tests.ps1 new file mode 100644 index 0000000..c438e45 --- /dev/null +++ b/Tests/ServiceNow.Tests.ps1 @@ -0,0 +1,130 @@ +$projectRoot = Resolve-Path "$PSScriptRoot\.." +$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") +$moduleName = Split-Path $moduleRoot -Leaf +$DefaultsFile = Join-Path $projectRoot "Tests\$($ModuleName).Pester.Defaults.json" + +# Load defaults from file (merging into $global:ServiceNowPesterTestDefaults) +if(Test-Path $DefaultsFile){ + $defaults = @{} + # Add properties to the defaults hash + (Get-Content $DefaultsFile | Out-String | ConvertFrom-Json).psobject.properties | ForEach-Object { + $defaults."$($_.Name)" = $_.Value + } + + # Prompt for credentials + $defaults.Creds = if($defaults.Creds){$defaults.Creds}else{Get-Credential} + + $global:ServiceNowPesterTestDefaults = $defaults +}else{ + # Write example file + @{ + ServiceNowURL = 'testingurl.service-now.com' + TestCategory = 'Internal' + TestUserGroup = 'e9e9a2406f4c35001855fa0dba3ee4f3' + TestUser = "7a4b573a6f3725001855fa0dba3ee485" + } | ConvertTo-Json | Set-Content $DefaultsFile + Write-Error "$DefaultsFile does not exist. Created example file. Please populate with your values" + Return +} + +# Load the module (unload it first in case we've made changes since loading it previously) +Remove-Module $ModuleName -ErrorAction SilentlyContinue +Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -Force + +Describe "ServiceNow-Module" { + If (Test-ServiceNowAuthisSet) { + Remove-ServiceNowAuth | Should -Be $True + } + + It "Test-ServiceNowAuthIsSet not set" { + Test-ServiceNowAuthIsSet | Should -Be $false + } + + It "Set-ServiceNowAuth works" { + Set-ServiceNowAuth -url $defaults.ServiceNowURL -Credentials $defaults.Creds | Should -Be $true + } + + It "Test-ServiceNowAuthIsSet set" { + Test-ServiceNowAuthIsSet | Should -Be $true + } + + It "New-ServiceNowIncident (and by extension New-ServiceNowTableEntry) works" { + $ShortDescription = "Testing Ticket Creation with Pester" + $newServiceNowIncidentSplat = @{ + Caller = $Defaults.TestUser + ShortDescription = $ShortDescription + Description = "Long description" + AssignmentGroup = $Defaults.TestUserGroup + Comment = "Comment" + Category = $Defaults.TestCategory + SubCategory = $Defaults.TestSubcategory + ConfigurationItem = $Defaults.TestConfigurationIte + } + $TestTicket = New-ServiceNowIncident @newServiceNowIncidentSplat + + $TestTicket.short_description | Should -Be $ShortDescription + } + + It "Get-ServiceNowTable works" { + # There should be one or more incidents returned + (Get-ServiceNowTable -Table 'incident' -Query 'ORDERBYDESCopened_at').Count -gt 0 | Should -Match $true + } + + It "Get-ServiceNowIncident works" { + # There should be one or more incidents returned + (Get-ServiceNowIncident).Count -gt 0 | Should -Match $true + } + + It "Update-ServiceNowIncident works" { + $ShortDescription = "Testing Ticket Update with Pester" + $newServiceNowIncidentSplat = @{ + Caller = $Defaults.TestUser + ShortDescription = $ShortDescription + Description = "Long description" + AssignmentGroup = $Defaults.TestUserGroup + Comment = "Comment" + Category = $Defaults.TestCategory + SubCategory = $Defaults.TestSubcategory + ConfigurationItem = $Defaults.TestConfigurationItem + + } + $TestTicket = New-ServiceNowIncident @newServiceNowIncidentSplat + + $TestTicket.short_description | Should -Be $ShortDescription + + $Values = + @{ + 'short_description' = 'Ticket Updated with Pester' + 'description' = 'Even Longer Description' + } + + Update-ServiceNowIncident -SysId $TestTicket.sys_id -Values $Values + + $TestTicket = Get-ServiceNowIncident -MatchExact @{sys_id=$TestTicket.sys_id} + $TestTicket.short_description | Should -Be "Ticket Updated with Pester" + $TestTicket.description | Should -Be "Even Longer Description" + } + + It "Get-ServiceNowUserGroup works" { + # There should be one or more user groups returned + (Get-ServiceNowUserGroup).Count -gt 0 | Should -Match $true + } + + It "Get-ServiceNowUser works" { + # There should be one or more user groups returned + (Get-ServiceNowUser).Count -gt 0 | Should -Match $true + } + + It "Get-ServiceNowConfigurationItem works" { + # There should be one or more configuration items returned + (Get-ServiceNowConfigurationItem).Count -gt 0 | Should -Match $true + } + + It "Get-ServiceNowChangeRequest works" { + (Get-ServiceNowChangeRequest).Count -gt 0 | Should -Match $true + } + + It "Remove-ServiceNowAuth works" { + Remove-ServiceNowAuth | Should be $true + } +}