Private/Invoke-InfraPulseScheduledTaskCheck.ps1

function Invoke-InfraPulseScheduledTaskCheck {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [psobject]$Context,

        [Parameter(Mandatory)]
        [System.Collections.IDictionary]$Settings
    )

    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    $scriptBlock = {
        param($CheckSettings)

        if ($env:OS -ne 'Windows_NT') {
            throw 'The ScheduledTasks check requires a Windows target.'
        }

        if (-not (Get-Command -Name Get-ScheduledTask -ErrorAction SilentlyContinue)) {
            return [pscustomobject]@{
                Supported      = $false
                EvaluatedCount = 0
                FailedTasks    = @()
            }
        }

        $failedTasks = New-Object System.Collections.Generic.List[object]
        $evaluatedCount = 0

        foreach ($task in @(Get-ScheduledTask -ErrorAction Stop)) {
            if ([string]$task.State -eq 'Disabled') {
                continue
            }

            $taskPath = [string]$task.TaskPath
            $taskName = [string]$task.TaskName

            $included = $false
            foreach ($pattern in @($CheckSettings.IncludePaths)) {
                if ($taskPath -like [string]$pattern) {
                    $included = $true
                    break
                }
            }
            if (-not $included) {
                continue
            }

            $excluded = $false
            foreach ($pattern in @($CheckSettings.ExcludePaths)) {
                if ($taskPath -like [string]$pattern) {
                    $excluded = $true
                    break
                }
            }
            if (-not $excluded) {
                foreach ($pattern in @($CheckSettings.ExcludeTasks)) {
                    if ($taskName -like [string]$pattern) {
                        $excluded = $true
                        break
                    }
                }
            }
            if ($excluded) {
                continue
            }

            $evaluatedCount++

            try {
                $taskInfo = Get-ScheduledTaskInfo -TaskName $taskName -TaskPath $taskPath -ErrorAction Stop
            }
            catch {
                continue
            }

            $lastResult = [int64]$taskInfo.LastTaskResult
            if ($lastResult -eq 0 -or $lastResult -in @($CheckSettings.ExcludeResults | ForEach-Object { [int64]$_ })) {
                continue
            }

            $failedTaskRecord = [pscustomobject]@{
                TaskPath       = $taskPath
                TaskName       = $taskName
                LastTaskResult = $lastResult
                LastRunTime    = $taskInfo.LastRunTime
            }
            [void]$failedTasks.Add($failedTaskRecord)
        }

        [pscustomobject]@{
            Supported      = $true
            EvaluatedCount = $evaluatedCount
            FailedTasks    = @($failedTasks.ToArray())
        }
    }

    $taskState = Invoke-InfraPulseCommand -Context $Context -ScriptBlock $scriptBlock -ArgumentList @($Settings)
    $stopwatch.Stop()

    if (-not [bool]$taskState.Supported) {
        return New-InfraPulseResult -Status 'Unknown' -CheckName 'ScheduledTasks' -Category 'Availability' -ComputerName $Context.ComputerName -Target 'Scheduled tasks' -Message 'Scheduled-task interfaces are unavailable on the target.' -Recommendation 'Get-ScheduledTask requires the ScheduledTasks module (Windows Server 2012 / Windows 8 or later).' -DurationMs $stopwatch.Elapsed.TotalMilliseconds
    }

    $failedTasks = @($taskState.FailedTasks)
    $failedCount = $failedTasks.Count
    $evaluatedCount = [int]$taskState.EvaluatedCount

    if ($failedCount -ge [int]$Settings.CriticalCount) {
        $status = 'Critical'
        $message = "$failedCount of $evaluatedCount evaluated scheduled task(s) failed their last run."
        $recommendation = 'Review the failing tasks; silently failing maintenance and backup jobs are a common root cause of later data loss.'
    }
    elseif ($failedCount -ge [int]$Settings.WarningCount) {
        $status = 'Warning'
        $message = "$failedCount of $evaluatedCount evaluated scheduled task(s) failed their last run."
        $recommendation = 'Check the task result codes and last run times, then re-run or repair the failing tasks.'
    }
    else {
        $status = 'Healthy'
        $message = "All $evaluatedCount evaluated scheduled task(s) completed their last run successfully."
        $recommendation = ''
    }

    $evidence = [ordered]@{
        FailedCount    = $failedCount
        EvaluatedCount = $evaluatedCount
        FailedTasks    = @(
            $failedTasks |
                Select-Object -First 25 |
                ForEach-Object {
                    [pscustomobject]@{
                        Task           = ('{0}{1}' -f $_.TaskPath, $_.TaskName)
                        LastTaskResult = ('0x{0:X}' -f [int64]$_.LastTaskResult)
                        LastRunTime    = $_.LastRunTime
                    }
                }
        )
        Truncated      = $failedCount -gt 25
    }

    return New-InfraPulseResult -Status $status -CheckName 'ScheduledTasks' -Category 'Availability' -ComputerName $Context.ComputerName -Target 'Scheduled tasks' -Message $message -ObservedValue $failedCount -WarningThreshold (">= {0} failed task(s)" -f $Settings.WarningCount) -CriticalThreshold (">= {0} failed task(s)" -f $Settings.CriticalCount) -Recommendation $recommendation -Evidence $evidence -DurationMs $stopwatch.Elapsed.TotalMilliseconds
}