Guide technique ANYSYS · vérifié le 26 octobre 2026

Veeam ONE 13 : API REST et rapport PowerShell en lecture seule

Horloge de table à mouvement squelette, laiton doré
Horloge de table à mouvement squelette, laiton doréThe Metropolitan Museum of Art, The Met via Wikimedia Commons, CC0 · Crédits

Authentification OAuth, pagination et filtres de l’API REST Veeam ONE 2.3, puis un script PowerShell qui relève, sans rien modifier, les serveurs VBR, les alarmes actives et les VM dont la dernière sauvegarde est trop ancienne. Pour Veeam ONE 13.1.

01 / Périmètre

Périmètre : une API documentée pour la v13

L’API REST de Veeam ONE est documentée officiellement par Veeam pour la version 13 : la référence Veeam ONE REST API 2.3 indique s’appliquer au build 13.1.0.7034. Ce guide cible donc Veeam ONE 13.1 (correctif P0 : 13.1.0.7233) et l’API en version 2.3.

  • URL de base : https://<hostname>:<port>/api/<version>, où hostname est la machine qui héberge Veeam ONE Web Services.
  • Port : celui choisi à l’installation de Web Services, 1239 par défaut (le même que le Web Client).
  • Méthodes : GET lit une ressource ou une collection, POST crée une ressource ou déclenche une action, PATCH modifie une ressource ou exécute une opération (par exemple le planning de collecte ou la mise à jour de licence).
  • Une interface Swagger UI est disponible sur https://<hostname>:<port>/swagger/index.html pour explorer l’API.

02 / Authentification

Authentification OAuth 2.0

L’autorisation suit OAuth 2.0 : le client obtient un jeton d’accès et un jeton de rafraîchissement.

  1. 01

    Ouverture : POST /api/token, corps application/x-www-form-urlencoded avec grant_type=password, username et password. Si la MFA est activée sur le compte, le code est passé dans l’en-tête Veeam-Mfa-Code.

  2. 02

    Appels : en-tête Authorization: Bearer <access_token>.

  3. 03

    Rafraîchissement : POST /api/token avec grant_type=refresh_token et le jeton de rafraîchissement sauvegardé.

  4. 04

    Fermeture : POST /api/revoke avec le jeton d’accès. Avec le paramètre token, seul ce jeton est révoqué ; les autres jetons obtenus restent actifs.

PowerShell
# Ouverture de session (aucune modification de configuration)
$cred = Get-Credential
$body = @{ grant_type = 'password'; username = $cred.UserName; password = $cred.GetNetworkCredential().Password }
$tok  = Invoke-RestMethod -Method Post -Uri 'https://vone01.contoso.local:1239/api/token' `
            -Body $body -ContentType 'application/x-www-form-urlencoded'
$h    = @{ Authorization = "Bearer $($tok.access_token)" }

# Lecture
Invoke-RestMethod -Uri 'https://vone01.contoso.local:1239/api/v2.3/about' -Headers $h

# Fermeture de session
Invoke-RestMethod -Method Post -Uri 'https://vone01.contoso.local:1239/api/revoke' -Headers $h `
    -Body @{ token = $tok.access_token } -ContentType 'application/x-www-form-urlencoded'

03 / Sécurité

Compte, secret et certificat

  • Compte dédié à l’automatisation, jamais un administrateur personnel. L’exemple de la documentation utilise un administrateur local (rôle Admin dans la réponse) ; Veeam ONE propose aussi le groupe Veeam ONE Read-Only Users. Le comportement de l’API pour un compte en lecture seule n’est pas décrit dans la référence : à vérifier dans votre version, en testant les appels du script avec ce compte (une réponse 403 Forbidden est documentée sur les GET).
  • Secret : pas de mot de passe en clair dans le script ni dans une tâche planifiée. Utilisez Get-Credential en interactif, ou le coffre de secrets de votre organisation pour une exécution planifiée.
  • MFA : si le compte a la MFA, le script accepte -MfaCode ; une exécution sans surveillance impose alors une décision de sécurité explicite sur ce compte.
  • Certificat : par défaut, l’installation génère un certificat autosigné si aucun certificat adéquat n’est trouvé. Installez un certificat de votre PKI sur Web Services. Le commutateur -SkipCertificateCheck du script (PowerShell 7 seulement) est réservé au test.

04 / Requêtes

Pagination, filtres et sélection

Les collections acceptent Offset, Limit, Sort, Filter et Select. Dans la référence 2.3, Limit vaut 100 et Offset 0 par défaut ; Offset s’applique avant Limit quelle que soit leur position. Chaque page renvoie items et totalCount, ce qui permet de boucler jusqu’au total.

Filter prend un objet JSON (property, operation, collation, value), combinable avec and, or, not ou exclusiveOr. Opérateurs : in, contains, subset, superset, equals, notEquals, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual. La collation par défaut est ignorecase.

PowerShell
# Exemple de la documentation : tâches de collecte en échec (lecture)
$f = '{"property":"status","operation":"equals","collation":"ignorecase","value":"failed"}'
$uri = 'https://vone01.contoso.local:1239/api/v2.3/dataCollection/tasks?Filter=' + [uri]::EscapeDataString($f)
Invoke-RestMethod -Uri $uri -Headers $h

05 / Endpoints

Les endpoints utilisés

Noms de champs relevés dans la spécification OpenAPI publiée avec la référence REST Veeam ONE 13.
Endpoint (GET)ContenuChamps repris par le script
/api/v2.3/aboutInformations sur le serveur Veeam ONE et le service de reportingmachine, version
/api/v2.3/vbr/backupServersServeurs VBR connectésname, version, connectionState (Unknown, Connected, NotResponding), isConfigurationBackupEnabled, bestPracticeCheckStatus
/api/v2.3/alarms/triggeredAlarmsToutes les alarmes déclenchéesstatus (Success, Resolved, Information, Warning, Error, Acknowledged, Remediated, Unknown), name, alarmSource, triggeredTime
/api/v2.3/protectedData/virtualMachinesToutes les VM protégéesname, platform, jobName, lastProtectedDate (date du dernier point de restauration)

06 / Script

Le script de relevé en lecture seule

Get-VeeamOneHealthReport.ps1 ouvre une session, lit les quatre endpoints page par page, garde par défaut les alarmes en Error et Warning, marque comme Stale les VM protégées dont le dernier point a plus de 24 heures (paramétrable), puis révoque le jeton. Il renvoie un objet PowerShell et peut écrire trois CSV.

Get-VeeamOneHealthReport.ps1PowerShell · 221 lignesTélécharger
<#
.SYNOPSIS
    Read-only health report from the Veeam ONE REST API (v2.3, Veeam ONE 13.x).

.DESCRIPTION
    Authenticates against the Veeam ONE REST API (OAuth 2.0 password grant on /api/token),
    then reads, with GET requests only:
      - /api/v2.3/about                         Veeam ONE server information
      - /api/v2.3/vbr/backupServers             connected Veeam Backup & Replication servers
      - /api/v2.3/alarms/triggeredAlarms        triggered alarms (Error/Warning kept by default)
      - /api/v2.3/protectedData/virtualMachines protected VMs and their latest restore point
    Collections are read page by page with the documented Offset and Limit query parameters.
    At the end the access token is revoked (/api/revoke). Nothing is changed in Veeam ONE.

.PARAMETER Server
    DNS name or IP address of the machine running Veeam ONE Web Services.

.PARAMETER Port
    REST API port chosen during setup. Veeam documents 1239 as the default.

.PARAMETER Credential
    Account allowed to sign in to Veeam ONE. Prefer a dedicated, least-privileged account.

.PARAMETER MfaCode
    One-time code, only if multi-factor authentication is enabled for the account
    (sent in the Veeam-Mfa-Code header documented for /api/token).

.PARAMETER StaleHours
    A protected VM whose latest restore point is older than this is flagged as stale. Default 24.

.PARAMETER IncludeAllAlarmStates
    Keep every triggered alarm (Resolved, Acknowledged, Information...). Default: Error and Warning only.

.PARAMETER PageSize
    Items per request (Limit). Default 100, the documented default value.

.PARAMETER OutputFolder
    Optional folder: one CSV per section is written there (semicolon separated, UTF-8).

.PARAMETER SkipCertificateCheck
    PowerShell 7 only. Accepts a self-signed Veeam ONE certificate for this session.
    Prefer installing a trusted certificate on Veeam ONE Web Services instead.

.EXAMPLE
    $cred = Get-Credential
    .\Get-VeeamOneHealthReport.ps1 -Server vone01.contoso.local -Credential $cred

.EXAMPLE
    .\Get-VeeamOneHealthReport.ps1 -Server vone01.contoso.local -Credential $cred -StaleHours 36 -OutputFolder C:\Reports\VeeamONE

.NOTES
    Read-only: GET requests on data endpoints; POST only on /api/token (sign-in) and /api/revoke (sign-out).
    Requires Windows PowerShell 5.1 or PowerShell 7. Endpoints and fields from the Veeam ONE 13 REST API
    reference (API version 2.3). Test against a non-production Veeam ONE server first.
    Source: ANYSYS ICT, https://www.anysys.be/articles/
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [string]$Server,

    [ValidateRange(1, 65535)]
    [int]$Port = 1239,

    [Parameter(Mandatory)]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credential,

    [string]$MfaCode,

    [ValidateRange(1, 8760)]
    [int]$StaleHours = 24,

    [switch]$IncludeAllAlarmStates,

    [ValidateRange(1, 1000)]
    [int]$PageSize = 100,

    [string]$OutputFolder,

    [switch]$SkipCertificateCheck
)

$ErrorActionPreference = 'Stop'
$baseUri = 'https://{0}:{1}' -f $Server, $Port
$apiRoot = "$baseUri/api/v2.3"

# Common parameters for every Invoke-RestMethod call
$irmCommon = @{}
if ($SkipCertificateCheck) {
    if ($PSVersionTable.PSVersion.Major -lt 7) {
        throw '-SkipCertificateCheck requires PowerShell 7. Install a trusted certificate on Veeam ONE Web Services instead.'
    }
    $irmCommon['SkipCertificateCheck'] = $true
}
if ($PSVersionTable.PSVersion.Major -lt 6) {
    # Windows PowerShell 5.1: make sure TLS 1.2 is allowed for this session only
    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
}

function Get-VoneCollection {
    <# Reads every page of a collection endpoint using Offset/Limit and returns the items. #>
    param(
        [Parameter(Mandatory)][string]$Path,
        [Parameter(Mandatory)][hashtable]$Headers,
        [Parameter(Mandatory)][int]$Limit
    )
    $offset = 0
    $all = New-Object System.Collections.Generic.List[object]
    do {
        $uri = '{0}{1}?Offset={2}&Limit={3}' -f $apiRoot, $Path, $offset, $Limit
        $page = Invoke-RestMethod -Method Get -Uri $uri -Headers $Headers @irmCommon
        $items = @($page.items)
        foreach ($i in $items) { $all.Add($i) }
        $offset += $items.Count
        $total = [int]$page.totalCount
    } while ($items.Count -gt 0 -and $offset -lt $total)
    return $all.ToArray()
}

# 1. Sign in: OAuth 2.0 password grant (documented on /api/token)
$tokenHeaders = @{}
if ($MfaCode) { $tokenHeaders['Veeam-Mfa-Code'] = $MfaCode }
$tokenBody = @{
    grant_type = 'password'
    username   = $Credential.UserName
    password   = $Credential.GetNetworkCredential().Password
}
$token = Invoke-RestMethod -Method Post -Uri "$baseUri/api/token" -Headers $tokenHeaders `
    -Body $tokenBody -ContentType 'application/x-www-form-urlencoded' @irmCommon
$tokenBody = $null
$headers = @{ Authorization = "Bearer $($token.access_token)" }
Write-Verbose ("Signed in as {0} (role: {1})" -f $token.user, $token.user_role)

try {
    # 2. Read-only data collection
    $about   = Invoke-RestMethod -Method Get -Uri "$apiRoot/about" -Headers $headers @irmCommon
    $servers = Get-VoneCollection -Path '/vbr/backupServers' -Headers $headers -Limit $PageSize
    $alarms  = Get-VoneCollection -Path '/alarms/triggeredAlarms' -Headers $headers -Limit $PageSize
    $vms     = Get-VoneCollection -Path '/protectedData/virtualMachines' -Headers $headers -Limit $PageSize

    $backupServers = foreach ($s in $servers) {
        [pscustomobject]@{
            Name                  = $s.name
            Version               = $s.version
            Platform              = $s.platform
            ConnectionState       = $s.connectionState
            ConnectionError       = $s.connectionError
            ConfigBackupEnabled   = $s.isConfigurationBackupEnabled
            BestPracticeStatus    = $s.bestPracticeCheckStatus
            LastBestPracticeCheck = $s.lastBestPracticeCheckDate
            IntelligentDiagnostics = $s.intelligentDiagnosticsEnabled
        }
    }

    $keptStates = @('Error', 'Warning')
    $activeAlarms = foreach ($a in $alarms) {
        if (-not $IncludeAllAlarmStates -and ($keptStates -notcontains [string]$a.status)) { continue }
        [pscustomobject]@{
            Status      = $a.status
            Name        = $a.name
            Object      = $a.alarmSource.objectName
            ObjectType  = $a.alarmSource.objectType
            Triggered   = $a.triggeredTime
            RepeatCount = $a.repeatCount
            Description = $a.description
        }
    }
    $activeAlarms = @($activeAlarms | Sort-Object Status, Triggered)

    $staleLimit = (Get-Date).ToUniversalTime().AddHours(-$StaleHours)
    $protectedVms = foreach ($v in $vms) {
        $last = $null
        if ($v.lastProtectedDate) { $last = ([datetime]$v.lastProtectedDate).ToUniversalTime() }
        [pscustomobject]@{
            Name             = $v.name
            Platform         = $v.platform
            Host             = $v.parentHostName
            BackupServer     = $v.backupServerName
            Job              = $v.jobName
            LastRestorePoint = $last
            Stale            = ($null -eq $last) -or ($last -lt $staleLimit)
        }
    }
    $protectedVms = @($protectedVms)
    $staleVms = @($protectedVms | Where-Object Stale)

    # 3. Optional CSV export
    if ($OutputFolder) {
        $null = New-Item -ItemType Directory -Path $OutputFolder -Force
        $csv = @{ NoTypeInformation = $true; Delimiter = ';'; Encoding = 'UTF8' }
        $backupServers | Export-Csv -Path (Join-Path $OutputFolder 'vone-backup-servers.csv') @csv
        $activeAlarms  | Export-Csv -Path (Join-Path $OutputFolder 'vone-alarms.csv') @csv
        $protectedVms  | Export-Csv -Path (Join-Path $OutputFolder 'vone-protected-vms.csv') @csv
    }

    [pscustomobject]@{
        VeeamOneServer   = $about.machine
        VeeamOneVersion  = $about.version
        CollectedAtUtc   = (Get-Date).ToUniversalTime()
        BackupServers    = $backupServers
        ServersNotOk     = @($backupServers | Where-Object { $_.ConnectionState -ne 'Connected' }).Count
        Alarms           = $activeAlarms
        AlarmErrors      = @($activeAlarms | Where-Object { $_.Status -eq 'Error' }).Count
        AlarmWarnings    = @($activeAlarms | Where-Object { $_.Status -eq 'Warning' }).Count
        ProtectedVms     = $protectedVms
        StaleVms         = $staleVms
        StaleThresholdH  = $StaleHours
    }
}
finally {
    # 4. Sign out: revoke the access token (documented on /api/revoke)
    try {
        Invoke-RestMethod -Method Post -Uri "$baseUri/api/revoke" -Headers $headers `
            -Body @{ token = $token.access_token } -ContentType 'application/x-www-form-urlencoded' @irmCommon | Out-Null
    }
    catch {
        Write-Warning "Token revocation failed: $($_.Exception.Message)"
    }
}
PowerShell
$cred = Get-Credential   # compte dédié, voir section 03
$r = .\Get-VeeamOneHealthReport.ps1 -Server vone01.contoso.local -Credential $cred -StaleHours 26 `
         -OutputFolder 'D:\Reports\VeeamONE'
$r | Select-Object VeeamOneServer, VeeamOneVersion, ServersNotOk, AlarmErrors, AlarmWarnings, StaleThresholdH
$r.Alarms   | Format-Table Status, Name, Object, Triggered
$r.StaleVms | Sort-Object LastRestorePoint | Format-Table Name, Job, LastRestorePoint

07 / Exploitation

Exploiter le résultat

Exemple de sortie (illustratif), valeurs fictives :

Exemple de sortie (illustratif)
VeeamOneServer  : VONE01
VeeamOneVersion : 13.1.0.7233
ServersNotOk    : 1
AlarmErrors     : 1
AlarmWarnings   : 3
StaleThresholdH : 26
  • ServersNotOk > 0 : un serveur VBR n’est pas en Connected. Tant que la collecte est coupée, les alarmes de ce serveur ne sont plus fiables : à traiter en premier.
  • StaleVms : à rapprocher du rapport Protected VMs. Une VM peut apparaître une fois par job (le champ jobName est porté par chaque élément) : dédoublonnez avant de compter.
  • Alarms : la liste brute ; la priorisation et le réglage des alarmes sont traités dans alarmes et notifications.
  • Pour une exécution quotidienne, planifiez le script sur une machine d’administration et envoyez les CSV vers votre outil de ticket ou de supervision. Le pack de rapports à destination de la direction est dans rapports et Business View.

08 / Pièges

Pièges et limites

  • Version d’API dans l’URL : le script appelle /api/v2.3. Après une mise à niveau, consultez le changelog de la référence REST avant de changer de version.
  • Durée du jeton : la réponse de /api/token contient expires_in. Le script est court et ne rafraîchit pas le jeton ; pour une extraction très longue, ajoutez le rafraîchissement documenté.
  • Windows PowerShell 5.1 : le script active TLS 1.2 pour la session et refuse -SkipCertificateCheck, qui n’existe qu’en PowerShell 7.
  • Révocation : si le script est interrompu avant la fin, le jeton n’est pas révoqué et expire de lui-même.
  • Volumes : -PageSize règle la taille de page ; restez raisonnable sur une instance chargée et planifiez hors des fenêtres de rapports lourds.

09 / Références

Sources