diff --git a/Get-VeeamCollectorDiagnostics.ps1 b/Get-VeeamCollectorDiagnostics.ps1 deleted file mode 100644 index 199a834..0000000 --- a/Get-VeeamCollectorDiagnostics.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -<# -.SYNOPSIS - Read-only report of how Veeam jobs and sessions relate, for troubleshooting the collector. - -.DESCRIPTION - The collector matches sessions to jobs by Session.JobId = Job.Id. When a job shows as - "Unknown" on the dashboard, no session matched. This lists: - 1. every job from Get-VBRJob and how many sessions matched it - 2. sessions whose JobId matches no job (the runs that are going missing) - 3. for jobs with no matching sessions, their type/link-related properties and methods - 4. what other job cmdlets (e.g. Get-VBRBackupCopyJob) return - Nothing is changed in Veeam or the dashboard. Output is also saved to a text file. - -.EXAMPLE - .\Get-VeeamCollectorDiagnostics.ps1 -.EXAMPLE - .\Get-VeeamCollectorDiagnostics.ps1 -LookbackHours 168 -#> -[CmdletBinding()] -param( - [int]$LookbackHours = 48, - [string]$OutFile = (Join-Path $env:TEMP 'veeam-collector-diagnostics.txt') -) - -$ErrorActionPreference = 'Stop' -$LinkPattern = 'Parent|Linked|Worker|Child|Policy|Source|Target|Type|Cloud|Mode|Session' - -if (-not (Get-Command Get-VBRJob -ErrorAction SilentlyContinue)) { - if (Get-Module -ListAvailable -Name Veeam.Backup.PowerShell) { - Import-Module Veeam.Backup.PowerShell -WarningAction SilentlyContinue - } else { - Add-PSSnapin VeeamPSSnapin - } -} - -function Format-Value($Value) { - if ($null -eq $Value) { return '' } - $text = ($Value | ForEach-Object { [string]$_ }) -join ', ' - if ($text.Length -gt 120) { $text = $text.Substring(0, 117) + '...' } - return $text -} - -# Property and method names matching $LinkPattern, with property values. Every read is guarded: -# some Veeam properties throw when accessed. -function Show-LinkMembers($Object) { - foreach ($p in $Object.PSObject.Properties | Where-Object { $_.Name -match $LinkPattern }) { - $value = try { Format-Value $p.Value } catch { "" } - ' {0} = {1}' -f $p.Name, $value - } - $methods = @($Object | Get-Member -MemberType Methods -ErrorAction SilentlyContinue | - Where-Object { $_.Name -match $LinkPattern } | ForEach-Object Name) - if ($methods) { ' methods: ' + ($methods -join ', ') } -} - -function Get-Prop($Object, [string]$Name) { - try { Format-Value $Object.$Name } catch { '' } -} - -$report = & { - $vbrVersion = try { - (Get-Item (Join-Path ${env:ProgramFiles} 'Veeam\Backup and Replication\Backup\Veeam.Backup.Core.dll')).VersionInfo.ProductVersion - } catch { 'unknown' } - "Generated $(Get-Date -Format o) on $env:COMPUTERNAME, VBR $vbrVersion, PowerShell $($PSVersionTable.PSVersion)" - "Lookback: $LookbackHours hours" - '' - - $since = (Get-Date).AddHours(-$LookbackHours) - $jobs = @(Get-VBRJob -WarningAction SilentlyContinue) - $sessions = @(Get-VBRBackupSession -WarningAction SilentlyContinue | Where-Object { $_.CreationTime -ge $since }) - $jobIds = @{} - foreach ($j in $jobs) { $jobIds[$j.Id.ToString()] = $j } - - '== 1. Jobs from Get-VBRJob' - $jobs | ForEach-Object { - $id = $_.Id.ToString() - [pscustomobject]@{ - Name = $_.Name - JobType = Get-Prop $_ 'JobType' - TypeToString = Get-Prop $_ 'TypeToString' - Id = $id - MatchingSessions = @($sessions | Where-Object { $_.JobId.ToString() -eq $id }).Count - } - } | Format-Table -AutoSize | Out-String -Width 300 - - '== 2. Sessions whose JobId matches no job from Get-VBRJob' - $orphans = @($sessions | Where-Object { -not $jobIds.ContainsKey($_.JobId.ToString()) }) - if ($orphans.Count -eq 0) { - ' (none)' - } else { - $orphans | Group-Object { $_.JobId.ToString() } | ForEach-Object { - $latest = $_.Group | Sort-Object CreationTime | Select-Object -Last 1 - [pscustomobject]@{ - JobId = $_.Name - JobName = Get-Prop $latest 'JobName' - JobType = Get-Prop $latest 'JobType' - Sessions = $_.Count - LatestStart = $latest.CreationTime - State = Get-Prop $latest 'State' - Result = Get-Prop $latest 'Result' - } - } | Format-Table -AutoSize | Out-String -Width 300 - - ' Link-related members of one orphan session:' - Show-LinkMembers $orphans[0] - '' - ' Job object for that session (via GetJob(), if available):' - try { - $owner = $orphans[0].GetJob() - ' Name = {0}, Id = {1}' -f (Get-Prop $owner 'Name'), (Get-Prop $owner 'Id') - Show-LinkMembers $owner - } catch { - " not available: $($_.Exception.Message)" - } - } - '' - - '== 3. Jobs with no matching sessions in the window' - $empty = @($jobs | Where-Object { $id = $_.Id.ToString(); -not ($sessions | Where-Object { $_.JobId.ToString() -eq $id }) }) - if ($empty.Count -eq 0) { ' (none)' } - foreach ($j in $empty) { - " $($j.Name) [$($j.Id)]" - Show-LinkMembers $j - try { - $last = $j.FindLastSession() - if ($last) { - ' FindLastSession(): JobId = {0}, JobName = {1}, Start = {2}, Result = {3}' -f ` - (Get-Prop $last 'JobId'), (Get-Prop $last 'JobName'), $last.CreationTime, (Get-Prop $last 'Result') - } else { - ' FindLastSession(): ' - } - } catch { - " FindLastSession(): not available: $($_.Exception.Message)" - } - '' - } - - '== 4. Other job cmdlets' - foreach ($cmd in 'Get-VBRBackupCopyJob', 'Get-VBRComputerBackupCopyJob', 'Get-VBRComputerBackupJob') { - if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) { " ${cmd}: not available"; continue } - try { - $found = @(& $cmd -WarningAction SilentlyContinue) - " ${cmd}: $($found.Count) job(s)" - foreach ($f in $found) { - ' {0} [{1}] Type = {2}, Mode = {3}' -f (Get-Prop $f 'Name'), (Get-Prop $f 'Id'), (Get-Prop $f 'Type'), (Get-Prop $f 'Mode') - } - } catch { - " ${cmd}: error: $($_.Exception.Message)" - } - } -} | Out-String -Width 300 - -$report -$report | Set-Content -Path $OutFile -Encoding UTF8 -Write-Host "Saved to $OutFile"