Public/Set-ZbxMaintenance.ps1
|
function Set-ZbxMaintenance { <# .SYNOPSIS Updates the active window of an existing maintenance in Zabbix. .DESCRIPTION Quickly modifies an existing maintenance window's active period. Looks up the maintenance by exact name, updates its ActiveSince and ActiveTill timestamps, and regenerates time-periods as a single one-time entry that matches the new active window. Hosts, host groups, name, description and maintenance type are preserved from the existing maintenance configuration. Designed for the common "quick reschedule" scenario where you need to shift, extend or trigger an existing maintenance window without opening the Zabbix UI. For complex recurring period configurations (daily/weekly/monthly schedules) please use the Zabbix UI instead. .PARAMETER Name Exact name of the existing maintenance window to update. Mandatory. .PARAMETER ActiveSince New start time of the maintenance window in format 'yyyy-MM-dd HH:mm'. Defaults to current date and time when the cmdlet is invoked. .PARAMETER ActiveTill New end time of the maintenance window in format 'yyyy-MM-dd HH:mm'. Defaults to ActiveSince + 1 hour when not specified. .EXAMPLE Set-ZbxMaintenance -Name 'Weekly Patching' Updates 'Weekly Patching' to start now and end 1 hour later. Useful for triggering a pre-configured maintenance immediately. .EXAMPLE Set-ZbxMaintenance -Name 'Emergency Window' -ActiveTill '2026-05-20 22:00' Updates maintenance to start now and end at the specified time. .EXAMPLE Set-ZbxMaintenance -Name 'DB Migration' -ActiveSince '2026-05-25 18:00' -ActiveTill '2026-05-26 06:00' Schedules maintenance for a specific 12-hour window in the future. .EXAMPLE # Extend a currently active maintenance by 2 more hours $maint = Get-ZbxMaintenance -Name 'Server Patch' -Details $newTill = ([DateTime]$maint.'Active till').AddHours(2).ToString('yyyy-MM-dd HH:mm') Set-ZbxMaintenance -Name 'Server Patch' -ActiveSince $maint.'Active since' -ActiveTill $newTill Reads the current window and extends Active till by 2 hours. .NOTES Time-periods design choice: Set-ZbxMaintenance intentionally does NOT expose Zabbix's full time-period configuration (timeperiod_type, every, dayofweek, month, day, start_time). Instead it always generates a single ONE-TIME period that spans the entire ActiveSince -> ActiveTill window. This matches the "quick set" use case and keeps the cmdlet simple. IMPORTANT: Existing time-periods on the maintenance are replaced by the generated one-time period. If the maintenance had recurring (daily/weekly/monthly) periods configured, they will be lost after running Set-ZbxMaintenance. Use the Zabbix UI for maintenances that must keep their recurring schedule. .OUTPUTS PSCustomObject describing the updated maintenance window. #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [string]$ActiveSince = (Get-Date -Format 'yyyy-MM-dd HH:mm'), [Parameter()] [string]$ActiveTill ) # Parse ActiveSince (accepts 'yyyy-MM-dd HH:mm' primarily, falls back to general parse) $sinceDate = $null try { $sinceDate = [DateTime]::ParseExact($ActiveSince, 'yyyy-MM-dd HH:mm', $null) } catch { try { $sinceDate = [DateTime]::Parse($ActiveSince) } catch { throw "Invalid ActiveSince date format: '$ActiveSince'. Expected format: yyyy-MM-dd HH:mm" } } # Resolve ActiveTill: default to ActiveSince + 1 hour when not provided $tillDate = $null if ([string]::IsNullOrWhiteSpace($ActiveTill)) { $tillDate = $sinceDate.AddHours(1) } else { try { $tillDate = [DateTime]::ParseExact($ActiveTill, 'yyyy-MM-dd HH:mm', $null) } catch { try { $tillDate = [DateTime]::Parse($ActiveTill) } catch { throw "Invalid ActiveTill date format: '$ActiveTill'. Expected format: yyyy-MM-dd HH:mm" } } } if ($tillDate -le $sinceDate) { throw "ActiveTill ($($tillDate.ToString('yyyy-MM-dd HH:mm'))) must be later than ActiveSince ($($sinceDate.ToString('yyyy-MM-dd HH:mm'))). Provide a wider window." } # Look up the existing maintenance by exact name $lookupParams = @{ output = 'extend' selectHosts = @('hostid') selectHostGroups = @('groupid') selectGroups = @('groupid') filter = @{ name = $Name } } $existing = Invoke-ZabbixAPI -Method 'maintenance.get' -Params $lookupParams if (-not $existing -or $existing.Count -eq 0) { throw "Maintenance '$Name' not found in Zabbix." } if ($existing.Count -gt 1) { throw "Multiple maintenances found with name '$Name'. Cannot update reliably - please ensure unique names." } $maint = $existing[0] # Convert parsed local times to Unix timestamps (UTC seconds since epoch) $epoch = [DateTime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) $sinceUnix = [int64](($sinceDate.ToUniversalTime() - $epoch).TotalSeconds) $tillUnix = [int64](($tillDate.ToUniversalTime() - $epoch).TotalSeconds) $periodSec = $tillUnix - $sinceUnix # Preserve existing hosts and host groups - if not re-supplied, Zabbix may clear them $groupRefs = @() if ($maint.hostgroups -and $maint.hostgroups.Count -gt 0) { $groupRefs = @($maint.hostgroups | ForEach-Object { @{ groupid = $_.groupid } }) } elseif ($maint.groups -and $maint.groups.Count -gt 0) { $groupRefs = @($maint.groups | ForEach-Object { @{ groupid = $_.groupid } }) } $hostRefs = @() if ($maint.hosts -and $maint.hosts.Count -gt 0) { $hostRefs = @($maint.hosts | ForEach-Object { @{ hostid = $_.hostid } }) } if ($groupRefs.Count -eq 0 -and $hostRefs.Count -eq 0) { throw "Maintenance '$Name' has no hosts or host groups assigned. At least one is required by Zabbix." } # Build update payload with a single one-time period that matches the new window $updateParams = @{ maintenanceid = $maint.maintenanceid active_since = $sinceUnix active_till = $tillUnix timeperiods = @( @{ timeperiod_type = 0 # 0 = one-time start_date = $sinceUnix period = $periodSec } ) } if ($groupRefs.Count -gt 0) { $updateParams['groups'] = $groupRefs } if ($hostRefs.Count -gt 0) { $updateParams['hosts'] = $hostRefs } try { $result = Invoke-ZabbixAPI -Method 'maintenance.update' -Params $updateParams # Human-readable duration $durStr = if ($periodSec -ge 86400) { "{0}d {1}h" -f [int]($periodSec / 86400), [int](($periodSec % 86400) / 3600) } elseif ($periodSec -ge 3600) { "{0}h {1}m" -f [int]($periodSec / 3600), [int](($periodSec % 3600) / 60) } else { "{0}m" -f [int]($periodSec / 60) } [PSCustomObject]@{ PSTypeName = 'psZBX.MaintenanceUpdate' Name = $Name MaintenanceID = $result.maintenanceids[0] 'Active since' = $sinceDate.ToString('yyyy-MM-dd HH:mm:ss') 'Active till' = $tillDate.ToString('yyyy-MM-dd HH:mm:ss') Duration = $durStr Status = 'Updated' } } catch { throw "Failed to update maintenance '$Name': $_" } } |