Public/Get-ZbxProblem.ps1

function Get-ZbxProblem {
    <#
    .SYNOPSIS
        Gets current active problems from Zabbix server.
    .DESCRIPTION
        Queries Zabbix API (method problem.get) for active/unresolved problems
        with various filtering options including severity, acknowledgement
        status, host name and count limits. For historical/resolved events use
        Get-ZbxEvent instead.

        Each result includes a LastValue field with the current value of the
        trigger's first item (the value behind '{ITEM.LASTVALUE}' macro) - the
        same number you see in the "Operational data" column of the Zabbix
        Monitoring -> Problems view when the default opdata template is used.

        Each result also includes a URL field with a direct link to the event
        detail page in the Zabbix web UI (/tr_events.php), useful for clicking
        through from the console.
    .PARAMETER Severity
        Problem severity level. Valid values: disaster, high, average, warning,
        information, "not classified", or * for all. Default is "disaster".
    .PARAMETER HostName
        Filter by host name. Use * for all hosts. Supports partial matching with wildcards.
        Default is * (all hosts).
    .PARAMETER ACK
        Filter by acknowledgement status. Valid values: Acknowledged,
        Unacknowledged, or * for both. Default is *.
    .PARAMETER Last
        Limit results to last N problems. Use * for all. Default is *.
    .PARAMETER ShowSuppressed
        Include suppressed problems in results. By default suppressed problems
        are hidden.
    .EXAMPLE
        Get-ZbxProblem

        Gets all disaster-level active problems (default severity).
    .EXAMPLE
        Get-ZbxProblem -Severity high -Last 5

        Gets last 5 high severity problems.
    .EXAMPLE
        Get-ZbxProblem -Severity disaster -HostName 'webserver01.example.com'

        Gets disaster-level problems for specific host (exact match).
    .EXAMPLE
        Get-ZbxProblem -Severity * -HostName 'web*'

        Gets all problems for hosts matching pattern 'web*' (partial match).
    .EXAMPLE
        Get-ZbxProblem -Severity disaster -ACK Unacknowledged

        Gets only unacknowledged disaster problems (require attention).
    .EXAMPLE
        Get-ZbxProblem -Severity * -ShowSuppressed

        Gets all problems including suppressed (maintenance/silenced) ones.
    .EXAMPLE
        Get-ZbxProblem -Severity disaster | Export-Csv -Path 'problems.csv' -NoTypeInformation

        Exports disaster problems to CSV file.
    .EXAMPLE
        $critical = Get-ZbxProblem -Severity disaster -ACK Unacknowledged
        if ($critical) {
            Write-Host "ALERT: $($critical.Count) unacknowledged disaster problems!" -ForegroundColor Red
            $critical | Format-Table EventTime, HostName, Problem -AutoSize
        }

        Script example for monitoring unacknowledged critical issues.
    .EXAMPLE
        Get-ZbxProblem -Severity disaster -ACK Unacknowledged | ForEach-Object { Start-Process $_.URL }

        Opens every unacknowledged disaster problem in the default web browser.
    .OUTPUTS
        PSCustomObject[] with one object per problem.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateSet('disaster', 'high', 'average', 'warning', 'information', 'not classified', '*')]
        [string]$Severity = 'disaster',

        [Parameter()]
        [string]$HostName = '*',

        [Parameter()]
        [ValidateSet('Acknowledged', 'Unacknowledged', '*')]
        [string]$ACK = '*',

        [Parameter()]
        [string]$Last = '*',

        [Parameter()]
        [switch]$ShowSuppressed
    )

    # Resolve host name to host IDs (if filtering by host)
    $hostIds = Resolve-ZbxHostId -NamePattern $HostName
    if ($hostIds -is [array] -and $hostIds.Count -eq 0) {
        Write-Warning "No hosts found matching '$HostName'."
        return
    }

    # Build API parameters
    $apiParams = @{
        output                = 'extend'
        selectTags            = 'extend'
        selectAcknowledges    = 'extend'
        selectSuppressionData = 'extend'
        sortfield             = @('eventid')
        sortorder             = 'DESC'
        recent                = $true
    }

    if ($hostIds) { $apiParams['hostids'] = $hostIds }

    # Severity filter
    if ($Severity -ne '*') {
        $severityCode = Get-SeverityCode -SeverityName $Severity
        if ($severityCode -ge 0) {
            $apiParams['severities'] = @($severityCode)
        }
    }

    # ACK filter
    if ($ACK -eq 'Acknowledged')   { $apiParams['acknowledged'] = $true  }
    if ($ACK -eq 'Unacknowledged') { $apiParams['acknowledged'] = $false }

    # Suppressed filter
    if (-not $ShowSuppressed) { $apiParams['suppressed'] = $false }

    # Limit
    if ($Last -ne '*' -and $Last -match '^\d+$') {
        $apiParams['limit'] = [int]$Last
    }

    try {
        $problems = Invoke-ZabbixAPI -Method 'problem.get' -Params $apiParams

        if (-not $problems -or $problems.Count -eq 0) {
            Write-Verbose "No problems found matching the specified criteria."
            return
        }

        # Collect unique trigger IDs to fetch host info, maintenance status and last values
        $objectIds = $problems |
            Where-Object { $_.source -eq '0' } |
            ForEach-Object { $_.objectid } |
            Select-Object -Unique

        $hostLookup        = @{}
        $maintenanceLookup = @{}
        $lastValueLookup   = @{}

        if ($objectIds -and $objectIds.Count -gt 0) {
            # selectItems="extend" returns each trigger's items so we can expose
            # the first item's lastvalue, matching the Zabbix UI "Operational data"
            # column with the default '{ITEM.LASTVALUE}' opdata template.
            $triggerParams = @{
                triggerids  = $objectIds
                output      = @('triggerid')
                selectHosts = @('hostid', 'host', 'name', 'maintenance_status', 'maintenance_from')
                selectItems = 'extend'
            }
            $triggers = Invoke-ZabbixAPI -Method 'trigger.get' -Params $triggerParams

            foreach ($trigger in $triggers) {
                if ($trigger.hosts -and $trigger.hosts.Count -gt 0) {
                    $hostLookup[$trigger.triggerid] = $trigger.hosts[0].name
                    $maintenanceLookup[$trigger.triggerid] = @{
                        InMaintenance   = $trigger.hosts[0].maintenance_status -eq '1'
                        MaintenanceFrom = $trigger.hosts[0].maintenance_from
                    }
                }
                if ($trigger.items -and $trigger.items.Count -gt 0) {
                    $firstItem = $trigger.items[0]
                    if ($null -ne $firstItem.lastvalue) {
                        $lastValueLookup[$trigger.triggerid] = [string]$firstItem.lastvalue
                    }
                }
            }
        }

        # Build Zabbix UI base URL once for the per-event hyperlinks
        $baseUrl = $script:ZabbixConnection.Url -replace '/api_jsonrpc\.php/?$', ''

        foreach ($problem in $problems) {
            $eventTime = Convert-UnixTimestamp -Timestamp $problem.clock
            $duration  = Get-DurationString -StartTime $eventTime

            $problemHostName = if ($hostLookup.ContainsKey($problem.objectid)) {
                $hostLookup[$problem.objectid]
            } else { 'Unknown' }

            $ackStatus = if ($problem.acknowledged -eq '1') { 'Acknowledged' } else { 'Unacknowledged' }

            $problemStatus = if ($problem.r_eventid -and $problem.r_eventid -ne '0') {
                'RESOLVED'
            } else { 'PROBLEM' }

            # Suppressed status, with end-time if available
            $suppressedInfo = 'No'
            if ($problem.suppressed -eq '1') {
                $suppressedInfo = 'Yes'
                if ($problem.suppression_data -and $problem.suppression_data.Count -gt 0) {
                    $suppData = $problem.suppression_data | Select-Object -First 1
                    if ($suppData.suppress_until -and $suppData.suppress_until -ne '0') {
                        $suppressUntil = Convert-UnixTimestamp -Timestamp $suppData.suppress_until
                        $suppressedInfo = "Yes (till: $($suppressUntil.ToString('yyyy-MM-dd HH:mm')))"
                    }
                }
            }

            # Maintenance status, with start-time if available
            $maintenanceInfo = 'No'
            if ($maintenanceLookup.ContainsKey($problem.objectid)) {
                $maintData = $maintenanceLookup[$problem.objectid]
                if ($maintData.InMaintenance) {
                    $maintenanceInfo = 'Yes'
                    if ($maintData.MaintenanceFrom -and $maintData.MaintenanceFrom -ne '0') {
                        $maintFrom = Convert-UnixTimestamp -Timestamp $maintData.MaintenanceFrom
                        $maintenanceInfo = "Yes (since: $($maintFrom.ToString('yyyy-MM-dd HH:mm')))"
                    }
                }
            }

            $problemUrl = "$baseUrl/tr_events.php?triggerid=$($problem.objectid)&eventid=$($problem.eventid)"

            $lastValue = if ($lastValueLookup.ContainsKey($problem.objectid)) {
                $lastValueLookup[$problem.objectid]
            } else { '' }

            [PSCustomObject]@{
                PSTypeName  = 'psZBX.Problem'
                EventTime   = $eventTime.ToString('yyyy-MM-dd HH:mm:ss')
                HostName    = $problemHostName
                Problem     = $problem.name
                LastValue   = $lastValue
                Severity    = Get-SeverityName -SeverityCode $problem.severity
                Status      = $problemStatus
                ACK         = $ackStatus
                Duration    = $duration
                Suppressed  = $suppressedInfo
                Maintenance = $maintenanceInfo
                EventID     = $problem.eventid
                URL         = $problemUrl
            }
        }
    }
    catch {
        throw "Failed to get problems: $_"
    }
}