<# .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