SCCM Scheduler Insights Script – Look inside the scheduler queue

I was recently working with a customer who often finds themselves digging through the scheduler.log file on various SCCM sites in their environment and wanted an ‘easier’ way to see what jobs are queued up and glean some more information – more rapidly.

This seemed to me to be a good excuse to dig into PowerShell, as I had not yet done very much work with it.

The result is a PowerShell script that I am sharing here called SchedulerInsights, details are below. If you find any issues or have any recommendations as to changes, please let me know.

Details/Sample:

  1. The script has two parameters:
    • -Log
      • The full path to the schedule.log file you’d like to parse
    • -Data
      • There are two possible values for –Data
        • CurrentJobs
          • image
        • CountsBySite
          • image
 <#
.SYNOPSIS
   <SCCM Scheduler Log Insights version 1.5 by Jamie Moyer>
.DESCRIPTION
   <Given a schedule log from an SCCM server, parses and displays the current send requests.  Also, shows the current Job Count and total size of Remaining data to be sent to each site in MBs>
.PARAMETER <Log>
   <Full path to the scheduler.log file you would like to parse>
.PARAMETER <Data>
<Whether you want stats by site or the list of current jobs returned>
.EXAMPLE
    .\SchedulerInsights.ps1 -Log \\SMSServer\c$\SMS\Logs\Schedule.log -Data CurrentJobs
.EXAMPLE
    .\SchedulerInsights.ps1 -Log \\SMSServer\c$\SMS\Logs\Schedule.log -Data CountsBySite
#>

#Get Parameters passed
 param($Log,$Data)

$SchedulerQueue = @{};

function Get-JobData() {
      param ($line)
      $job = New-Object PSObject
      $job | Add-Member -type NoteProperty -name SendRequest -value $line.ToString().Trim().Substring(13, 8).Trim();
      $job | Add-Member -type NoteProperty -name PkgID -value "";
      $job | Add-Member -type NoteProperty -name Version -value "";

      $RegexMatches = ([regex]::matches($line, "\b\w+:[\w\s]+\b(?!:)"))      

      foreach($match in $RegexMatches)
      {
            if($match.Value.contains("JobID")){ $job | Add-Member -type NoteProperty -name JobID -value $match.Value.ToString().Trim().Split(':')[1].Trim();continue;}
            if($match.Value.contains("DestSite")){ $job | Add-Member -type NoteProperty -name DestSite -value $match.Value.Trim().Split(':')[1].Trim();continue;}
            if($match.Value.contains("FinalSite")){ $job | Add-Member -type NoteProperty -name FinalSite -value $match.Value.ToString().Trim().Split(':')[1].Trim();continue;}
            if($match.Value.contains("State")){$job | Add-Member -type NoteProperty -name State -value $match.Value.ToString().Trim().Split(':')[1].Trim();continue;}
            if($match.Value.contains("Status")){ $job | Add-Member -type NoteProperty -name Status -value $match.Value.ToString().Trim().Split(':')[1].Trim();continue;}
            if($match.Value.contains("size")){ $job | Add-Member -type NoteProperty -name sizeKb -value $match.Value.ToString().Trim().Split(':')[1].Trim().TrimEnd('k');continue;}
            if($match.Value.contains("Remaining")){ $job | Add-Member -type NoteProperty -name RemainingKb -value $match.Value.ToString().Trim().Split(':')[1].Trim().TrimEnd('k');continue;}
            if($match.Value.contains("PkgID")){    $job.PkgID = $match.Value.ToString().Trim().Split(':')[1].Trim().Substring(0, 8).Trim();continue;}
            if($match.Value.contains("Version")){ $job.Version = $match.Value.ToString().Trim().Split(':')[1].Trim();continue;}
      } 

    #Handle adding only the most recent objects to the hash table
    if ($SchedulerQueue.ContainsKey($job.SendRequest))
    {
            $SchedulerQueue.Remove($job.SendRequest);# //always remove existing entry 
            if ($job.Status.Trim() -ne "Success")
            {
                $SchedulerQueue.Add($job.SendRequest, $job);
            }
    }
    elseif ($job.Status.Trim() -ne "Success")
    {
            $SchedulerQueue.Add($job.SendRequest, $job);
    } 

    return $job;
}

function Get-MostRecentJobData()
{
    $r = new-object system.collections.arraylist;
    foreach($j in $SchedulerQueue.Values)
    {
        [void]$r.Add($j)
    }
    return $r;
}

function Get-SiteCounts
{
  foreach($site in $Input)
  {
    $siteStats = New-Object System.Management.Automation.PsObject
    $siteStats | Add-Member NoteProperty SiteCode $site.Name
    $siteStats | Add-Member NoteProperty JobCount $site.Count
    $siteStats | Add-Member NoteProperty TotalJobSizeMb (($site.Group | Measure-Object -Sum RemainingKb).Sum /1024)
    $siteStats
  }
}

#MAIN
if($Data.contains("CurrentJobs"))
{
    $JobLines = Select-String $Log -pattern "DestSite" | Select-Object Line |ForEach-Object {Get-JobData($_.Line)}|Get-MostRecentJobData|Out-GridView
}
elseif($Data.contains("CountsBySite"))
{
    $JobLines = Select-String $Log -pattern "DestSite" | Select-Object Line |ForEach-Object {Get-JobData($_.Line)}|Get-MostRecentJobData|Group-Object -Property DestSite|Get-SiteCounts | Sort -Desc TotalJobSizeMb | Out-GridView
}