<# .SYNOPSIS Read-only dump of what the Windows Server Backup cmdlets return, for troubleshooting the collector. .DESCRIPTION The collector reads properties of Get-WBJob / Get-WBSummary results whose names and time zones aren't fully documented. This prints those objects' real structure (property names, types and values, a few levels deep), plus recent events from the Microsoft-Windows-Backup log, whose timestamps are reliable local times to compare against. Nothing is changed. Output is also saved to a text file. .EXAMPLE .\Get-WsbCollectorDiagnostics.ps1 #> [CmdletBinding()] param( [int]$Jobs = 3, [int]$Events = 15, [string]$OutFile = (Join-Path $env:TEMP 'wsb-collector-diagnostics.txt') ) $ErrorActionPreference = 'Stop' $MaxDepth = 6 if (-not (Get-Command Get-WBJob -ErrorAction SilentlyContinue)) { Import-Module WindowsServerBackup } function Test-Scalar($Value) { return $null -eq $Value -or $Value -is [string] -or $Value -is [ValueType] -or $Value -is [guid] } # Recursive "name (type) = value" listing. Collections show their first 3 items. function Show-Object($Value, [string]$Indent = ' ', [int]$Depth = 0) { if (Test-Scalar $Value) { return } if ($Depth -ge $MaxDepth) { "$Indent..."; return } if ($Value -is [Collections.IEnumerable]) { $items = @($Value) "$Indent[$($items.Count) item(s)]" $i = 0 foreach ($item in $items | Select-Object -First 3) { "$Indent[$i] ($($item.GetType().FullName))$(if (Test-Scalar $item) { " = $item" })" Show-Object $item "$Indent " ($Depth + 1) $i++ } return } foreach ($p in $Value.PSObject.Properties) { try { $v = $p.Value } catch { "$Indent$($p.Name) = "; continue } $type = if ($null -eq $v) { 'null' } else { $v.GetType().FullName } if (Test-Scalar $v) { "$Indent$($p.Name) ($type) = $v" } else { $text = [string]$v if ($text.Length -gt 100) { $text = $text.Substring(0, 97) + '...' } "$Indent$($p.Name) ($type) = $text" Show-Object $v "$Indent " ($Depth + 1) } } } function Show-Section([string]$Title, [scriptblock]$Get) { "== $Title" try { $result = @(& $Get) if ($result.Count -eq 0 -or ($result.Count -eq 1 -and $null -eq $result[0])) { ' (nothing returned)' } $n = 0 foreach ($item in $result) { if ($null -eq $item) { continue } " [$n] ($($item.GetType().FullName))" Show-Object $item ' ' $n++ } } catch { " error: $($_.Exception.Message)" } '' } $report = & { "Generated $(Get-Date -Format o) on $env:COMPUTERNAME, PowerShell $($PSVersionTable.PSVersion)" "Local time zone: $([TimeZoneInfo]::Local.Id), UTC offset now $([TimeZoneInfo]::Local.GetUtcOffset((Get-Date)))" '' Show-Section 'Get-WBSummary' { Get-WBSummary } Show-Section 'Get-WBJob (in progress)' { Get-WBJob } Show-Section "Get-WBJob -Previous $Jobs" { Get-WBJob -Previous $Jobs } Show-Section 'Get-WBPolicy | Get-WBSchedule' { $policy = Get-WBPolicy if ($policy) { Get-WBSchedule -Policy $policy } else { '(no scheduled backup policy)' } } "== Microsoft-Windows-Backup event log (last $Events, local times)" try { Get-WinEvent -LogName 'Microsoft-Windows-Backup' -MaxEvents $Events | Select-Object TimeCreated, Id, LevelDisplayName, @{ n = 'Message'; e = { ($_.Message -split "`r?`n")[0] } } | Format-Table -AutoSize -Wrap | Out-String -Width 300 } catch { " error: $($_.Exception.Message)" } } | Out-String -Width 300 $report $report | Set-Content -Path $OutFile -Encoding UTF8 Write-Host "Saved to $OutFile"