|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Export table records to a file |
| 4 | +
|
| 5 | +.DESCRIPTION |
| 6 | + Export table records in csv, xml, xls, xlsx, or pdf format. |
| 7 | + You can filter, sort, and choose specific properties to include in the report. |
| 8 | + Only basic authentication is supported. |
| 9 | + Export configurations, eg. row limit, can be found at System Properties->Import Export. |
| 10 | +
|
| 11 | +.PARAMETER Table |
| 12 | + Name of the table to be queried, by either table name or class name. Use tab completion for list of known tables. |
| 13 | + You can also provide any table name ad hoc. |
| 14 | +
|
| 15 | +.PARAMETER Id |
| 16 | + Either the record sys_id or number. |
| 17 | + If providing just an Id, not with Table, the Id prefix will be looked up to find the table name. |
| 18 | +
|
| 19 | +.PARAMETER Property |
| 20 | + Return one or more specific fields otherwise all fields will be returned |
| 21 | +
|
| 22 | +.PARAMETER Filter |
| 23 | + Array or multidimensional array of fields and values to filter on. |
| 24 | + Each array should be of the format @(field, comparison operator, value) separated by a join, either 'and', 'or', or 'group'. |
| 25 | + For a complete list of comparison operators, see $script:ServiceNowOperator and use Name in your filter. |
| 26 | + See the examples. |
| 27 | +
|
| 28 | +.PARAMETER Sort |
| 29 | + Array or multidimensional array of fields to sort on. |
| 30 | + Each array should be of the format @(field, asc/desc). |
| 31 | +
|
| 32 | +.PARAMETER Path |
| 33 | + Path to output file including the file name. |
| 34 | + File extension must be either .csv, .xml, .xls, .xlsx, or .pdf. |
| 35 | +
|
| 36 | +.PARAMETER ServiceNowSession |
| 37 | + ServiceNow session created by New-ServiceNowSession. Will default to script-level variable $ServiceNowSession. |
| 38 | +
|
| 39 | +.EXAMPLE |
| 40 | + Export-ServiceNowRecord -Id RITM0010001 -Path .\out.pdf |
| 41 | + Export a specific record by number |
| 42 | +
|
| 43 | +.EXAMPLE |
| 44 | + Export-ServiceNowRecord -Table incident -Filter @('assigned_to.name', '-like', 'greg') -Path .\out.xlsx |
| 45 | + Export incident records where the assigned to user's name contains greg |
| 46 | +
|
| 47 | +.EXAMPLE |
| 48 | + $filter = @('state', '-eq', '1'), |
| 49 | + '-and', |
| 50 | + @('short_description','-like', 'powershell'), |
| 51 | + '-group', |
| 52 | + @('state', '-eq', '2') |
| 53 | + PS > Export-ServiceNowRecord -Table incident -Filter $filter -Path .\out.pdf |
| 54 | + Export incident records where state is New and short description contains the word powershell or state is In Progress. |
| 55 | + The first 2 filters are combined and then or'd against the last. |
| 56 | +
|
| 57 | +.EXAMPLE |
| 58 | + Export-ServiceNowRecord -Table 'Incident' -Filter @('opened_at', '-between', (Get-Date).AddMonths(-24), (get-date).AddMonths(-12)) -Path .\out.pdf |
| 59 | + Export incident records that were opened between 1 and 2 years ago |
| 60 | +
|
| 61 | +.EXAMPLE |
| 62 | + Export-ServiceNowRecord -Table incident -Filter @('state', '-eq', '1') -Sort @('opened_at', 'desc') -Path .\out.pdf |
| 63 | + Export incident records where state equals New and sort by the field opened_at descending |
| 64 | +
|
| 65 | +.LINK |
| 66 | + https://docs.servicenow.com/bundle/rome-platform-user-interface/page/use/navigation/task/navigate-using-url.html |
| 67 | +
|
| 68 | +.LINK |
| 69 | + https://docs.servicenow.com/bundle/sandiego-platform-administration/page/administer/exporting-data/task/t_ExportDirectlyFromTheURL.html#t_ExportDirectlyFromTheURL |
| 70 | +#> |
| 71 | +function Export-ServiceNowRecord { |
| 72 | + |
| 73 | + [CmdletBinding(DefaultParameterSetName = 'Id')] |
| 74 | + |
| 75 | + Param ( |
| 76 | + [Parameter(ParameterSetName = 'Table', Mandatory)] |
| 77 | + [Alias('sys_class_name')] |
| 78 | + [string] $Table, |
| 79 | + |
| 80 | + [Parameter(ParameterSetName = 'Id', Mandatory, Position = 0)] |
| 81 | + [Parameter(ParameterSetName = 'Table')] |
| 82 | + [ValidateScript( { |
| 83 | + if ($_ -match '^[a-zA-Z0-9]{32}$' -or $_ -match '^([a-zA-Z]+)[0-9]+$') { |
| 84 | + $true |
| 85 | + } else { |
| 86 | + throw 'Id must be either a 32 character alphanumeric, ServiceNow sysid, or prefix/id, ServiceNow number.' |
| 87 | + } |
| 88 | + })] |
| 89 | + [Alias('sys_id', 'number')] |
| 90 | + [string] $Id, |
| 91 | + |
| 92 | + [Parameter()] |
| 93 | + [Alias('Fields', 'Properties')] |
| 94 | + [string[]] $Property, |
| 95 | + |
| 96 | + [Parameter()] |
| 97 | + [System.Collections.ArrayList] $Filter, |
| 98 | + |
| 99 | + [parameter()] |
| 100 | + [ValidateNotNullOrEmpty()] |
| 101 | + [System.Collections.ArrayList] $Sort, |
| 102 | + |
| 103 | + [Parameter(Mandatory)] |
| 104 | + [ValidateScript({ |
| 105 | + $allowedExts = '.csv', '.xml', '.pdf', '.xls', '.xlsx' |
| 106 | + if ([System.IO.Path]::GetExtension($_).ToLower() -in $allowedExts ) { |
| 107 | + $true |
| 108 | + } else { |
| 109 | + throw ('File extension must be one of {0}' -f ($allowedExts -join ', ')) |
| 110 | + } |
| 111 | + })] |
| 112 | + [string] $Path, |
| 113 | + |
| 114 | + [Parameter()] |
| 115 | + [hashtable] $ServiceNowSession = $script:ServiceNowSession |
| 116 | + ) |
| 117 | + |
| 118 | + process { |
| 119 | + |
| 120 | + $newFilter = $Filter |
| 121 | + |
| 122 | + if ( $Table ) { |
| 123 | + $thisTable = $Table |
| 124 | + } |
| 125 | + |
| 126 | + if ( $Id ) { |
| 127 | + if ( $Id -match '^[a-zA-Z0-9]{32}$' ) { |
| 128 | + if ( -not $thisTable ) { |
| 129 | + throw 'Providing sys_id for -Id requires a value for -Table. Alternatively, provide an Id with a prefix, eg. INC1234567, and the table will be automatically determined.' |
| 130 | + } |
| 131 | + |
| 132 | + $newFilter = @('sys_id', '-eq', $Id) |
| 133 | + } else { |
| 134 | + if ( -not $thisTable ) { |
| 135 | + # get table name from prefix if only Id was provided |
| 136 | + $idPrefix = ($Id | Select-String -Pattern '^([a-zA-Z]+)([0-9]+$)').Matches.Groups[1].Value.ToLower() |
| 137 | + Write-Debug "Id prefix is $idPrefix" |
| 138 | + $thisTable = $script:ServiceNowTable | Where-Object { $_.NumberPrefix -and $idPrefix -eq $_.NumberPrefix } | Select-Object -ExpandProperty Name |
| 139 | + if ( -not $thisTable ) { |
| 140 | + throw ('The prefix for Id ''{0}'' was not found and the appropriate table cannot be determined. Known prefixes are {1}. Please provide a value for -Table.' -f $Id, ($ServiceNowTable.NumberPrefix.Where( { $_ }) -join ', ')) |
| 141 | + } |
| 142 | + } |
| 143 | + $newFilter = @('number', '-eq', $Id) |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + $params = Get-ServiceNowAuth -S $ServiceNowSession |
| 148 | + $params.Body = @{ |
| 149 | + 'sysparm_query' = (New-ServiceNowQuery -Filter $newFilter -Sort $Sort) |
| 150 | + } |
| 151 | + |
| 152 | + if ($Property) { |
| 153 | + $params.Body.sysparm_fields = ($Property -join ',').ToLower() |
| 154 | + } |
| 155 | + |
| 156 | + $params.OutFile = $Path |
| 157 | + |
| 158 | + # need to tell SN the format, get it from file extension |
| 159 | + $format = [System.IO.Path]::GetExtension($Path).Replace('.', '').ToUpper() |
| 160 | + |
| 161 | + # only exception to 'extension is the format' rule |
| 162 | + if ( $format -eq 'XLS' ) { $format = 'EXCEL' } |
| 163 | + |
| 164 | + $params.Uri = 'https://{0}/{1}_list.do?{2}' -f $ServiceNowSession.Domain, $thisTable, $format |
| 165 | + |
| 166 | + Write-Verbose ($params | ConvertTo-Json) |
| 167 | + Invoke-RestMethod @params |
| 168 | + |
| 169 | + } |
| 170 | +} |
0 commit comments