Files
Backup-Dashboard/collectors/veeam/Install-VeeamCollector.ps1
kyleandClaude Opus 5.5 8d0e6dca71 Move code shared by the collectors into one module
The Veeam, MARS, WSB and VB365 collectors each carried identical copies
of logging, ISO time conversion, property lookup, posting to the API,
config loading, TLS setup, the sent-runs state file and the 401 check,
and their installers repeated the same key prompt, locked-down config,
file copy and scheduled-task registration. These now live in
collectors/common/BackupDashboard.Collector.psm1:

- Initialize-Collector / Test-RunSent / Send-JobReport /
  Test-AuthRejected / Complete-Collector for collector runs
- Write-Log, Get-ErrorText, ConvertTo-IsoTime, Get-FirstValue
- Install-CollectorTask for the shared half of each installer

Collectors find the module next to themselves (installed) or in
..\common (repo); installers copy it into Program Files beside the
collector. The diagnostic scripts stay standalone single files.

No behavior change: a harness ran each collector against its mock
(12 scenarios: dry run, two real runs against a stub API, state file,
logs, exit codes, rejected key, en-GB, backfill) before and after, and
all 84 outputs are identical. Installers, run with stand-ins for the
Windows-only commands, write identical configs and tasks; the only
differences are the module being copied and the summary line naming
the shell's path.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 11:09:01 -06:00

69 lines
3.2 KiB
PowerShell

<#
.SYNOPSIS
Installs the Veeam collector on a VBR server as a SYSTEM scheduled task.
.DESCRIPTION
- Copies Send-VeeamBackupStatus.ps1 to Program Files (only admins can modify it, which
matters because it runs as SYSTEM).
- Writes the config to ProgramData\BackupDashboard and restricts that folder to SYSTEM and
Administrators, since it contains the API key.
- Registers a scheduled task that runs every IntervalMinutes under PowerShell 7 (pwsh.exe),
which the Veeam module on VBR 13 expects. Install PowerShell 7 machine-wide (MSI or winget)
first: Microsoft Store installs can't be run by SYSTEM.
Safe to re-run to change settings or update the script.
.EXAMPLE
.\Install-VeeamCollector.ps1 -ApiUrl https://backups.example.com -Tenant "Acme Corp"
(prompts for the API key)
#>
#Requires -RunAsAdministrator
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$ApiUrl,
[Parameter(Mandatory)][string]$Tenant,
[string]$SourceSystem = 'Veeam B&R',
[ValidateRange(5, 1440)][int]$IntervalMinutes = 60,
[ValidateRange(1, 720)][int]$LookbackHours = 48,
[string]$InstallDir = (Join-Path $env:ProgramFiles 'BackupDashboard'),
# Defaults to the machine-wide PowerShell 7 install
[string]$PwshPath
)
$ErrorActionPreference = 'Stop'
# Find PowerShell 7 before changing anything, so a missing install fails cleanly
if (-not $PwshPath) {
$PwshPath = Join-Path $env:ProgramFiles 'PowerShell\7\pwsh.exe'
if (-not (Test-Path $PwshPath)) {
$onPath = Get-Command pwsh.exe -ErrorAction SilentlyContinue
if ($onPath) { $PwshPath = $onPath.Source }
}
}
if (-not (Test-Path $PwshPath)) {
throw "PowerShell 7 not found at '$PwshPath'. Install it machine-wide (e.g. winget install Microsoft.PowerShell) or pass -PwshPath."
}
if ($PwshPath -match '\\WindowsApps\\') {
throw "'$PwshPath' is a Microsoft Store install, which the SYSTEM account can't run. Install the MSI or winget version instead."
}
$pwshVersion = & $PwshPath -NoProfile -NonInteractive -Command '$PSVersionTable.PSVersion.ToString()'
$pwshMajor = & $PwshPath -NoProfile -NonInteractive -Command '$PSVersionTable.PSVersion.Major'
if ($LASTEXITCODE -or ($pwshMajor -as [int]) -lt 7) {
throw "'$PwshPath' isn't a working PowerShell 7 (reported '$pwshVersion')."
}
# Shared installer code: in the repo it's in ..\common, so copy the whole collectors folder
$sharedModule = @(
(Join-Path $PSScriptRoot 'BackupDashboard.Collector.psm1'),
(Join-Path (Join-Path (Join-Path $PSScriptRoot '..') 'common') 'BackupDashboard.Collector.psm1')
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $sharedModule) {
throw "BackupDashboard.Collector.psm1 not found. Copy the whole collectors folder, including common."
}
Import-Module $sharedModule -Force
$settings = [ordered]@{ Tenant = $Tenant; SourceSystem = $SourceSystem; LookbackHours = $LookbackHours }
Install-CollectorTask -Name 'veeam' -ScriptPath (Join-Path $PSScriptRoot 'Send-VeeamBackupStatus.ps1') `
-TaskName 'Backup Dashboard - Veeam collector' `
-TaskDescription 'Sends Veeam job results to the Backup Dashboard' `
-Shell $PwshPath -ApiUrl $ApiUrl -IntervalMinutes $IntervalMinutes -InstallDir $InstallDir -Settings $settings