Upload files to "/"

This commit is contained in:
2026-10-07 20:05:28 +00:00
parent 986cf3be88
commit 8fd17e3817
5 changed files with 739 additions and 86 deletions
+154
View File
@@ -0,0 +1,154 @@
<#
.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 '<null>' }
$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 { "<error: $($_.Exception.Message)>" }
' {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 { '<error>' }
}
$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(): <null>'
}
} 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"