reserve/Get-CMHourlyCollectionCounts.ps1

<#
.NOTES
    Source: https://model-technology.com/blog/troubleshooting-slow-collection-evaluation-in-sccm-2012-part-3-aka-how-to-identify-collection-update-loitering/
    Author: Steve Bowman
    Adapted by: David Stein
#>

Function Get-CMHourlyCollectionCounts {
    [CmdletBinding()]
    param (
        [parameter()][string] $CMDatabaseServer = "localhost",
        [parameter()][ValidateNotNullOrEmpty()][string] $CMDatabaseName = "",
        [parameter()][ValidateLength(3,3)][string] $CMSiteCode = ""
    )
    try {
        $OldLocation = $(Get-Location).Path

        if ([string]::IsNullOrEmpty($CMDatabaseName)) { throw "Database Name was not provided" }
        if ([string]::IsNullOrEmpty($CMSiteCode)) { throw "SiteCode was not provided" }
        
        #region Set up environment
        $StartDate = Get-Date
        $NumDays = 8
        $EndDate = $StartDate.AddDays($NumDays)
        $GridInterval = 1
        #Make Connection to SCCM 2012
        Write-Verbose "Making connection to ConfigMgr site"
    
        $cmModule = Join-Path $(Split-Path $env:SMS_ADMIN_UI_PATH) "ConfigurationManager.psd1"
        if (-not(Test-Path $cmModule)) { throw "CM PowerShell module not found" }
        #Modules
        Import-Module $cmModule -Force
        
        #Enter SCCM PSProvider
        Set-Location "$($CMSiteCode):\"
        #endregion
        
        #region Build Schedule Results Grid
        $Results = @{"$($StartDate.Date.ToString("MM-dd-yyyy")) $($StartDate.TimeofDay.ToString("hh")):00" = 0 }
        $TestDate = $StartDate
    
        Do {
            $TestDate = $TestDate.AddHours($GridInterval)
            $Results.Add("$($TestDate.Date.ToString("MM-dd-yyyy")) $($TestDate.TimeOfDay.ToSTring("hh")):00", 0)
            # Write-Host "$($TestDate.Date.ToString("MM-dd-yyyy")) $($TestDate.TimeOfDay.ToSTring("hh")):00"
            $Output = $Output + $Item
        
        } while ($TestDate -le $EndDate)
        #endregion
        
        #region Build Incremental Results Grid
        $IncResults = @{"$($StartDate.Date.ToString("MM-dd-yyyy")) $($StartDate.TimeofDay.ToString("hh")):00" = 0 }
        $TestDate = $StartDate
    
        Do {
            $TestDate = $TestDate.AddHours($GridInterval)
            $IncResults.Add("$($TestDate.Date.ToString("MM-dd-yyyy")) $($TestDate.TimeOfDay.ToSTring("hh")):00", 0)
            # Write-Host "$($TestDate.Date.ToString("MM-dd-yyyy")) $($TestDate.TimeOfDay.ToSTring("hh")):00"
            $Output = $Output + $Item
        
        } while ($TestDate -le $EndDate)
        #endregion
    
        #region Process All Collections
        $Collections = Invoke-Sqlcmd -ServerInstance $CMDatabaseServer -Database $CMDatabaseName -Query "Select SiteID, CollectionName, Schedule, EvaluationStartTime, RefreshType from v_Collections"
        
        $IncrInt = Invoke-Sqlcmd -ServerInstance $CMDatabaseServer -Database $CMDatabaseName -Query "Select Name, Value3 as Value from vSMS_SC_Component_Properties where Name = 'Incremental Interval'"
        
        Foreach ($Collection in $Collections) {
        
            Write-Verbose "Processing: $($Collection.SiteID) - $($Collection.CollectionName)"
            #Process Scheduled Collections
            If ($Collection.RefreshType -eq 2 -or $Collection.RefreshType -eq 6) {
        
                $Sched = Convert-CMSchedule -ScheduleString $Collection.Schedule
        
                #Process Monthly by Date
                if ($Sched.MonthDay -gt 0 -and $Sched.ForNumberOfMonths -gt 0) {
                    ##Monthly by Date Schedule
                    $LastEval = $Collection.EvaluationStartTime
        
                    If ($LastEval.Day -eq $Sched.MonthDay -and $LastEval -ge $StartDate -and $LastEval -le $EndDate) {
        
                        If ($Results.ContainsKey("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")) {
                            $Count = $Results.Get_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")
                            $Results.Set_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00", ($Count + 1))
                        }
        
                    }
                    else {
                        $NextMonth = Get-Date -Date "$($LastEval.Month + 1)/1/$($LastEval.Year) $($Sched.StartTime.TimeOfDay.ToString())"
        
                        if ($NextMonth -ge $StartDate -and $NextMonth -le $EndDate) {
                            If ($Results.ContainsKey("$($NextMonth.Date.ToString("MM-dd-yyyy")) $($NextMonth.TimeOfDay.ToSTring("hh")):00")) {
                                $Count = $Results.Get_Item("$($NextMonth.Date.ToString("MM-dd-yyyy")) $($NextMonth.TimeOfDay.ToSTring("hh")):00")
                                $Results.Set_Item("$($NextMonth.Date.ToString("MM-dd-yyyy")) $($NextMonth.TimeOfDay.ToSTring("hh")):00", ($Count + 1))
                            }
                        }
                    }
                }
        
                $LastEval = $Collection.EvaluationStartTime
                If ($Sched.DaySpan -gt 0 -or $Sched.HourSpan -gt 0 -or $Sched.MinuteSpan -gt 0) {
        
                    Do {
                        $LastEval = $LastEval.AddDays($Sched.DaySpan)
                        $LastEval = $LastEval.AddHours($Sched.HourSpan)
                        $LastEval = $LastEval.AddMinutes($Sched.MinuteSpan)
                        If ($Sched.ForNumberOfWeeks -gt 0) {
                            $Days = (($Sched.Day + 1) * $Sched.ForNumberOfWeeks)
                            $LastEval = $LastEval.AddDays($Days)
                        }
        
                        If ($Sched.ForNumberOfMonths -gt 0) {
                            If ($LastEval.Day -ne $Sched.MonthDay) {
                                $LastEval = $LastEval.AddDays(1)
                            }
                            else {
                                Continue
                            }
                        }
                        If ($LastEval -ge $StartDate -and $LastEval -le $EndDate) {
        
                            If ($Results.ContainsKey("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")) {
                                $Count = $Results.Get_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")
                                $Results.Set_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00", ($Count + 1))
                            }
        
                        }
        
                    } while ($LastEval -le $EndDate)
        
                } #End Processing Interval Schedules
        
            } #End Processing Schedules
        
            #Process Incremental Schedules
            If ($Collection.RefreshType -eq 4 -or $Collection.RefreshType -eq 6) {
                $LastEval = $Collection.EvaluationStartTime
                Do {
                    $LastEval = $LastEval.AddMinutes($IncrInt.Value)
        
                    If ($LastEval -ge $StartDate -and $LastEval -le $EndDate) {
        
                        If ($IncResults.ContainsKey("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")) {
                            $Count = $IncResults.Get_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00")
                            $IncResults.Set_Item("$($LastEval.Date.ToString("MM-dd-yyyy")) $($LastEval.TimeOfDay.ToSTring("hh")):00", ($Count + 1))
                        }
        
                    }
                } while ($LastEval -le $EndDate)
        
            } #End Processing Incremental Schedules
        
        }
        $Output = @()
    
        Foreach ($Result in $Results) {
            $FailedPkg = New-Object -TypeName PSObject
            Add-Member -InputObject $FailedPkg -MemberType NoteProperty -Name PackageID -Value $Failure.PackageID
        }
        
        $Results.GetEnumerator() | Sort-Object -Property Name | Select-Object Name, Value | Export-Csv -Path c:\Windows\Temp\SchedResults.csv -NoTypeInformation
        $IncResults.GetEnumerator() | Sort-Object -Property Name | Select-Object Name, Value | Export-Csv -Path c:\Windows\Temp\IncSchedResults.csv -NoTypeInformation
    }
    catch {
        Write-Error $_.Exception.Message
    }
    finally {
        Set-Location $OldLocation
    }
}