From ba798284dfcd1f03e5e5f5c4b27166c340ae95fc Mon Sep 17 00:00:00 2001 From: kyle Date: Wed, 7 Oct 2026 14:04:42 -0600 Subject: [PATCH] Run the Veeam collector under PowerShell 7 VBR 13's Veeam PowerShell module expects PowerShell 7, but the scheduled task ran powershell.exe (Windows PowerShell 5.1). - The installer finds a machine-wide pwsh.exe (Program Files, then PATH, or -PwshPath), checks it reports major version 7+, and rejects Microsoft Store installs, which SYSTEM can't run. All checks run before anything is changed - The scheduled task and printed next steps use that pwsh.exe Co-Authored-By: Claude Opus 5.5 --- collectors/veeam/Install-VeeamCollector.ps1 | 36 +++++++++++++++++---- collectors/veeam/README.md | 12 +++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/collectors/veeam/Install-VeeamCollector.ps1 b/collectors/veeam/Install-VeeamCollector.ps1 index 0225eb4..74ab304 100644 --- a/collectors/veeam/Install-VeeamCollector.ps1 +++ b/collectors/veeam/Install-VeeamCollector.ps1 @@ -7,7 +7,9 @@ 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. + - 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 @@ -22,7 +24,9 @@ param( [string]$SourceSystem = 'Veeam B&R', [ValidateRange(5, 1440)][int]$IntervalMinutes = 15, [ValidateRange(1, 720)][int]$LookbackHours = 48, - [string]$InstallDir = (Join-Path $env:ProgramFiles 'BackupDashboard') + [string]$InstallDir = (Join-Path $env:ProgramFiles 'BackupDashboard'), + # Defaults to the machine-wide PowerShell 7 install + [string]$PwshPath ) $ErrorActionPreference = 'Stop' @@ -30,6 +34,26 @@ $TaskName = 'Backup Dashboard - Veeam collector' $DataDir = Join-Path $env:ProgramData 'BackupDashboard' $ConfigPath = Join-Path $DataDir 'veeam-collector.json' +# 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')." +} + if ($ApiUrl -notmatch '^https://') { Write-Warning 'ApiUrl is not HTTPS: the API key would be sent in clear text.' } @@ -59,7 +83,7 @@ $scriptPath = Join-Path $InstallDir 'Send-VeeamBackupStatus.ps1' Copy-Item -Path (Join-Path $PSScriptRoot 'Send-VeeamBackupStatus.ps1') -Destination $scriptPath -Force Unblock-File -Path $scriptPath -$action = New-ScheduledTaskAction -Execute 'powershell.exe' ` +$action = New-ScheduledTaskAction -Execute $PwshPath ` -Argument "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$scriptPath`"" $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) ` -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes) @@ -70,9 +94,9 @@ Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Pr -Settings $settings -Description 'Sends Veeam job results to the Backup Dashboard' -Force | Out-Null Write-Host "Installed. Config: $ConfigPath" -Write-Host "Task '$TaskName' runs every $IntervalMinutes minutes as SYSTEM." +Write-Host "Task '$TaskName' runs every $IntervalMinutes minutes as SYSTEM, using PowerShell $pwshVersion ($PwshPath)." Write-Host '' Write-Host 'Next steps:' -Write-Host " 1. Preview what will be sent: & '$scriptPath' -DryRun" -Write-Host " 2. Backfill 30 days of history: & '$scriptPath' -LookbackHours 720" +Write-Host " 1. Preview what will be sent: & '$PwshPath' -File '$scriptPath' -DryRun" +Write-Host " 2. Backfill 30 days of history: & '$PwshPath' -File '$scriptPath' -LookbackHours 720" Write-Host " 3. Check the log: Get-Content '$DataDir\veeam-collector.log' -Tail 20" diff --git a/collectors/veeam/README.md b/collectors/veeam/README.md index 896bb47..7e96948 100644 --- a/collectors/veeam/README.md +++ b/collectors/veeam/README.md @@ -5,19 +5,25 @@ It covers jobs returned by `Get-VBRJob`: VM backup, replication and backup copy ## 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: ```bash python manage.py create-key veeam-acme ``` -2. Copy this folder to the VBR server and run: +2. Copy this folder to the VBR server and run, from PowerShell 7 (`pwsh`): ```powershell .\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 15 minutes) -3. Preview the payloads, then backfill 30 days so the history view is populated straight away: + - registers the scheduled task **Backup Dashboard - Veeam collector** (SYSTEM, every 15 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`): ```powershell & 'C:\Program Files\BackupDashboard\Send-VeeamBackupStatus.ps1' -DryRun & 'C:\Program Files\BackupDashboard\Send-VeeamBackupStatus.ps1' -LookbackHours 720