Extend Veeam job monitoring

This commit is contained in:
2026-07-16 08:58:49 +07:00
commit c67ec238f4
3 changed files with 1016 additions and 0 deletions

583
Monitor-VeeamJobs.ps1 Normal file
View File

@@ -0,0 +1,583 @@
<#
Monitors Veeam Backup & Replication jobs for Zabbix.
Default mode prints one JSON object for a Zabbix Agent 2 UserParameter:
UserParameter=veeam.scripts.monitor,powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\Zabbix Agent 2\scripts\Monitor-VeeamJobs.ps1"
Sender mode pushes the same metrics with zabbix_sender, useful for Task Scheduler:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\Zabbix Agent 2\scripts\Monitor-VeeamJobs.ps1" -Mode Sender
#>
[CmdletBinding()]
param(
[ValidateSet('Json', 'Sender')]
[string]$Mode = 'Json',
[string]$ScriptDir = $PSScriptRoot,
[string]$JobExclusionFilterFile,
[string]$ZabbixSender = 'C:\Program Files\Zabbix Agent 2\zabbix_sender.exe',
[string]$ZabbixConfig = 'C:\Program Files\Zabbix Agent 2\zabbix_agent2.conf',
[string]$ZabbixServer,
[string]$ZabbixHost
)
$ErrorActionPreference = 'Stop'
if ([string]::IsNullOrWhiteSpace($ScriptDir)) {
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
}
if ([string]::IsNullOrWhiteSpace($JobExclusionFilterFile)) {
$JobExclusionFilterFile = Join-Path $ScriptDir 'NotifyOfDisabledJobs_EXCLUSIONS.IN'
}
function Import-VeeamPowerShell {
Add-PSSnapin -Name VeeamPSSnapIn -ErrorAction SilentlyContinue
if (-not (Get-Command Get-VBRJob -ErrorAction SilentlyContinue) -and
-not (Get-Command Get-VBRComputerBackupJob -ErrorAction SilentlyContinue)) {
Import-Module Veeam.Backup.PowerShell -ErrorAction SilentlyContinue
}
if (-not (Get-Command Get-VBRJob -ErrorAction SilentlyContinue) -and
-not (Get-Command Get-VBRComputerBackupJob -ErrorAction SilentlyContinue)) {
throw 'Veeam PowerShell commands are not available. Run the script on the Veeam server or install/load the Veeam PowerShell module.'
}
}
function Get-ZabbixAgentConfigValue {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[string]$Name
)
if (-not (Test-Path -Path $Path)) {
return $null
}
$line = Get-Content -Path $Path |
Where-Object { $_ -match "^\s*$([regex]::Escape($Name))\s*=" -and $_ -notmatch '^\s*#' } |
Select-Object -Last 1
if (-not $line) {
return $null
}
return (($line -split '=', 2)[1]).Trim()
}
function Get-ZabbixServerEndpoints {
param(
[Parameter(Mandatory = $true)]
[string]$Servers
)
$endpoints = @()
foreach ($serverEndpoint in ($Servers -split ',')) {
$serverEndpoint = $serverEndpoint.Trim()
if ([string]::IsNullOrWhiteSpace($serverEndpoint)) {
continue
}
$serverHost = $serverEndpoint
$serverPort = $null
if ($serverEndpoint -match '^(?<host>.+):(?<port>\d+)$') {
$serverHost = $Matches.host
$serverPort = $Matches.port
}
$endpoints += [pscustomobject]@{
Host = $serverHost
Port = $serverPort
}
}
return $endpoints
}
function Get-ObjectValue {
param(
[Parameter(Mandatory = $true)]
[object]$InputObject,
[Parameter(Mandatory = $true)]
[string[]]$Paths
)
foreach ($path in $Paths) {
$current = $InputObject
foreach ($part in ($path -split '\.')) {
if ($null -eq $current) {
break
}
$property = $current.PSObject.Properties[$part]
if ($null -eq $property) {
$current = $null
break
}
$current = $property.Value
}
if ($null -ne $current) {
return $current
}
}
return $null
}
function Get-VeeamJobName {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
$name = Get-ObjectValue -InputObject $Job -Paths @('Name', 'Info.Name')
if ([string]::IsNullOrWhiteSpace($name)) {
return '<unknown>'
}
return $name
}
function ConvertTo-ZabbixDateTime {
param(
[object]$Value
)
if ($null -eq $Value) {
return $null
}
if ($Value -is [datetime]) {
if ($Value -eq [datetime]::MinValue -or $Value -eq [datetime]::MaxValue) {
return $null
}
return $Value.ToString('yyyy-MM-dd HH:mm:ss')
}
$text = [string]$Value
if ([string]::IsNullOrWhiteSpace($text)) {
return $null
}
return $text
}
function Get-VeeamJobScheduleEnabled {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
return Get-ObjectValue -InputObject $Job -Paths @(
'Info.IsScheduleEnabled',
'IsScheduleEnabled',
'ScheduleEnabled',
'ScheduleOptions.Enabled',
'JobScheduleOptions.Enabled'
)
}
function Get-VeeamJobLastResult {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
return Get-ObjectValue -InputObject $Job -Paths @(
'Info.LastResult',
'LastResult',
'Info.LatestRunResult',
'LatestRunResult'
)
}
function Get-VeeamJobLastState {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
return Get-ObjectValue -InputObject $Job -Paths @(
'Info.LastState',
'LastState',
'Info.LatestRunState',
'LatestRunState'
)
}
function Get-VeeamJobLastRun {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
$value = Get-ObjectValue -InputObject $Job -Paths @(
'Info.LatestRunLocal',
'LatestRunLocal',
'Info.LastRunLocal',
'LastRunLocal',
'Info.LastRun',
'LastRun',
'Info.LastStartTime',
'LastStartTime',
'Info.LastEndTime',
'LastEndTime'
)
return ConvertTo-ZabbixDateTime -Value $value
}
function Get-VeeamJobNextRun {
param(
[Parameter(Mandatory = $true)]
[object]$Job
)
$value = Get-ObjectValue -InputObject $Job -Paths @(
'Info.NextRun',
'NextRun',
'ScheduleOptions.NextRun',
'JobScheduleOptions.NextRun',
'ScheduleOptions.NextRunLocal',
'JobScheduleOptions.NextRunLocal'
)
return ConvertTo-ZabbixDateTime -Value $value
}
function Get-VeeamBackupJobName {
param(
[Parameter(Mandatory = $true)]
[object]$Backup
)
return Get-ObjectValue -InputObject $Backup -Paths @(
'JobName',
'Info.JobName',
'Job.Name',
'Name'
)
}
function Get-VeeamRestorePointCounts {
$counts = @{}
if (-not (Get-Command Get-VBRBackup -ErrorAction SilentlyContinue) -or
-not (Get-Command Get-VBRRestorePoint -ErrorAction SilentlyContinue)) {
return $counts
}
foreach ($backup in @(Get-VBRBackup -WarningAction SilentlyContinue)) {
$jobName = Get-VeeamBackupJobName -Backup $backup
if ([string]::IsNullOrWhiteSpace($jobName)) {
continue
}
try {
$restorePointCount = @(Get-VBRRestorePoint -Backup $backup -WarningAction SilentlyContinue).Count
if (-not $counts.ContainsKey($jobName)) {
$counts[$jobName] = 0
}
$counts[$jobName] += $restorePointCount
}
catch {
if (-not $counts.ContainsKey($jobName)) {
$counts[$jobName] = $null
}
}
}
return $counts
}
function Format-ZabbixTextValue {
param(
[object[]]$Values
)
$text = @($Values |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
Sort-Object -Unique) -join '; '
if ([string]::IsNullOrWhiteSpace($text)) {
return '-'
}
return $text
}
function Format-VeeamJobDetailsTable {
param(
[Parameter(Mandatory = $true)]
[object[]]$JobDetails
)
if ($JobDetails.Count -eq 0) {
return '-'
}
$lines = @(
'Name | Enabled | Last run | Next run | Result | Restore points'
'--- | --- | --- | --- | --- | ---'
)
foreach ($job in ($JobDetails | Sort-Object -Property name)) {
$enabled = if ($job.enabled -eq $true) { 'yes' } elseif ($job.enabled -eq $false) { 'no' } else { 'unknown' }
$lastRun = if ([string]::IsNullOrWhiteSpace($job.last_run)) { '-' } else { $job.last_run }
$nextRun = if ([string]::IsNullOrWhiteSpace($job.next_run)) { '-' } else { $job.next_run }
$result = if ([string]::IsNullOrWhiteSpace($job.last_result)) { $job.last_state } else { $job.last_result }
$restorePoints = if ($null -eq $job.restore_points) { '-' } else { $job.restore_points }
$lines += ('{0} | {1} | {2} | {3} | {4} | {5}' -f $job.name, $enabled, $lastRun, $nextRun, $result, $restorePoints)
}
return ($lines -join [Environment]::NewLine)
}
function Format-ZabbixSenderValue {
param(
[object]$Value
)
if ($null -eq $Value) {
return '-'
}
$text = [string]$Value
$text = $text -replace "(`r`n|`n|`r)", ' / '
if ([string]::IsNullOrWhiteSpace($text)) {
return '-'
}
return $text
}
function Get-VeeamJobs {
Import-VeeamPowerShell
$jobs = @()
if (Get-Command Get-VBRJob -ErrorAction SilentlyContinue) {
$jobs += @(Get-VBRJob -WarningAction SilentlyContinue)
}
if (Get-Command Get-VBRComputerBackupJob -ErrorAction SilentlyContinue) {
$jobs += @(Get-VBRComputerBackupJob -WarningAction SilentlyContinue)
}
return $jobs
}
function Get-VeeamJobMetrics {
$allJobs = @(Get-VeeamJobs)
$restorePointCounts = Get-VeeamRestorePointCounts
$jobDetails = @($allJobs | ForEach-Object {
$jobName = Get-VeeamJobName -Job $_
$scheduleEnabled = Get-VeeamJobScheduleEnabled -Job $_
$lastResult = Get-VeeamJobLastResult -Job $_
$lastState = Get-VeeamJobLastState -Job $_
$restorePoints = $null
if ($restorePointCounts.ContainsKey($jobName)) {
$restorePoints = $restorePointCounts[$jobName]
}
[ordered]@{
name = $jobName
enabled = $scheduleEnabled
last_run = Get-VeeamJobLastRun -Job $_
next_run = Get-VeeamJobNextRun -Job $_
last_result = if ($null -eq $lastResult) { $null } else { [string]$lastResult }
last_state = if ($null -eq $lastState) { $null } else { [string]$lastState }
restore_points = $restorePoints
}
})
$disabledJobs = @($allJobs | Where-Object {
$scheduleEnabled = Get-VeeamJobScheduleEnabled -Job $_
$scheduleEnabled -eq $false
})
$errorJobs = @($allJobs | Where-Object {
$lastState = Get-VeeamJobLastState -Job $_
$lastResult = Get-VeeamJobLastResult -Job $_
$lastState -eq 'Failed' -or $lastResult -eq 'Failed'
})
$warningJobs = @($allJobs | Where-Object {
$lastState = Get-VeeamJobLastState -Job $_
$lastResult = Get-VeeamJobLastResult -Job $_
$lastState -eq 'Warning' -or $lastResult -eq 'Warning'
})
$successfulJobs = @($allJobs | Where-Object {
$lastResult = Get-VeeamJobLastResult -Job $_
$lastResult -eq 'Success'
})
$disabledCount = $disabledJobs.Count
$disabledExcludedJobs = @()
if (Test-Path -Path $JobExclusionFilterFile) {
$jobsToExclude = @(Get-Content -Path $JobExclusionFilterFile |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
ForEach-Object { $_.Trim() })
if ($jobsToExclude.Count -gt 0) {
$disabledExcludedJobs = @($disabledJobs | Where-Object {
$jobName = Get-VeeamJobName -Job $_
$jobsToExclude | Where-Object { $jobName -like "*$_*" }
})
$disabledJobsFiltered = @($disabledJobs | Where-Object {
$jobName = Get-VeeamJobName -Job $_
-not ($jobsToExclude | Where-Object { $jobName -like "*$_*" })
})
$disabledJobs = $disabledJobsFiltered
$disabledCount = $disabledJobsFiltered.Count
}
}
[ordered]@{
total = $allJobs.Count
disabled = $disabledCount
successful = $successfulJobs.Count
error = $errorJobs.Count
warning = $warningJobs.Count
disabled_names = Format-ZabbixTextValue -Values @($disabledJobs | ForEach-Object { Get-VeeamJobName -Job $_ })
disabled_excluded_names = Format-ZabbixTextValue -Values @($disabledExcludedJobs | ForEach-Object { Get-VeeamJobName -Job $_ })
error_names = Format-ZabbixTextValue -Values @($errorJobs | ForEach-Object { Get-VeeamJobName -Job $_ })
warning_names = Format-ZabbixTextValue -Values @($warningJobs | ForEach-Object { Get-VeeamJobName -Job $_ })
jobs_table = Format-VeeamJobDetailsTable -JobDetails $jobDetails
jobs = $jobDetails
}
}
function Send-ZabbixMetrics {
param(
[Parameter(Mandatory = $true)]
[System.Collections.IDictionary]$Metrics
)
if (-not (Test-Path -Path $ZabbixSender)) {
throw "zabbix_sender.exe was not found: $ZabbixSender"
}
if ([string]::IsNullOrWhiteSpace($ZabbixServer)) {
$ZabbixServer = Get-ZabbixAgentConfigValue -Path $ZabbixConfig -Name 'ServerActive'
}
if ([string]::IsNullOrWhiteSpace($ZabbixServer)) {
$ZabbixServer = Get-ZabbixAgentConfigValue -Path $ZabbixConfig -Name 'Server'
}
if ([string]::IsNullOrWhiteSpace($ZabbixHost)) {
$ZabbixHost = Get-ZabbixAgentConfigValue -Path $ZabbixConfig -Name 'Hostname'
}
if ([string]::IsNullOrWhiteSpace($ZabbixHost)) {
$hostnameItem = Get-ZabbixAgentConfigValue -Path $ZabbixConfig -Name 'HostnameItem'
if ([string]::IsNullOrWhiteSpace($hostnameItem) -or $hostnameItem -eq 'system.hostname') {
$ZabbixHost = $env:COMPUTERNAME
}
}
if ([string]::IsNullOrWhiteSpace($ZabbixServer)) {
throw "Zabbix server was not provided and was not found in $ZabbixConfig"
}
if ([string]::IsNullOrWhiteSpace($ZabbixHost)) {
throw "Zabbix hostname was not provided, was not found in $ZabbixConfig, and COMPUTERNAME is empty. Pass -ZabbixHost with the exact Zabbix host name."
}
$serverEndpoints = @(Get-ZabbixServerEndpoints -Servers $ZabbixServer)
if ($serverEndpoints.Count -eq 0) {
throw "No usable Zabbix server endpoints were found in: $ZabbixServer"
}
$senderInput = New-TemporaryFile
try {
Write-Host "Sending as host: $ZabbixHost"
@(
"`"$ZabbixHost`" veeam.jobs.total $($Metrics.total)"
"`"$ZabbixHost`" veeam.jobs.disabled $($Metrics.disabled)"
"`"$ZabbixHost`" veeam.jobs.successful $($Metrics.successful)"
"`"$ZabbixHost`" veeam.jobs.error $($Metrics.error)"
"`"$ZabbixHost`" veeam.jobs.warning $($Metrics.warning)"
"`"$ZabbixHost`" veeam.jobs.disabled.names $(Format-ZabbixSenderValue -Value $Metrics.disabled_names)"
"`"$ZabbixHost`" veeam.jobs.disabled.excluded.names $(Format-ZabbixSenderValue -Value $Metrics.disabled_excluded_names)"
"`"$ZabbixHost`" veeam.jobs.error.names $(Format-ZabbixSenderValue -Value $Metrics.error_names)"
"`"$ZabbixHost`" veeam.jobs.warning.names $(Format-ZabbixSenderValue -Value $Metrics.warning_names)"
"`"$ZabbixHost`" veeam.jobs.table $(Format-ZabbixSenderValue -Value $Metrics.jobs_table)"
) | Set-Content -Path $senderInput -Encoding ASCII
$failedEndpoints = @()
foreach ($serverEndpoint in $serverEndpoints) {
$senderArgs = @('-z', $serverEndpoint.Host, '-i', $senderInput)
if (-not [string]::IsNullOrWhiteSpace($serverEndpoint.Port)) {
$senderArgs += @('-p', $serverEndpoint.Port)
}
$displayEndpoint = $serverEndpoint.Host
if (-not [string]::IsNullOrWhiteSpace($serverEndpoint.Port)) {
$displayEndpoint = "$displayEndpoint`:$($serverEndpoint.Port)"
}
Write-Host "Sending to Zabbix endpoint: $displayEndpoint"
& $ZabbixSender @senderArgs
if ($LASTEXITCODE -eq 0) {
return
}
$failedEndpoints += "$displayEndpoint (exit code $LASTEXITCODE)"
}
throw "zabbix_sender failed for all configured endpoints: $($failedEndpoints -join '; ')"
}
finally {
Remove-Item -Path $senderInput -Force -ErrorAction SilentlyContinue
}
}
try {
$metrics = Get-VeeamJobMetrics
if ($Mode -eq 'Sender') {
Send-ZabbixMetrics -Metrics $metrics
}
else {
$metrics | ConvertTo-Json -Depth 5 -Compress
}
}
catch {
Write-Error $_.Exception.Message
exit 1
}

63
README.md Normal file
View File

@@ -0,0 +1,63 @@
# Veeam Backup Jobs for Zabbix
This repository contains a PowerShell collector and a Zabbix 7.0 template for
monitoring Veeam Backup & Replication jobs.
## Collector output
`Monitor-VeeamJobs.ps1` prints one compressed JSON object by default:
```json
{
"total": 10,
"disabled": 1,
"successful": 7,
"error": 1,
"warning": 1,
"disabled_names": "Disabled job",
"disabled_excluded_names": "Policy disabled by design",
"error_names": "Failed job",
"warning_names": "Warning job",
"jobs_table": "Name | Enabled | Last run | Next run | Result | Restore points\n...",
"jobs": [
{
"name": "Daily backup",
"enabled": true,
"last_run": "2026-07-16 01:00:00",
"next_run": "2026-07-17 01:00:00",
"last_result": "Success",
"last_state": "Stopped",
"restore_points": 14
}
]
}
```
Restore point counting is best effort. The script uses `Get-VBRBackup` and
`Get-VBRRestorePoint` when both cmdlets are available; if Veeam cannot map a job
to backups in the current environment, the job remains visible and its restore
point count is shown as `-`.
## Zabbix dashboard idea
Create a dashboard named `Veeam Jobs` for hosts linked to the `Veeam Backup Jobs`
template.
Suggested widgets:
| Area | Widget | Items |
| --- | --- | --- |
| Top row | Item value | `veeam.jobs.total` |
| Top row | Item value | `veeam.jobs.successful` |
| Top row | Item value | `veeam.jobs.disabled` |
| Top row | Item value | `veeam.jobs.error` |
| Top row | Item value | `veeam.jobs.warning` |
| Middle | Plain text | `veeam.jobs.table` |
| Bottom left | Plain text | `veeam.jobs.error.names` |
| Bottom center | Plain text | `veeam.jobs.warning.names` |
| Bottom right | Plain text | `veeam.jobs.disabled.names` |
Use a red threshold for `veeam.jobs.error`, yellow for `veeam.jobs.warning`, and
keep `veeam.jobs.disabled` aligned with the existing exclusion policy. The
template intentionally keeps `veeam.jobs.disabled.excluded.names`, because some
disabled policies are expected.

View File

@@ -0,0 +1,370 @@
<?xml version="1.0" encoding="UTF-8"?>
<zabbix_export>
<version>7.0</version>
<template_groups>
<template_group>
<uuid>6822b9e565e14f159eb87e271cc98988</uuid>
<name>Templates/Backup</name>
</template_group>
</template_groups>
<templates>
<template>
<uuid>aba24cc8492744808e216c4a3d9ff0ea</uuid>
<template>Veeam Backup Jobs</template>
<name>Veeam Backup Jobs</name>
<description>Monitors Veeam Backup &amp; Replication job states via Zabbix Agent 2 UserParameter.</description>
<groups>
<group>
<name>Templates/Backup</name>
</group>
</groups>
<items>
<item>
<uuid>30c699d3e87e4c2d9354837cc202ed5f</uuid>
<name>Veeam: Monitor</name>
<type>ZABBIX_PASSIVE</type>
<key>veeam.scripts.monitor</key>
<delay>15m</delay>
<value_type>TEXT</value_type>
<description>Raw JSON from Monitor-VeeamJobs.ps1 via UserParameter.</description>
<history>0</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>107474afe229462b8f3642e2637a89d9</uuid>
<name>Veeam: Total jobs</name>
<type>DEPENDENT</type>
<key>veeam.jobs.total</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>UNSIGNED</value_type>
<description>Total number of Veeam backup jobs.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.total</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>365d</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>1836afb72c2c47248497f83116154cae</uuid>
<name>Veeam: Disabled jobs</name>
<type>DEPENDENT</type>
<key>veeam.jobs.disabled</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>UNSIGNED</value_type>
<description>Number of disabled Veeam jobs (after exclusion filter).</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.disabled</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>365d</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>ec3eca9a55924ca4985a7c848c49663e</uuid>
<expression>last(/Veeam Backup Jobs/veeam.jobs.disabled)&gt;0 and last(/Veeam Backup Jobs/veeam.jobs.disabled.names)&lt;&gt;&quot;&quot; and last(/Veeam Backup Jobs/veeam.jobs.disabled.names)&lt;&gt;&quot;-&quot; and last(/Veeam Backup Jobs/veeam.jobs.disabled.excluded.names)&lt;&gt;&quot;&quot;</expression>
<name>Disabled Veeam jobs detected: {ITEM.LASTVALUE2}</name>
<priority>HIGH</priority>
<description>There are disabled Veeam backup jobs. Excluded disabled jobs: {ITEM.LASTVALUE3}</description>
<status>ENABLED</status>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</trigger>
</triggers>
</item>
<item>
<uuid>bcef9c99992e4fb28737740c9d8a2c0f</uuid>
<name>Veeam: Error jobs</name>
<type>DEPENDENT</type>
<key>veeam.jobs.error</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>UNSIGNED</value_type>
<description>Number of Veeam jobs with errors.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.error</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>365d</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>b7cc8e4cba7e42ca912acd03eabc3073</uuid>
<expression>last(/Veeam Backup Jobs/veeam.jobs.error)&gt;5 and last(/Veeam Backup Jobs/veeam.jobs.error.names)&lt;&gt;&quot;&quot; and last(/Veeam Backup Jobs/veeam.jobs.error.names)&lt;&gt;&quot;-&quot;</expression>
<name>More than 5 Veeam jobs in error state: {ITEM.LASTVALUE2}</name>
<priority>HIGH</priority>
<description>More than 5 Veeam backup jobs have failed.</description>
<status>ENABLED</status>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</trigger>
</triggers>
</item>
<item>
<uuid>56a111fea6794fa5ada55b86b8cf39f5</uuid>
<name>Veeam: Successful jobs</name>
<type>DEPENDENT</type>
<key>veeam.jobs.successful</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>UNSIGNED</value_type>
<description>Number of Veeam jobs whose latest run completed successfully.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.successful</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>365d</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>f872f71af61948bfb397167793a783de</uuid>
<name>Veeam: Disabled job names</name>
<type>DEPENDENT</type>
<key>veeam.jobs.disabled.names</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>TEXT</value_type>
<description>Names of disabled Veeam jobs (after exclusion filter).</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.disabled_names</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>91be94066325497dba8d7ff16bcbd014</uuid>
<name>Veeam: Excluded disabled job names</name>
<type>DEPENDENT</type>
<key>veeam.jobs.disabled.excluded.names</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>TEXT</value_type>
<description>Names of disabled Veeam jobs ignored by the exclusion filter.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.disabled_excluded_names</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>e4cd149f583249608d4d5fe65eaae780</uuid>
<name>Veeam: Error job names</name>
<type>DEPENDENT</type>
<key>veeam.jobs.error.names</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>TEXT</value_type>
<description>Names of Veeam jobs with errors.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.error_names</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>e167e78b395d4c40ae7ddaba4e60c118</uuid>
<name>Veeam: Warning jobs</name>
<type>DEPENDENT</type>
<key>veeam.jobs.warning</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>UNSIGNED</value_type>
<description>Number of Veeam jobs with warnings.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.warning</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>365d</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
<triggers>
<trigger>
<uuid>447f923c2377452698f3964e063aa8e7</uuid>
<expression>last(/Veeam Backup Jobs/veeam.jobs.warning)&gt;0 and last(/Veeam Backup Jobs/veeam.jobs.warning.names)&lt;&gt;&quot;&quot; and last(/Veeam Backup Jobs/veeam.jobs.warning.names)&lt;&gt;&quot;-&quot;</expression>
<name>Veeam jobs in warning state: {ITEM.LASTVALUE2}</name>
<priority>WARNING</priority>
<description>One or more Veeam backup jobs have warnings.</description>
<status>ENABLED</status>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</trigger>
</triggers>
</item>
<item>
<uuid>80d706f12efa4b98b051d3fcf35ccab1</uuid>
<name>Veeam: Warning job names</name>
<type>DEPENDENT</type>
<key>veeam.jobs.warning.names</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>TEXT</value_type>
<description>Names of Veeam jobs with warnings.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.warning_names</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
<item>
<uuid>3925f767110e4f0aa74d6b996ff1f1c9</uuid>
<name>Veeam: Job details table</name>
<type>DEPENDENT</type>
<key>veeam.jobs.table</key>
<delay>0</delay>
<master_item>
<key>veeam.scripts.monitor</key>
</master_item>
<value_type>TEXT</value_type>
<description>Text table with all Veeam jobs: name, enabled state, last run, next run, latest result, and restore point count.</description>
<preprocessing>
<step>
<type>JSONPATH</type>
<parameters>
<parameter>$.jobs_table</parameter>
</parameters>
</step>
</preprocessing>
<history>90d</history>
<trends>0</trends>
<tags>
<tag>
<tag>component</tag>
<value>backup</value>
</tag>
</tags>
</item>
</items>
</template>
</templates>
</zabbix_export>