functions/Get-WUGDeviceReport.ps1
|
<#
.SYNOPSIS Retrieves performance and availability reports for one or more WhatsUp Gold devices. .DESCRIPTION Get-WUGDeviceReport queries the WhatsUp Gold REST API for device-level reports across multiple report types such as CPU, memory, disk, interface, ping, and state change. Results are automatically paginated. Supports sorting, grouping, thresholds, time ranges, and business hours filtering. .PARAMETER DeviceId One or more device IDs to retrieve reports for. Accepts pipeline input and the alias 'id'. Required. .PARAMETER ReportType The type of report to retrieve. Required. Valid values: Cpu, Disk, DiskSpaceFree, Interface, InterfaceDiscards, InterfaceErrors, InterfaceTraffic, Memory, PingAvailability, PingResponseTime, StateChange. .PARAMETER Range The time range preset for the report. Valid values: today, lastPolled, yesterday, lastWeek, lastMonth, lastQuarter, weekToDate, monthToDate, quarterToDate, lastNSeconds, lastNMinutes, lastNHours, lastNDays, lastNWeeks, lastNMonths, custom. .PARAMETER RangeStartUtc The start date/time in UTC for a custom time range. Used when Range is set to 'custom'. .PARAMETER RangeEndUtc The end date/time in UTC for a custom time range. Used when Range is set to 'custom'. .PARAMETER RangeN The number of time units for lastN* range types (e.g., lastNHours with RangeN=4 means last 4 hours). Default: 1. .PARAMETER SortBy The column to sort results by. Valid values depend on the ReportType selected. .PARAMETER SortByDir The sort direction. Valid values: asc, desc. Default: desc. .PARAMETER GroupBy The column to group results by. Valid values depend on the ReportType selected. .PARAMETER GroupByDir The group sort direction. Valid values: asc, desc. .PARAMETER ApplyThreshold Whether to apply a threshold filter to the results. Valid values: true, false. .PARAMETER OverThreshold When ApplyThreshold is true, determines whether to return values over or under the threshold. Valid values: true, false. .PARAMETER ThresholdValue The numeric threshold value to filter against. Default: 0.0. .PARAMETER BusinessHoursId The ID of a business hours profile to restrict the report to. Default: 0 (all hours). .PARAMETER RollupByDevice Whether to roll up (aggregate) results per device. Valid values: true, false. .PARAMETER PageId The page identifier for retrieving a specific page of paginated results. .PARAMETER Limit The maximum number of results per page. Valid range: 0-250. Default: 50. .EXAMPLE Get-WUGDeviceReport -DeviceId 1 -ReportType Cpu -Range lastWeek Returns the CPU utilization report for device 1 over the last week. .EXAMPLE Get-WUGDeviceReport -DeviceId 1,2,3 -ReportType Memory -Range lastNHours -RangeN 8 Returns the memory utilization report for devices 1, 2, and 3 over the last 8 hours. .EXAMPLE Get-WUGDeviceReport -DeviceId 5 -ReportType PingAvailability -Range custom -RangeStartUtc '2026-03-01T00:00:00Z' -RangeEndUtc '2026-03-06T00:00:00Z' Returns the ping availability report for device 5 within a custom UTC date range. .EXAMPLE 1..10 | Get-WUGDeviceReport -ReportType StateChange -Range today -SortBy deviceName -SortByDir asc Pipes device IDs 1 through 10 and retrieves today's state change report sorted by device name ascending. .EXAMPLE Get-WUGDeviceReport -DeviceId 3 -ReportType Disk -Range lastMonth -ApplyThreshold true -OverThreshold true -ThresholdValue 90 Returns disk utilization data for device 3 over the last month where values exceed 90%. .NOTES Author: Jason Alberino (jason@wug.ninja) Reference: https://docs.ipswitch.com/NM/WhatsUpGold2024/02_Guides/rest_api/#tag/Device-Reports #> function Get-WUGDeviceReport { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('id')] [int[]]$DeviceId, [Parameter(Mandatory = $true)] [ValidateSet("Cpu", "Disk", "DiskSpaceFree", "Interface", "InterfaceDiscards", "InterfaceErrors", "InterfaceTraffic", "Memory", "PingAvailability", "PingResponseTime", "StateChange")] [string]$ReportType, [ValidateSet("today", "lastPolled", "yesterday", "lastWeek", "lastMonth", "lastQuarter", "weekToDate", "monthToDate", "quarterToDate", "lastNSeconds", "lastNMinutes", "lastNHours", "lastNDays", "lastNWeeks", "lastNMonths", "custom")] [string]$Range, [string]$RangeStartUtc, [string]$RangeEndUtc, [int]$RangeN = 1, [string]$SortBy, [ValidateSet("asc", "desc")] [string]$SortByDir = "desc", [string]$GroupBy, [ValidateSet("asc", "desc")] [string]$GroupByDir, [ValidateSet("true", "false")] [string]$ApplyThreshold, [ValidateSet("true", "false")] [string]$OverThreshold, [double]$ThresholdValue = 0.0, [int]$BusinessHoursId = 0, [ValidateSet("true", "false")] [string]$RollupByDevice, [string]$PageId, [ValidateRange(0, 250)] [int]$Limit = 50 ) begin { if (-not $global:WhatsUpServerBaseURI) { Write-Error "WhatsUpServerBaseURI is not set. Please run Connect-WUGServer to establish a connection." return } Write-Verbose "Starting Get-WUGDeviceReport" $baseUri = "${global:WhatsUpServerBaseURI}/api/v1" # Map ReportType to API endpoint and valid SortBy/GroupBy values $reportConfig = @{ "Cpu" = @{ Endpoint = "cpu-utilization" SortBy = @("defaultColumn", "id", "deviceName", "cpu", "cpuId", "pollTimeUtc", "timeFromLastPollSeconds", "minPercent", "maxPercent", "avgPercent") GroupBy = @("noGrouping", "id", "deviceName", "cpu", "cpuId", "pollTimeUtc", "timeFromLastPollSeconds", "minPercent", "maxPercent", "avgPercent") } "Disk" = @{ Endpoint = "disk-utilization" SortBy = @("defaultColumn", "id", "deviceName", "disk", "diskId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "minUsed", "maxUsed", "avgUsed", "avgFree", "minPercent", "maxPercent", "avgPercent") GroupBy = @("noGrouping", "id", "deviceName", "disk", "diskId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "mi", "ma", "av") } "DiskSpaceFree" = @{ Endpoint = "disk-free-space" SortBy = @("defaultColumn", "id", "deviceName", "disk", "diskId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "minFree", "maxFree", "avgFree") GroupBy = @("noGrouping", "id", "deviceName", "disk", "diskId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "minFree", "maxFree", "avgFree") } "Interface" = @{ Endpoint = "interface-utilization" SortBy = @("defaultColumn", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") GroupBy = @("noGrouping", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") } "InterfaceDiscards" = @{ Endpoint = "interface-discards" SortBy = @("defaultColumn", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") GroupBy = @("noGrouping", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") } "InterfaceErrors" = @{ Endpoint = "interface-errors" SortBy = @("defaultColumn", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") GroupBy = @("noGrouping", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") } "InterfaceTraffic" = @{ Endpoint = "interface-traffic" SortBy = @("defaultColumn", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") GroupBy = @("noGrouping", "id", "deviceName", "interfaceName", "interfaceId", "pollTimeUtc", "timeFromLastPollSeconds", "rxMin", "rxMax", "rxAvg", "rxTotal", "txMin", "txMax", "txAvg", "txTotal", "totalAvg") } "Memory" = @{ Endpoint = "memory-utilization" SortBy = @("defaultColumn", "id", "deviceName", "memory", "memoryId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "minUsed", "maxUsed", "avgUsed", "minPercent", "maxPercent", "avgPercent") GroupBy = @("noGrouping", "id", "deviceName", "memory", "memoryId", "pollTimeUtc", "timeFromLastPollSeconds", "size", "minUsed", "maxUsed", "avgUsed", "minPercent", "maxPercent", "avgPercent") } "PingAvailability" = @{ Endpoint = "ping-availability" SortBy = @("defaultColumn", "id", "deviceName", "interfaceId", "interfaceName", "packetsLost", "packetsSent", "percentAvailable", "percentPacketLoss", "totalTimeMinutes", "timeUnavailableMinutes", "pollTimeUtc", "timeFromLastPollSeconds") GroupBy = @("noGrouping", "id", "deviceName", "interfaceId", "interfaceName", "packetsLost", "packetsSent", "percentAvailable", "percentPacketLoss", "totalTimeMinutes", "timeUnavailableMinutes", "pollTimeUtc", "timeFromLastPollSeconds") } "PingResponseTime" = @{ Endpoint = "ping-response-time" SortBy = @("defaultColumn", "id", "deviceName", "interfaceId", "interfaceName", "minMilliSec", "maxMilliSec", "avgMilliSec", "pollTimeUtc", "timeFromLastPollSeconds") GroupBy = @("noGrouping", "id", "deviceName", "interfaceId", "interfaceName", "minMilliSec", "maxMilliSec", "avgMilliSec", "pollTimeUtc", "timeFromLastPollSeconds") } "StateChange" = @{ Endpoint = "state-change" SortBy = @("defaultColumn", "deviceName", "monitorTypeName", "stateName", "startTimeUtc", "endTimeUtc", "totalSeconds", "result") GroupBy = @("noGrouping", "deviceName", "monitorTypeName", "stateName", "startTimeUtc", "endTimeUtc", "totalSeconds", "result") } } $config = $reportConfig[$ReportType] $reportEndpoint = $config.Endpoint # Validate SortBy against the allowed values for this report type if ($SortBy -and $SortBy -notin $config.SortBy) { Write-Error "Invalid SortBy value '$SortBy' for report type '$ReportType'. Valid values: $($config.SortBy -join ', ')" return } # Validate GroupBy against the allowed values for this report type if ($GroupBy -and $GroupBy -notin $config.GroupBy) { Write-Error "Invalid GroupBy value '$GroupBy' for report type '$ReportType'. Valid values: $($config.GroupBy -join ', ')" return } $queryParams = @{} if ($Range) { $queryParams["range"] = $Range } if ($RangeStartUtc) { $queryParams["rangeStartUtc"] = $RangeStartUtc } if ($RangeEndUtc) { $queryParams["rangeEndUtc"] = $RangeEndUtc } if ($PSBoundParameters.ContainsKey('RangeN')) { $queryParams["rangeN"] = $RangeN } if ($SortBy) { $queryParams["sortBy"] = $SortBy } if ($SortByDir) { $queryParams["sortByDir"] = $SortByDir } if ($GroupBy) { $queryParams["groupBy"] = $GroupBy } if ($GroupByDir) { $queryParams["groupByDir"] = $GroupByDir } if ($PSBoundParameters.ContainsKey('ApplyThreshold')) { $queryParams["applyThreshold"] = $ApplyThreshold } if ($PSBoundParameters.ContainsKey('OverThreshold')) { $queryParams["overThreshold"] = $OverThreshold } if ($ThresholdValue -ne $null) { $queryParams["thresholdValue"] = $ThresholdValue } if ($BusinessHoursId) { $queryParams["businessHoursId"] = $BusinessHoursId } if ($PSBoundParameters.ContainsKey('RollupByDevice')) { $queryParams["rollupByDevice"] = $RollupByDevice } if ($PageId) { $queryParams["pageId"] = $PageId } if ($Limit) { $queryParams["limit"] = $Limit } $queryString = ($queryParams.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "&" $collectedDeviceIds = @() $finalOutput = @() } process { foreach ($id in $DeviceId) { $collectedDeviceIds += $id } } end { # If no DeviceId was specified, fetch all device IDs if ($collectedDeviceIds.Count -eq 0) { Write-Verbose "No DeviceId specified. Fetching all device IDs via Get-WUGDevice." $allDevices = Get-WUGDevice -View id if ($allDevices) { $collectedDeviceIds = @($allDevices.id) } if ($collectedDeviceIds.Count -eq 0) { Write-Warning "No devices found." return } Write-Verbose "Found $($collectedDeviceIds.Count) devices." } $totalDevices = $collectedDeviceIds.Count $currentDeviceIndex = 0 foreach ($id in $collectedDeviceIds) { $currentDeviceIndex++ $percentCompleteDevices = [Math]::Round(($currentDeviceIndex / $totalDevices) * 100, 2) Write-Progress -Id 1 -Activity "Fetching $ReportType report for $totalDevices devices" -Status "Processing Device $currentDeviceIndex of $totalDevices (DeviceID: $id)" -PercentComplete $percentCompleteDevices $currentPageId = $null $pageCount = 0 do { if ($currentPageId) { $uri = "$baseUri/devices/$id/reports/$reportEndpoint`?pageId=$currentPageId" if ($queryString) { $uri += "&$queryString" } } else { $uri = "$baseUri/devices/$id/reports/$reportEndpoint" if ($queryString) { $uri += "?$queryString" } } Write-Verbose "API Call: $uri" try { $result = Get-WUGAPIResponse -Uri $uri -Method "GET" $finalOutput += $result.data $currentPageId = $result.paging.nextPageId $pageCount++ if ($result.paging.totalPages) { $percentCompletePages = ($pageCount / $result.paging.totalPages) * 100 Write-Progress -Id 2 -Activity "Fetching $ReportType report for DeviceID: $id" -Status "Page $pageCount of $($result.paging.totalPages)" -PercentComplete $percentCompletePages } else { Write-Progress -Id 2 -Activity "Fetching $ReportType report for DeviceID: $id" -Status "Processing page $pageCount" -PercentComplete 0 } } catch { Write-Error "Failed to fetch $ReportType report for device $id. Error: $_" $currentPageId = $null } } while ($currentPageId) Write-Progress -Id 2 -Activity "Fetching $ReportType report for DeviceID: $id" -Status "Completed" -Completed } Write-Progress -Id 1 -Activity "Fetching $ReportType report for $totalDevices devices" -Status "All devices processed" -Completed return $finalOutput } } # SIG # Begin signature block # MIIVlwYJKoZIhvcNAQcCoIIViDCCFYQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAsJlljFEckHWTR # eL+SnxbiBrvxZYEwtg2lHGcQlbryhKCCEdMwggVvMIIEV6ADAgECAhBI/JO0YFWU # jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI # DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM # EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy # dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv # IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s # hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD # J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7 # P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme # me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz # T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q # RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz # mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc # QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T # OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/ # AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID # AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD # VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE # VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF # OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC # J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ # pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl # d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH # +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M # UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv # ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5 # NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp # BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G # CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI # ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV # DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3 # 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw # mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm # +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe # dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4 # 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM # dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY # MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU # pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV # HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG # A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG # AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl # U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0 # aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh # w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd # OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj # cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc # WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO # hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs # zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7 # 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J # KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH # j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2 # Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/ # L9Uo2bC5a4CH2RwwggY+MIIEpqADAgECAhAHnODk0RR/hc05c892LTfrMA0GCSqG # SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw # HhcNMjYwMjA5MDAwMDAwWhcNMjkwNDIxMjM1OTU5WjBVMQswCQYDVQQGEwJVUzEU # MBIGA1UECAwLQ29ubmVjdGljdXQxFzAVBgNVBAoMDkphc29uIEFsYmVyaW5vMRcw # FQYDVQQDDA5KYXNvbiBBbGJlcmlubzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC # AgoCggIBAPN6aN4B1yYWkI5b5TBj3I0VV/peETrHb6EY4BHGxt8Ap+eT+WpEpJyE # tRYPxEmNJL3A38Bkg7mwzPE3/1NK570ZBCuBjSAn4mSDIgIuXZnvyBO9W1OQs5d6 # 7MlJLUAEufl18tOr3ST1DeO9gSjQSAE5Nql0QDxPnm93OZBon+Fz3CmE+z3MwAe2 # h4KdtRAnCqwM+/V7iBdbw+JOxolpx+7RVjGyProTENIG3pe/hKvPb501lf8uBAAD # LdjZr5ip8vIWbf857Yw1Bu10nVI7HW3eE8Cl5//d1ribHlzTzQLfttW+k+DaFsKZ # BBL56l4YAlIVRsrOiE1kdHYYx6IGrEA809R7+TZA9DzGqyFiv9qmJAbL4fDwetDe # yIq+Oztz1LvEdy8Rcd0JBY+J4S0eDEFIA3X0N8VcLeAwabKb9AjulKXwUeqCJLvN # 79CJ90UTZb2+I+tamj0dn+IKMEsJ4v4Ggx72sxFr9+6XziodtTg5Luf2xd6+Phha # mOxF2px9LObhBLLEMyRsCHZIzVZOFKu9BpHQH7ufGB+Sa80Tli0/6LEyn9+bMYWi # 2ttn6lLOPThXMiQaooRUq6q2u3+F4SaPlxVFLI7OJVMhar6nW6joBvELTJPmANSM # jDSRFDfHRCdGbZsL/keELJNy+jZctF6VvxQEjFM8/bazu6qYhrA7AgMBAAGjggGJ # MIIBhTAfBgNVHSMEGDAWgBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQU # 6YF0o0D5AVhKHbVocr8GaSIBibAwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC # MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIB # AwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EM # AQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2Vj # dGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBE # BggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGlj # Q29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl # Y3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQAEIsm4xnOd/tZMVrKwi3doAXvC # wOA/RYQnFJD7R/bSQRu3wXEK4o9SIefye18B/q4fhBkhNAJuEvTQAGfqbbpxow03 # J5PrDTp1WPCWbXKX8Oz9vGWJFyJxRGftkdzZ57JE00synEMS8XCwLO9P32MyR9Z9 # URrpiLPJ9rQjfHMb1BUdvaNayomm7aWLAnD+X7jm6o8sNT5An1cwEAob7obWDM6s # X93wphwJNBJAstH9Ozs6LwISOX6sKS7CKm9N3Kp8hOUue0ZHAtZdFl6o5u12wy+z # zieGEI50fKnN77FfNKFOWKlS6OJwlArcbFegB5K89LcE5iNSmaM3VMB2ADV1FEcj # GSHw4lTg1Wx+WMAMdl/7nbvfFxJ9uu5tNiT54B0s+lZO/HztwXYQUczdsFon3pjs # Nrsk9ZlalBi5SHkIu+F6g7tWiEv3rtVApmJRnLkUr2Xq2a4nbslUCt4jKs5UX4V1 # nSX8OM++AXoyVGO+iTj7z+pl6XE9Gw/Td6WKKKsxggMaMIIDFgIBATBoMFQxCzAJ # BgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNl # Y3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYCEAec4OTRFH+FzTlzz3Yt # N+swDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZ # BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB # BAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg1LRfUnBmU1ehEpGKDWyCoa1u0JMReFse # xuRTr3elHu0wDQYJKoZIhvcNAQEBBQAEggIAquaw0FEOGF2DhudM/xzTc3jM9exN # 6mTF+KESpmfYgZ3qntNvxp6WeTN59ZYjbgGEGteDfFBl2vqk90r9RciAlfTrbUlP # 8Tc2/NzVpv9Q6au3i8HjD0VujGDRdq/PhIjTy9MOO+NfS+t0mKfifHJrfqMrp3Sr # CKl+oN17XkvLo9TkOUAk93RF5MKc9V3Jcorihke5MtXKr9PZPARdogOL5Nx5AO7u # kzdN9HhjURuSTP6cNH1FWljDCNYicxRmFkqOn+M+2IootzQBxI41ME74UdTA2PnV # BgIwYvmSaCLov8vYE0vZUYKL/j+UK0z+vfKo3Gs3P4Ej7KCWP46ZRUKneWbqDkKc # v4CoUU4ELB62UbXfJyDOThJsA8zRmqPx56HKClf441od3E5LkTnCWP5lidK3Boyg # +DRqv9SbNIEXHOn1/N120D9W5g0dc5qrN2gWvu+BNSmnrWn74zLmBI0W5m6HygBM # E+5vStMhZD6iICOxueoQnX9PIMQiru24z0tYpDZcm+Nl68fTaxoqzf7vjgAVLESf # 5RCgwya/iqQOVcn1wcT93PEuSA8bK3e54qvX+trkoAx/DskG4rXoRkTtsUY/Y8e0 # sNt6bzGr7HGjwky0lAfVREzkfcY11R+C4JOzLId9B+lJPDCCn15yXAVXKuylfxSi # Phi9K8j2FLxqKk4= # SIG # End signature block |