Files
kyleandClaude Opus 5.5 c5fe69a813 Add a Pester test suite for the collectors
84 tests in collectors/tests, run with Invoke-CollectorTests.ps1 (~15 s,
PowerShell 7.2+ and Pester 5.5+):

- Module.Tests: the shared module (property lookup, time format,
  config/lookback, sent-run state and pruning, dry runs, exit codes,
  401 detection)
- Veeam/Mars/Wsb/Vb365.Tests: each collector run in its own process
  against its mock and a stand-in ingest API, asserting the behaviours
  confirmed on real servers: VBR 13 live schedule, copy jobs, MARS UTC
  times and ErrorCode 0, WSB culture-formatted times and nested VMs,
  VB365 organization mapping, log prefixes and retries, state and
  resending, rejected keys
- Installers.Tests: config, files and scheduled task from each
  installer, with stand-ins for icacls / Task Scheduler / the key prompt

Collectors run under the test runner's PowerShell, or another shell via
-CollectorShell (e.g. powershell.exe, to test under Windows PowerShell
5.1 like the MARS, WSB and VB365 servers). The mocks take their "now"
from MOCK_NOW so expectations can't drift with the clock; the WSB mock
gains -RunningJobFinished for the running-to-finished case.

Each of 12 past bugs, put back one at a time, fails the test for its
behaviour (and the runner exits 1).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 13:36:27 -06:00
..

Veeam Backup & Replication collector

A PowerShell script that runs on each client's VBR server and reports job results to the dashboard. It covers jobs returned by Get-VBRJob: VM backup, replication and backup copy jobs.

Install (on the VBR server, as an administrator)

Requires PowerShell 7 installed machine-wide (winget install Microsoft.PowerShell or the MSI), which VBR 13's Veeam module expects. The Microsoft Store version won't work, because the SYSTEM account can't run it. The installer checks for this before changing anything.

  1. On the dashboard host, create a key for this server:
    python manage.py create-key veeam-acme
    
  2. Copy the whole collectors folder to the VBR server (the installer needs common as well as this folder), then from collectors\veeam run, from PowerShell 7 (pwsh):
    .\Install-VeeamCollector.ps1 -ApiUrl https://backups.example.com -Tenant "Acme Corp"
    
    It prompts for the key, then:
    • writes C:\ProgramData\BackupDashboard\veeam-collector.json (readable only by SYSTEM and Administrators)
    • copies the script to C:\Program Files\BackupDashboard\
    • registers the scheduled task Backup Dashboard - Veeam collector (SYSTEM, every 60 minutes, run by C:\Program Files\PowerShell\7\pwsh.exe; use -PwshPath if it's installed elsewhere)
  3. Preview the payloads, then backfill 30 days so the history view is populated straight away (from pwsh):
    & 'C:\Program Files\BackupDashboard\Send-VeeamBackupStatus.ps1' -DryRun
    & 'C:\Program Files\BackupDashboard\Send-VeeamBackupStatus.ps1' -LookbackHours 720
    

Re-run the installer to change settings or update the script. It also installs BackupDashboard.Collector.psm1, the code shared by all collectors, next to the script. To remove the collector, delete the scheduled task and both BackupDashboard folders.

What it sends

Dashboard Veeam
Company Tenant from the config
Job Job name. The job's GUID is the ID, so renaming a job in Veeam doesn't duplicate it
Status Session result (Success / Warning / Failed). Any state other than Stopped is Running. A session that stopped with no result (e.g. cancelled) is reported as Warning
Started / finished Session CreationTime / EndTime
Next scheduled Job's next run, from the live schedule (GetScheduleOptions()). Sent as empty, so never flagged Overdue, for disabled jobs, chained ("run after") jobs, and immediate-mode backup copy jobs, which run whenever their source does
Message Session reason, each VM or object that didn't succeed, and warning/error lines from the session log
Size BackupStats.BackupSize

Backup copy jobs (VBR 12+): Veeam records their sessions under hidden per-source "worker" jobs. The collector files each worker session under its copy job (via the worker's PolicyJobId), so a copy job gets one run per source job it copies, with the source in copied_from.

Each run covers the last 48 hours (LookbackHours), so a few missed runs (server rebooting, API down) fill in automatically. Sessions already delivered are recorded in veeam-collector.state.json. Running sessions are re-sent until they finish.

Troubleshooting

  • Log: C:\ProgramData\BackupDashboard\veeam-collector.log (rotates at 5 MB).
  • Exit codes: 0 OK, 1 some jobs failed to send (they're retried next run), 2 setup failed (config, Veeam module, or Veeam connection).
  • API key rejected: the key was revoked or mistyped. Re-run the installer with a new key.
  • A job shows Unknown on the dashboard: the collector sent the job but matched no sessions to it. Run .\Get-VeeamCollectorDiagnostics.ps1 (read-only). It lists sessions whose job ID matches no job, plus the type and link properties of the jobs left without sessions.
  • Get-VBRBackupSession loads every session in the VBR database. On a server with years of history, this can make each run take a minute or more. The task's 30-minute limit and "don't start if already running" setting keep that safe.

Testing without Veeam

tests/MockVeeam.ps1 defines stand-in Get-VBRJob / Get-VBRBackupSession cmdlets with a mix of success, warning, failed, running and disabled jobs:

. ./tests/MockVeeam.ps1
./Send-VeeamBackupStatus.ps1 -ConfigPath ./my-test-config.json -DryRun

Not covered yet

  • Veeam Agent jobs managed by VBR (Get-VBRComputerBackupJob), tape jobs, and SureBackup.
  • Veeam Backup for Microsoft 365, which has its own PowerShell module.