Guide technique ANYSYS · vérifié le 26 octobre 2026
Veeam 13 en PowerShell : rapport quotidien et contrôle du RPO

Le module officiel Veeam.Backup.PowerShell, la connexion, les cmdlets de lecture réellement documentés en version 13 et un script en lecture seule qui produit un rapport CSV et HTML et renvoie un code de sortie non nul quand le RPO n’est pas tenu.
01 / Périmètre
Périmètre et prérequis
La console Veeam montre l’état des tâches ; un rapport automatisé le montre chaque matin à quelqu’un qui n’a pas besoin d’ouvrir la console, et il peut alimenter une supervision. Ce guide clôt la série après l’architecture, les dépôts immuables, les tâches et la restauration.
- Module : Veeam.Backup.PowerShell, installé par défaut avec la console Veeam Backup & Replication. Contrôle :
Get-Module -Name Veeam.Backup.PowerShell -ListAvailable. - PowerShell 7.6.3 requis sur la machine qui exécute la session (Windows comme Linux, selon la référence v13).
- Sur Linux : Rocky Linux 9 ou RHEL 9.6, ASP.NET Core Runtime 8.0, paquet
veeam-powershell, module importé depuis/opt/veeam/powershell/Veeam.Backup.PowerShell/Veeam.Backup.PowerShell.psd1. - Rôle Backup Administrator requis pour exécuter les cmdlets Veeam.
- La MFA n’est pas supportée pour PowerShell : utilisez un compte de service dédié pour lequel la MFA est désactivée.
02 / Connexion
Se connecter
Connect-VBRServer ouvre la session. Sans -Server, il se connecte au serveur local ; le port par défaut est 443 et le délai d’attente par défaut de 30 secondes. Les comptes SAML ne sont pas supportés et une session PowerShell ne se connecte qu’à un seul serveur à la fois.
# Read-only session
Import-Module Veeam.Backup.PowerShell
$cred = Get-Credential -Message 'Veeam service account (DOMAIN\User or UPN)'
Connect-VBRServer -Server 'vbr01.mgmt.local' -Credential $cred
Get-VBRServerSession
# ... read-only cmdlets ...
Disconnect-VBRServerLe paramètre -ForceAcceptTlsCertificate accepte le certificat TLS du serveur sans contrôle : réservez-le à un laboratoire et installez un certificat de confiance en production. Évitez -User / -Password en clair dans les scripts.
03 / Cmdlets
Cmdlets de lecture
| Cmdlet | Ce qu’il renvoie (référence v13) | Remarque |
|---|---|---|
Get-VBRJob | Tâches de sauvegarde, réplication, copie de fichiers, copie de VM, sauvegarde de stockage objet (objet CBackupJob) | Obsolète pour les tâches Veeam Agent : utiliser Get-VBRComputerBackupJob |
Get-VBRBackupCopyJob | Tâches de copie de sauvegarde (VBRBackupCopyJob) | Paramètres -Name, -Id |
Get-VBRComputerBackupJob | Tâches et politiques Veeam Agent (VBRComputerBackupJob) | -Mode ManagedByAgent ou ManagedByBackupServer |
Get-VBRBackupSession | Sessions des tâches de sauvegarde, réplication, copie, fichiers (VBRBackupSession) | Propriétés documentées : CreationTime, EndTime, JobId, Result, State |
Get-VBRComputerBackupJobSession | Sessions des tâches et politiques Veeam Agent (VBRSession) | Mêmes propriétés de résultat et JobId |
Get-VBRBackupRepository | Dépôts standards (CBackupRepository) ; -ScaleOut pour les SOBR | Les dépôts objet passent par Get-VBRObjectStorageRepository |
Get-VBRObjectStorageRepository | Dépôts objet (S3, S3 compatible, Azure Blob, Google Cloud, Wasabi…) | Objets S3 et Azure : BackupImmutabilityEnabled, ImmutabilityPeriod |
L’énumération VBRSessionResult vaut None, Success, Warning ou Failed. La référence précise qu’elle ne documente pas les classes .NET internes : c’est le cas de CBackupJob et CBackupRepository. Le script lit donc uniquement Name et Id sur ces objets, et tolère leur absence.
04 / Script
Le script de rapport
Le script parcourt les tâches, retrouve pour chacune la dernière session et le dernier succès, calcule l’âge de ce succès et le compare au seuil -RpoHours. Il liste les dépôts et, pour les dépôts objet, l’état d’immuabilité. Il écrit deux CSV (séparateur point-virgule par défaut) et un HTML, puis sort avec le code 0 si tout est dans le RPO, 2 si au moins une tâche le dépasse ou n’a jamais réussi, 1 en cas d’erreur. Rien n’est créé ni modifié sur le serveur Veeam.
<#
.SYNOPSIS
Read-only daily report for Veeam Backup & Replication 13: last result per job,
age of the last successful session against an RPO threshold, repositories and
object storage immutability flags. Exits with code 2 when the RPO is violated.
.DESCRIPTION
Uses only cmdlets documented in the Veeam Backup & Replication 13 PowerShell
Reference: Connect-VBRServer, Get-VBRServerSession, Get-VBRJob,
Get-VBRBackupCopyJob, Get-VBRComputerBackupJob, Get-VBRBackupSession,
Get-VBRComputerBackupJobSession, Get-VBRBackupRepository,
Get-VBRObjectStorageRepository, Disconnect-VBRServer.
Session properties used (JobId, Result, EndTime) are documented for the
VBRBackupSession and VBRSession objects. Job objects returned by Get-VBRJob
(CBackupJob) are internal .NET classes: the script only reads Name and Id and
tolerates their absence. Repository free space is NOT reported because no
documented cmdlet or property exposes it in the v13 reference.
Nothing is created, modified, started or deleted on the backup server.
.PARAMETER Server
Backup server name or address. Default: localhost.
.PARAMETER Port
Connection port. Default: 443 (Veeam Backup & Replication 13 default).
.PARAMETER Credential
Optional PSCredential (DOMAIN\User or UPN format). MFA is not supported for
PowerShell: use a dedicated service account with MFA disabled.
.PARAMETER RpoHours
Maximum accepted age, in hours, of the last successful session. Default: 24.
.PARAMETER WarningIsSuccess
Count sessions that ended with Warning as successful. Default: on.
.PARAMETER ExcludeJob
Job names (wildcards allowed) to leave out of the RPO evaluation.
.PARAMETER OutputDirectory
Folder for the CSV and HTML files. Default: current folder.
.PARAMETER Delimiter
CSV delimiter. Default: ';' (Excel with Belgian/French regional settings).
.PARAMETER NoHtml
Do not write the HTML report.
.EXAMPLE
.\Get-VBRDailyReport.ps1 -RpoHours 26
.EXAMPLE
.\Get-VBRDailyReport.ps1 -Server vbr01.mgmt.local -Credential (Get-Credential) -OutputDirectory D:\Reports
.NOTES
Requires the Veeam.Backup.PowerShell module (installed with the Veeam Backup &
Replication console, or on Linux under /opt/veeam/powershell) and the Backup
Administrator role, as stated in the Veeam PowerShell Reference.
Exit codes: 0 = all evaluated jobs within RPO, 2 = at least one RPO violation,
1 = script error.
Source: ANYSYS ICT, https://www.anysys.be/articles/
#>
[CmdletBinding()]
param(
[string]$Server = 'localhost',
[int]$Port = 443,
[System.Management.Automation.PSCredential]$Credential,
[ValidateRange(1, 8760)]
[int]$RpoHours = 24,
[bool]$WarningIsSuccess = $true,
[string[]]$ExcludeJob = @(),
[string]$OutputDirectory = (Get-Location).Path,
[string]$Delimiter = ';',
[switch]$NoHtml
)
$ErrorActionPreference = 'Stop'
function Get-PropValue {
# Returns a property value if the object exposes it, otherwise $null.
param($InputObject, [string]$Name)
if ($null -eq $InputObject) { return $null }
$prop = $InputObject.PSObject.Properties[$Name]
if ($prop) { return $prop.Value }
return $null
}
function ConvertTo-IdString {
param($Value)
if ($null -eq $Value) { return '' }
return ([string]$Value).ToLowerInvariant()
}
function Test-Excluded {
param([string]$Name, [string[]]$Pattern)
foreach ($p in $Pattern) { if ($Name -like $p) { return $true } }
return $false
}
$connectedHere = $false
$exitCode = 0
try {
# 1. Load the official module (Windows console install or Linux path).
if (-not (Get-Module -Name Veeam.Backup.PowerShell)) {
$linuxPath = '/opt/veeam/powershell/Veeam.Backup.PowerShell/Veeam.Backup.PowerShell.psd1'
if (Get-Module -Name Veeam.Backup.PowerShell -ListAvailable) {
Import-Module -Name Veeam.Backup.PowerShell
}
elseif (Test-Path -LiteralPath $linuxPath) {
Import-Module -Name $linuxPath
}
else {
throw 'Module Veeam.Backup.PowerShell not found. Install the Veeam Backup & Replication console first.'
}
}
# 2. Reuse an existing session, otherwise connect (read-only usage).
if (-not (Get-VBRServerSession)) {
$connect = @{ Server = $Server; Port = $Port }
if ($Credential) { $connect.Credential = $Credential }
Connect-VBRServer @connect
$connectedHere = $true
}
$now = Get-Date
# 3. Jobs: VM/host jobs, backup copy jobs, agent jobs and policies.
$jobList = [System.Collections.Generic.List[object]]::new()
foreach ($j in @(Get-VBRJob)) {
$jobList.Add([pscustomobject]@{ Name = Get-PropValue $j 'Name'; Id = ConvertTo-IdString (Get-PropValue $j 'Id'); Kind = 'Job' })
}
foreach ($j in @(Get-VBRBackupCopyJob)) {
$jobList.Add([pscustomobject]@{ Name = Get-PropValue $j 'Name'; Id = ConvertTo-IdString (Get-PropValue $j 'Id'); Kind = 'BackupCopy' })
}
foreach ($j in @(Get-VBRComputerBackupJob)) {
$jobList.Add([pscustomobject]@{ Name = Get-PropValue $j 'Name'; Id = ConvertTo-IdString (Get-PropValue $j 'Id'); Kind = 'Agent' })
}
$jobs = @($jobList | Where-Object { $_.Id } | Sort-Object -Property Id -Unique)
# 4. Sessions, grouped by JobId (documented property).
$sessions = @(Get-VBRBackupSession) + @(Get-VBRComputerBackupJobSession)
$byJob = @{}
foreach ($s in $sessions) {
$key = ConvertTo-IdString (Get-PropValue $s 'JobId')
if (-not $key) { continue }
if (-not $byJob.ContainsKey($key)) { $byJob[$key] = [System.Collections.Generic.List[object]]::new() }
$byJob[$key].Add($s)
}
$okResults = @('Success')
if ($WarningIsSuccess) { $okResults += 'Warning' }
$jobRows = foreach ($job in $jobs) {
$list = if ($byJob.ContainsKey($job.Id)) { @($byJob[$job.Id]) } else { @() }
$ended = @($list | Where-Object { Get-PropValue $_ 'EndTime' } | Sort-Object -Property { Get-PropValue $_ 'EndTime' } -Descending)
$last = $ended | Select-Object -First 1
$lastOk = $ended | Where-Object { [string](Get-PropValue $_ 'Result') -in $okResults } | Select-Object -First 1
$lastOkTime = Get-PropValue $lastOk 'EndTime'
$ageHours = if ($lastOkTime) { [math]::Round(($now - [datetime]$lastOkTime).TotalHours, 1) } else { $null }
$excluded = Test-Excluded -Name $job.Name -Pattern $ExcludeJob
$violated = (-not $excluded) -and (($null -eq $ageHours) -or ($ageHours -gt $RpoHours))
[pscustomobject]@{
Job = $job.Name
Kind = $job.Kind
LastResult = [string](Get-PropValue $last 'Result')
LastEnd = Get-PropValue $last 'EndTime'
LastSuccessEnd = $lastOkTime
SuccessAgeH = $ageHours
RpoHours = $RpoHours
Excluded = $excluded
RpoViolated = $violated
}
}
$jobRows = @($jobRows)
# 5. Repositories. Free space is not exposed by a documented cmdlet/property.
$repoRows = [System.Collections.Generic.List[object]]::new()
foreach ($r in @(Get-VBRBackupRepository)) {
$repoRows.Add([pscustomobject]@{ Repository = Get-PropValue $r 'Name'; Kind = 'Standard'; ImmutabilityEnabled = 'n/a (not documented)'; ImmutabilityDays = $null })
}
foreach ($r in @(Get-VBRBackupRepository -ScaleOut)) {
$repoRows.Add([pscustomobject]@{ Repository = Get-PropValue $r 'Name'; Kind = 'ScaleOut'; ImmutabilityEnabled = 'see extents'; ImmutabilityDays = $null })
}
foreach ($r in @(Get-VBRObjectStorageRepository)) {
$imm = Get-PropValue $r 'BackupImmutabilityEnabled'
$repoRows.Add([pscustomobject]@{
Repository = Get-PropValue $r 'Name'
Kind = 'ObjectStorage:' + $r.GetType().Name
ImmutabilityEnabled = if ($null -eq $imm) { 'n/a (not documented)' } else { [string]$imm }
ImmutabilityDays = Get-PropValue $r 'ImmutabilityPeriod'
})
}
# 6. Output files.
if (-not (Test-Path -LiteralPath $OutputDirectory)) { throw "Output directory not found: $OutputDirectory" }
$stamp = $now.ToString('yyyyMMdd-HHmm')
$jobCsv = Join-Path $OutputDirectory "vbr-jobs-$stamp.csv"
$repoCsv = Join-Path $OutputDirectory "vbr-repositories-$stamp.csv"
$jobRows | Export-Csv -LiteralPath $jobCsv -NoTypeInformation -Delimiter $Delimiter -Encoding utf8
$repoRows | Export-Csv -LiteralPath $repoCsv -NoTypeInformation -Delimiter $Delimiter -Encoding utf8
$violations = @($jobRows | Where-Object RpoViolated)
if (-not $NoHtml) {
$htmlPath = Join-Path $OutputDirectory "vbr-report-$stamp.html"
$title = "Veeam daily report $($now.ToString('yyyy-MM-dd HH:mm')) - $($violations.Count) RPO violation(s)"
$body = @(
"<h1>$([System.Net.WebUtility]::HtmlEncode($title))</h1>"
'<h2>Jobs</h2>'
($jobRows | Sort-Object RpoViolated -Descending | ConvertTo-Html -Fragment)
'<h2>Repositories</h2>'
($repoRows | ConvertTo-Html -Fragment)
) -join "`n"
ConvertTo-Html -Title $title -Body $body | Set-Content -LiteralPath $htmlPath -Encoding utf8
}
Write-Host ("Jobs evaluated: {0} | RPO violations: {1} | Repositories: {2}" -f $jobRows.Count, $violations.Count, $repoRows.Count)
foreach ($v in $violations) {
Write-Host (" RPO VIOLATION: {0} (last success age: {1} h)" -f $v.Job, $(if ($null -eq $v.SuccessAgeH) { 'never' } else { $v.SuccessAgeH }))
}
Write-Host "CSV: $jobCsv ; $repoCsv"
if ($violations.Count -gt 0) { $exitCode = 2 }
}
catch {
Write-Error -Message $_.Exception.Message -ErrorAction Continue
$exitCode = 1
}
finally {
if ($connectedHere) { Disconnect-VBRServer }
}
exit $exitCodeJobs evaluated: 3 | RPO violations: 1 | Repositories: 3
RPO VIOLATION: VM-Daily (last success age: 30 h)
CSV: D:\Reports\vbr-jobs-20261027-0700.csv ; D:\Reports\vbr-repositories-20261027-0700.csv05 / Exploitation
Planifier et superviser
Planifiez le script chaque matin, après la fenêtre de sauvegarde, sur un poste d’administration ou le serveur Veeam, sous le compte de service dédié. Le code de sortie est l’interface avec votre supervision : une sonde qui lance le script et alerte sur un code différent de 0 suffit.
- Fixez
-RpoHoursun peu au-dessus de l’intervalle réel (par exemple 26 h pour une tâche quotidienne) pour absorber les variations de durée. - Excluez explicitement les tâches désactivées ou saisonnières avec
-ExcludeJobplutôt que de les laisser en alerte permanente. - Archivez les CSV : ils constituent la preuve, mois par mois, que le RPO a été tenu.
- Envoyez le HTML par la messagerie de votre choix ; le script n’envoie rien lui-même.
06 / Limites
Limites connues
- Espace libre des dépôts : aucun cmdlet ni aucune propriété documentée de la référence v13 ne l’expose pour les dépôts standards. Le script ne l’affiche donc pas. Suivez-le dans la console, Veeam ONE ou votre supervision du stockage.
- Immuabilité des dépôts durcis : non exposée par une propriété documentée de CBackupRepository. Le script l’indique « n/a » ; contrôlez-la comme décrit dans le guide 2.
- Fuseau horaire de EndTime : la référence ne précise pas s’il s’agit d’heure locale ou UTC ; à vérifier sur votre serveur en comparant avec la console.
- Volume d’historique :
Get-VBRBackupSessionsans filtre renvoie toutes les sessions. Sur un historique très long, l’exécution peut prendre du temps. - Support : Veeam n’écrit pas de scripts sur demande et ne dépanne pas les scripts personnalisés.
07 / Prudence
Avant d’écrire
La référence Veeam le rappelle : une action en PowerShell a la même portée qu’une action dans l’interface. Supprimer une tâche en script la retire de la base sans retour possible. Toute commande Set-, Remove-, Start- ou Stop- doit donc être précédée d’une lecture qui affiche exactement les objets ciblés, et validée sur un environnement de test.
# 1. Read-only: show exactly what would be targeted
Get-VBRBackupRepository -Name 'Hardened-*' | Select-Object Name
# 2. CONFIGURATION CHANGE (only after review, with four-eyes approval if enabled)
# Set-VBRBackupRepository -Repository $repo -EnableBackupImmutability -ImmutabilityPeriod 30Avec l’autorisation à quatre yeux activée, la désactivation de l’immuabilité ou la réduction de sa période exige l’approbation d’un second administrateur, que l’opération vienne de la console ou d’ailleurs.
08 / Contrôle
Vérification
- 01
Sur le poste d’exécution :
Get-Module -Name Veeam.Backup.PowerShell -ListAvailableet$PSVersionTable.PSVersion. - 02
Lancez le script avec
-RpoHours 26et comparez chaque ligne avec la vue Home de la console. - 03
Provoquez un cas d’échec maîtrisé (tâche de test désactivée depuis plus de 26 h) et vérifiez le code de sortie 2.
- 04
Vérifiez que la sonde de supervision remonte bien l’alerte.
- 05
Consignez la version du module et le build Veeam dans la documentation d’exploitation.
.\Get-VBRDailyReport.ps1 -RpoHours 26 -OutputDirectory D:\Reports
"Exit code: $LASTEXITCODE"09 / Sources
Sources
- PowerShell Reference v13 : Getting Started
- PowerShell Reference v13 : Running Veeam Backup PowerShell on Windows
- PowerShell Reference v13 : Running Veeam PowerShell on Linux
- PowerShell Reference v13 : Connect-VBRServer
- PowerShell Reference v13 : Disconnect-VBRServer
- PowerShell Reference v13 : Get-VBRServerSession
- PowerShell Reference v13 : Get-VBRJob
- PowerShell Reference v13 : Get-VBRBackupCopyJob
- PowerShell Reference v13 : Get-VBRComputerBackupJob
- PowerShell Reference v13 : Get-VBRBackupSession
- PowerShell Reference v13 : Get-VBRComputerBackupJobSession
- PowerShell Reference v13 : VBRBackupSession
- PowerShell Reference v13 : VBRSession
- PowerShell Reference v13 : Get-VBRBackupRepository
- PowerShell Reference v13 : Get-VBRObjectStorageRepository
- PowerShell Reference v13 : VBRAmazonS3CompatibleRepository
- PowerShell Reference v13 : Enums
- User Guide v13 : Multi-Factor Authentication