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]>
75 lines
3.6 KiB
PowerShell
75 lines
3.6 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Installs the Veeam Backup for Microsoft 365 collector as a SYSTEM scheduled task.
|
|
|
|
.DESCRIPTION
|
|
- Copies Send-Vb365BackupStatus.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 Windows PowerShell 5.1
|
|
(powershell.exe) unless -PwshPath points at PowerShell 7.
|
|
Safe to re-run to change settings or update the script. Can sit alongside the other collectors.
|
|
|
|
Dashboard company per job: -OrganizationTenants maps Microsoft 365 organization names to
|
|
companies; jobs of unmapped organizations use -Tenant, or the organization's name if no -Tenant.
|
|
|
|
.EXAMPLE
|
|
.\Install-Vb365Collector.ps1 -ApiUrl https://backups.example.com -Tenant "Acme Corp"
|
|
One client per VB365 server. Prompts for the API key.
|
|
.EXAMPLE
|
|
.\Install-Vb365Collector.ps1 -ApiUrl https://backups.example.com -OrganizationTenants @{
|
|
'acme.onmicrosoft.com' = 'Acme Corp'; 'bluewater.onmicrosoft.com' = 'Bluewater Dental' }
|
|
A shared VB365 server protecting several organizations.
|
|
#>
|
|
#Requires -RunAsAdministrator
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)][string]$ApiUrl,
|
|
[string]$Tenant,
|
|
[hashtable]$OrganizationTenants = @{},
|
|
[string]$SourceSystem = 'Veeam Backup for M365',
|
|
[ValidateRange(5, 1440)][int]$IntervalMinutes = 60,
|
|
[ValidateRange(1, 720)][int]$LookbackHours = 48,
|
|
[string]$InstallDir = (Join-Path $env:ProgramFiles 'BackupDashboard'),
|
|
# Run under PowerShell 7 instead of Windows PowerShell, e.g. 'C:\Program Files\PowerShell\7\pwsh.exe'
|
|
[string]$PwshPath
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$Shell = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
|
if ($PwshPath) {
|
|
if (-not (Test-Path $PwshPath)) { throw "PowerShell 7 not found at '$PwshPath'." }
|
|
$Shell = $PwshPath
|
|
}
|
|
|
|
# Check VB365 is here before changing anything
|
|
$moduleFile = Join-Path $env:ProgramFiles 'Veeam\Backup365\Veeam.Archiver.PowerShell\Veeam.Archiver.PowerShell.psd1'
|
|
if (-not (Get-Module -ListAvailable -Name Veeam.Archiver.PowerShell) -and -not (Test-Path $moduleFile)) {
|
|
throw 'The Veeam Backup for Microsoft 365 PowerShell module (Veeam.Archiver.PowerShell) is not installed on this server.'
|
|
}
|
|
if (-not $Tenant -and $OrganizationTenants.Count -eq 0) {
|
|
Write-Warning 'No -Tenant or -OrganizationTenants: jobs will be listed under their Microsoft 365 organization name.'
|
|
}
|
|
|
|
# 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]@{
|
|
SourceSystem = $SourceSystem
|
|
LookbackHours = $LookbackHours
|
|
OrganizationTenants = $OrganizationTenants
|
|
}
|
|
if ($Tenant) { $settings.Tenant = $Tenant }
|
|
Install-CollectorTask -Name 'vb365' -ScriptPath (Join-Path $PSScriptRoot 'Send-Vb365BackupStatus.ps1') `
|
|
-TaskName 'Backup Dashboard - VB365 collector' `
|
|
-TaskDescription 'Sends Veeam Backup for Microsoft 365 results to the Backup Dashboard' `
|
|
-Shell $Shell -ApiUrl $ApiUrl -IntervalMinutes $IntervalMinutes -InstallDir $InstallDir -Settings $settings
|