Public/system/Get-ScheduledTaskDetail.ps1

#Requires -Version 5.1
function Get-ScheduledTaskDetail {
    <#
        .SYNOPSIS
            Retrieves scheduled task details from local or remote computers
 
        .DESCRIPTION
            Queries the ScheduledTasks CIM namespace to enumerate scheduled tasks and their
            last/next run details. Returns typed PSWinOps.ScheduledTaskDetail objects with
            human-readable HRESULT translation. Microsoft built-in tasks are excluded by
            default to reduce noise.
 
        .PARAMETER ComputerName
            One or more computer names to query. Defaults to the local machine.
            Accepts pipeline input by value and by property name.
 
        .PARAMETER TaskPath
            Filter tasks by folder path. Defaults to all paths.
            Supports wildcards. Example: '\Backup\*'
 
        .PARAMETER TaskName
            Filter tasks by name using -like matching.
            Supports wildcards. Example: 'Backup*'
 
        .PARAMETER IncludeMicrosoftTasks
            Include tasks under the '\Microsoft\' path hierarchy.
            These are excluded by default as there are typically hundreds of built-in tasks.
 
        .PARAMETER Credential
            PSCredential object for remote authentication.
            Used when creating CimSessions to remote machines.
 
        .EXAMPLE
            Get-ScheduledTaskDetail
 
            Retrieves all non-Microsoft scheduled tasks from the local computer.
 
        .EXAMPLE
            Get-ScheduledTaskDetail -ComputerName 'SRV01' -TaskName 'Backup*' -Credential (Get-Credential)
 
            Retrieves scheduled tasks matching 'Backup*' on SRV01 with explicit credentials.
 
        .EXAMPLE
            'SRV01', 'SRV02' | Get-ScheduledTaskDetail -TaskPath '\Maintenance\*' -IncludeMicrosoftTasks
 
            Queries multiple servers via pipeline for tasks in the Maintenance folder.
 
        .OUTPUTS
            PSWinOps.ScheduledTaskDetail
            Returns one object per scheduled task with ComputerName, TaskName, TaskPath,
            State, LastRunTime, LastTaskResult, LastResultMessage, NextRunTime, Author,
            Description, and Timestamp.
 
        .NOTES
            Author: Franck SALLET
            Version: 1.0.0
            Last Modified: 2026-03-25
            Requires: PowerShell 5.1+ / Windows only
            Requires: ScheduledTasks module (built-in on Windows 8+ / Server 2012+)
 
        .LINK
            https://github.com/k9fr4n/PSWinOps
 
        .LINK
            https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/get-scheduledtask
    #>

    [CmdletBinding()]
    [OutputType('PSWinOps.ScheduledTaskDetail')]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias('CN', 'Name', 'DNSHostName')]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$TaskPath,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$TaskName,

        [Parameter(Mandatory = $false)]
        [switch]$IncludeMicrosoftTasks,

        [Parameter(Mandatory = $false)]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential
    )

    begin {
        Write-Verbose -Message "[$($MyInvocation.MyCommand)] Starting"

        $scriptBlock = {
            param($FilterTaskPath, $FilterTaskName, $InclMicrosoft)

            $allTasks = @(Get-ScheduledTask -ErrorAction Stop)

            if ($FilterTaskPath) {
                $allTasks = @($allTasks | Where-Object -FilterScript { $_.TaskPath -like $FilterTaskPath })
            }
            if (-not $InclMicrosoft) {
                $allTasks = @($allTasks | Where-Object -FilterScript { $_.TaskPath -notlike '\Microsoft\*' })
            }
            if ($FilterTaskName) {
                $allTasks = @($allTasks | Where-Object -FilterScript { $_.TaskName -like $FilterTaskName })
            }

            foreach ($task in $allTasks) {
                $runInfo = $null
                try {
                    $runInfo = ScheduledTasks\Get-ScheduledTaskInfo -TaskName $task.TaskName -TaskPath $task.TaskPath -ErrorAction SilentlyContinue
                } catch {
                    Write-Verbose -Message "[$($MyInvocation.MyCommand)] Could not retrieve run info for '$($task.TaskPath)$($task.TaskName)': $_"
                }

                [PSCustomObject]@{
                    TaskName       = $task.TaskName
                    TaskPath       = $task.TaskPath
                    State          = $task.State.ToString()
                    LastRunTime    = if ($runInfo) { $runInfo.LastRunTime } else { $null }
                    LastTaskResult = if ($runInfo) { $runInfo.LastTaskResult } else { $null }
                    NextRunTime    = if ($runInfo) { $runInfo.NextRunTime } else { $null }
                    Author         = $task.Author
                    Description    = $task.Description
                }
            }
        }
    }

    process {
        foreach ($machine in $ComputerName) {
            try {
                Write-Verbose -Message "[$($MyInvocation.MyCommand)] Querying scheduled tasks on '$machine'"

                $argList = @(
                    $(if ($PSBoundParameters.ContainsKey('TaskPath')) { $TaskPath } else { $null }),
                    $(if ($PSBoundParameters.ContainsKey('TaskName')) { $TaskName } else { $null }),
                    $IncludeMicrosoftTasks.IsPresent
                )

                $rawTasks = @(Invoke-RemoteOrLocal -ComputerName $machine -ScriptBlock $scriptBlock -ArgumentList $argList -Credential $Credential)

                if ($rawTasks.Count -eq 0) {
                    Write-Verbose -Message "[$($MyInvocation.MyCommand)] No matching tasks found on '$machine'"
                    continue
                }

                Write-Verbose -Message "[$($MyInvocation.MyCommand)] Found $($rawTasks.Count) task(s) on '$machine'"

                foreach ($task in $rawTasks) {
                    $resultMessage = ConvertTo-ScheduledTaskResultMessage -ResultCode $task.LastTaskResult

                    [PSCustomObject]@{
                        PSTypeName        = 'PSWinOps.ScheduledTaskDetail'
                        ComputerName      = $machine
                        TaskName          = $task.TaskName
                        TaskPath          = $task.TaskPath
                        State             = $task.State
                        LastRunTime       = $task.LastRunTime
                        LastTaskResult    = $task.LastTaskResult
                        LastResultMessage = $resultMessage
                        NextRunTime       = $task.NextRunTime
                        Author            = $task.Author
                        Description       = $task.Description
                        Timestamp         = Get-Date -Format 'o'
                    }
                }
            }
            catch {
                Write-Error -Message "[$($MyInvocation.MyCommand)] Failed on '${machine}': $_"
                continue
            }
        }
    }

    end {
        Write-Verbose -Message "[$($MyInvocation.MyCommand)] Completed"
    }
}