Display a Maintenance Window for an SCCM Collection

This function is based on the original code from Nickolaj Andersen that you can find here GitHub Get-MaintenanceWindows.ps1 however I have modified this to allow for Verbose output and to specify the day, month and year for which you want to determine the Maintenance Window. In addition since I work a lot with non-recurring Maintenance Windows (set for the complete year in advance) I have added a date match for RecurrenceType 1 (same as is done for other recurrence types). The changes have been made so that this function can be used in scripts to identify when a Maintenance Window occurs and combined with other functions to show which servers will be affected during that Maintenance Window.

Again all credits to Nickolaj Andersen for having created the initial function.

NOTE: By including the SYNOPSIS and other information you can use get-help on the function to find out what parameters you can specify or read what this function actually can be used for. I can highly recommended to always include this information in your PowerShell function(s) so that anybody can understand it.

Examples:
In the below examples names pointing to a sitecode, collection ID or server name are blurred out (this is a real site server behind a FAKE FQDN).

In the example below you can see that the first detected Maintenance Window for April 2018 is on the 16th and this is returned as a value. This value can be used in other scripts to determine what and when, the currently shown Recurrence type is 1 (non-recurring).
Get-SCCMCollectionMaintenanceWindow without verbose

In the example below the same command line as above was used however this time with the Verbose parameter to show more information of what is found/happening during the function execution, in this case a small sample was taken but more output is shown during the run until a match is found.
Get-SCCMCollectionMaintenanceWindow with verbose output

Code:

Function Get-SCCMCollectionMaintenanceWindow
{
  <#
    .SYNOPSIS
    Gets the configured maintenance window(s) for the specified collection based on the SCCM Collection ID.
    .DESCRIPTION
    Displays the date and time of the Maintenance Window if found, if executed without a day,month or year the current day,month and year is used.
    You can use the parameters to find a Maintenance Window for a specific date in the (near) future.

    .EXAMPLE
    Get-SCCMCollectionMaintenanceWindow.ps1 -SCCMCollectionID <sccm collection id>
    .EXAMPLE
    Get-SCCMCollectionMaintenanceWindow.ps1 -SCCMCollectionID <sccm collection id> -Day 19 -Month 2 -Year 2018

    .PARAMETER SCCMCollectionID
    The SCCMCollectionID parameter is the actual collection for which you want to query a Maintenance Window
    .PARAMETER Day
    Optional parameter that specifies the day for which to determine a Maintenance Window, if not specified the current day will be used
    .PARAMETER Month
    Optional parameter that specifies the month for which to determine a Maintenance Window, if not specified the current month will be used
    .PARAMETER Year
    Optional parameter that specifies the year for which to determine a Maintenance Window, if not specified the current year will be used
    .PARAMETER SiteServer
    Optional parameter that specifies the Site Server to use for all connections
    .PARAMETER SiteCode
    Optional parameter that specifies the Site Code however by default WMI is used to gather this from the Site Server
  #>
  Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [string]$SCCMCollectionID ,
    [Parameter(Mandatory=$false)]
    [string]$SiteServer = "mysiteserver.example.com",
    [Parameter(Mandatory=$false)]
    [string]$SiteCode = (Get-WmiObject -Namespace "root\SMS" -Class SMS_ProviderLocation -ComputerName $SiteServer).SiteCode,
    [Parameter(Mandatory=$false)]
    [int]$Month,
    [Parameter(Mandatory=$false)]
    [int]$Day,
    [Parameter(Mandatory=$false)]
    [int]$Year
  )
  Write-Verbose "SCCM Site Server                   : $($SiteServer)"
  Write-Verbose "SCCM Site code                     : $($SiteCode)"
  Write-Verbose "SCCM Collection ID                 : $($SCCMCollectionID)"
  If ($Month)
  {
    $MonthChosen = $True
    Write-Verbose "Chosen Month                       : $($Month)"
  }
  Else
  {
    [int]$Month = (Get-Date).Month
    $MonthChosen = $False
    Write-Verbose "Current Month                      : $($Month)"
  }
  If ($Year)
  {
    Write-Verbose "Chosen Year                        : $($Year)"
  }
  Else
  {
    [int]$Year = (Get-Date).Year
    Write-Verbose "Current Year                       : $($Year)"
  }
  If ($Day)
  {
    $DayChosen = $True
    Write-Verbose "Chosen Day                         : $($Day)"
  }
  Else
  {
    [int]$Day = (Get-Date).Day
    $DayChosen = $False
    Write-Verbose "Current Day                        : $($Day)"
  } 
  $DateArray = @()
  
  Function Get-CMSchedule
  {
    param(
      $String
      )
      $WMIConnection = [WmiClass]"\\$($SiteServer)\root\SMS\site_$($SiteCode):SMS_ScheduleMethods"
      $Schedule = $WMIConnection.psbase.GetMethodParameters("ReadFromString")
      $Schedule.StringData = $String
      $ScheduleData = $WMIConnection.psbase.InvokeMethod("ReadFromString",$Schedule,$null)
      $ScheduleInfo = $ScheduleData.TokenData
      return $ScheduleInfo
  }
  
  $CollectionSettings = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_CollectionSettings -ComputerName $SiteServer -Filter "CollectionID = '$($SCCMCollectionID)'"
  foreach ($CollectionSetting in $CollectionSettings)
  {
    $CollectionSetting.Get()
    ForEach ($MaintenanceWindow in $CollectionSetting.ServiceWindows)
    {
      $StartTime = [Management.ManagementDateTimeConverter]::ToDateTime($MaintenanceWindow.StartTime)
      $DateArray += $MaintenanceWindow
      $CollectionName = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_Collection -ComputerName $SiteServer -Filter "CollectionID = '$($CollectionSetting.CollectionID)'" | Select-Object -ExpandProperty Name
      Write-Verbose "SCCM Collection Name               : $($SCCMCollectionID)"
      Write-Verbose "SCCM Maintenance Window Name       : $($MaintenanceWindow.Name)"
      $SortedDateArray = $DateArray | Sort-Object -Property RecurrenceType | Select-Object Description, RecurrenceType, ServiceWindowSchedules, isEnabled
      Write-Verbose "Sorted Date Array                  : $($SortedDateArray)"
      $RecurrenceType1 = ($SortedDateArray | Where-Object { $_.RecurrenceType -eq 1 })
      if ($RecurrenceType1 -ne $null)
      {
        foreach ($R1RecurrenceType in $RecurrenceType1)
        {
          Write-Verbose "RecurrenceType 1                   : $($R1RecurrenceType.IsEnabled)"
          If ($R1RecurrenceType.IsEnabled -eq $true)
          {
            $R1Schedule = Get-CMSchedule -String $R1RecurrenceType.ServiceWindowSchedules
            Write-Verbose "Schedule                           : $($R1Schedule.StartTime)"
            $R1StartTime = [Management.ManagementDateTimeConverter]::ToDateTime($R1Schedule.StartTime)
            Write-Verbose "Start Time                         : $($R1StartTime)"
            $R1WorkingDate = (Get-Date -Month $Month -Year $Year -Day $Day)
            Write-Verbose "Working Date/Time                  : $($R1WorkingDate)"
            If (($R1StartTime.Day -eq $R1WorkingDate.Day) -and ($R1StartTime.Month -eq $R1WorkingDate.Month) -and ($R1StartTime.Year -eq $R1WorkingDate.Year))
            {
              Write-Verbose "Start Day                          : $($R1StartTime.Day)"
              Write-Verbose "Start Month                        : $($R1StartTime.Month)"
              Write-Verbose "Start Year                         : $($R1StartTime.Year)"
              return $R1StartTime
            }
            If (($R1StartTime.Month -eq $R1WorkingDate.Month) -and ($R1StartTime.Year -eq $R1WorkingDate.Year))
            {
              Write-Verbose "Start Day                          : $($R1StartTime.Day)"
              Write-Verbose "Start Month                        : $($R1StartTime.Month)"
              Write-Verbose "Start Year                         : $($R1StartTime.Year)"
              return $R1StartTime
            }
          }
        }
      }
      $RecurrenceType2 = ($SortedDateArray | Where-Object { $_.RecurrenceType -eq 2 })
      if ($RecurrenceType2 -ne $null)
      {
        foreach ($R2RecurrenceType in $RecurrenceType2)
        {
          Write-Verbose "RecurrenceType 2                   : $($R2RecurrenceType.IsEnabled)"
          If ($R2RecurrenceType.IsEnabled -eq $true)
          {
            $R2Schedule = Get-CMSchedule -String $R2RecurrenceType.ServiceWindowSchedules
            Write-Verbose "Schedule                           : $($R2Schedule.StartTime)"
            $R2StartTime = [Management.ManagementDateTimeConverter]::ToDateTime($R2Schedule.StartTime)
            Write-Verbose "Schedule Start Time                : $($R2StartTime)"
            $R2DaySpan = $R2Schedule.DaySpan
            Write-Verbose "Day Span                           : $($R2DaySpan)"
            $R2CurrentDate = (Get-Date -Month $Month -Year $Year -Day $Day)
            Write-Verbose "Working Date/Time                  : $($R2CurrentDate)"
            If ($MonthChosen -eq $False -and $DayChosen -eq $False)
            {
              Write-Verbose "Month chosen on command line       : $MonthChosen"
              Write-Verbose "Day chosen on command line         : $DayChosen"
              do
              {
                $R2StartTime = $R2StartTime.AddDays($R2DaySpan)
              }
              until ($R2StartTime -ge $R2CurrentDate)
            }
            ElseIf ($MonthChosen -eq $True -and $DayChosen -eq $False)
            {
              Write-Verbose "Month chosen on command line       : $MonthChosen"
              Write-Verbose "Day chosen on command line         : $DayChosen"
              do
              {
                $R2StartTime = $R2StartTime.AddDays($R2DaySpan)
              }
              until ($R2StartTime.Month -eq $R2CurrentDate.Month)
            }
            ElseIf ($MonthChosen -eq $True -and $DayChosen -eq $True)
            {
              Write-Verbose "Month chosen on command line       : $MonthChosen"
              Write-Verbose "Day chosen on command line         : $DayChosen"
              do
              {
                $R2StartTime = $R2StartTime.AddDays($R2DaySpan)
              }
              until (($R2StartTime.Month -eq $R2CurrentDate.Month) -and ($R2StartTime.Day -eq $R2CurrentDate.Day))
            }
            Write-Verbose "Scheduled Maintenance Date/Time    : $($R2StartTime)"
            return $R2StartTime
          }
        }
      }
      $RecurrenceType3 = ($SortedDateArray | Where-Object { $_.RecurrenceType -eq 3 })
      If ($RecurrenceType3 -ne $null) {
        foreach ($R3RecurrenceType in $RecurrenceType3)
        {
          Write-Verbose "RecurrenceType 3                   : $($R3RecurrenceType.IsEnabled)"
          If ($R3RecurrenceType.IsEnabled -eq $true) 
          {
            $R3Schedule = Get-CMSchedule -String $R3RecurrenceType.ServiceWindowSchedules
            Write-Verbose "Schedule                           : $($R3Schedule.StartTime)"
            $R3StartMin = [Management.ManagementDateTimeConverter]::ToDateTime($R3Schedule.StartTime) | Select-Object -ExpandProperty Minute
            Write-Verbose "Start Minute                       : $($R3StartMin)"
            $R3StartHour = [Management.ManagementDateTimeConverter]::ToDateTime($R3Schedule.StartTime) | Select-Object -ExpandProperty Hour
            Write-Verbose "Start Hour                         : $($R3StartHour)"
            $R3StartDay = $R3Schedule.Day
            switch ($R3StartDay)
            {
              1 { $R3DayOfWeek = "Sunday" }
              2 { $R3DayOfWeek = "Monday" }
              3 { $R3DayOfWeek = "Tuesday" }
              4 { $R3DayOfWeek = "Wednesday" }
              5 { $R3DayOfWeek = "Thursday" }
              6 { $R3DayOfWeek = "Friday" }
              7 { $R3DayOfWeek = "Saturday" }
            }
            Write-Verbose "Start Day                          : $($R3StartDay) = $($R3DayOfWeek)"
            $R3WeekSpan = $R3Schedule.ForNumberOfWeeks
            switch ($R3WeekSpan)
            {
              1 { $R3AddDays = 0 }
              2 { $R3AddDays = 7 }
              3 { $R3AddDays = 14 }
              4 { $R3AddDays = 21 }
            }
            Write-Verbose "For Number of Weeks                : $($R3WeekSpan)"
            $R3CurrentDate = (Get-Date -Month $Month -Year $Year -Day $Day)
            Write-Verbose "Working Date/Time                  : $($R3CurrentDate)"
            $R3DaysUntil = 0
            While ($R3CurrentDate.DayOfWeek -ne "$($R3DayOfWeek)")
            {
              $R3DaysUntil++
              $R3CurrentDate = $R3CurrentDate.AddDays(1)
            }
            If ($R3StartHour -le 9)
            {
              If ($R3StartMin -le 9)
              {
                $R3DateTime = ([datetime]::ParseExact("0$($R3StartHour):0$($R3StartMin)","hh:mm",$null)).AddDays($R3DaysUntil).AddDays($R3AddDays)
              }
              elseif ($R3StartMin -ge 10)
              {
                $R3DateTime = ([datetime]::ParseExact("0$($R3StartHour):$($R3StartMin)","hh:mm",$null)).AddDays($R3DaysUntil).AddDays($R3AddDays)
              }
            }
            elseif ($R3StartHour -ge 10)
            {
              If ($R3StartMin -le 9)
              {
                $R3DateTime = ([datetime]::ParseExact("$($R3StartHour):0$($R3StartMin)","hh:mm",$null)).AddDays($R3DaysUntil).AddDays($R3AddDays)
              }
              elseif ($R3StartMin -ge 10)
              {
                $R3DateTime = ([datetime]::ParseExact("$($R3StartHour):$($R3StartMin)","hh:mm",$null)).AddDays($R3DaysUntil).AddDays($R3AddDays)
              }
            }
            Write-Verbose "Scheduled Maintenance Date/Time    : $($R3DateTime)"
            return $R3DateTime
          }
        }
      }
      $RecurrenceType4 = ($SortedDateArray | Where-Object { $_.RecurrenceType -eq 4 })
      if ($RecurrenceType4 -ne $null) {
        foreach ($R4RecurrenceType in $RecurrenceType4)
        {
          Write-Verbose "RecurrenceType 4                   : $($R4RecurrenceType.IsEnabled)"
          If ($R4RecurrenceType.IsEnabled -eq $true)
          {
            $R4Schedule = Get-CMSchedule -String $R4RecurrenceType.ServiceWindowSchedules
            Write-Verbose "Schedule                           : $($R4Schedule.StartTime)"
            $R4WeekOrder = $R4Schedule.WeekOrder
            Write-Verbose "Week Order                         : $($R4WeekOrder)"
            $R4StartHour = [Management.ManagementDateTimeConverter]::ToDateTime($R4Schedule.StartTime) | Select-Object -ExpandProperty Hour
            Write-Verbose "Start Hour                         : $($R4StartHour)"
            $R4StartMin = [Management.ManagementDateTimeConverter]::ToDateTime($R4Schedule.StartTime) | Select-Object -ExpandProperty Minute
            Write-Verbose "Start Minute                       : $($R4StartMin)"
            $R4StartSec = [Management.ManagementDateTimeConverter]::ToDateTime($R4Schedule.StartTime) | Select-Object -ExpandProperty Second
            Write-Verbose "Start Second                       : $($R4StartSec)"
            $R4WeekDay = $R4Schedule.Day
            switch ($R4WeekDay)
            {
              1 { $R4DayOfWeek = "Sunday" }
              2 { $R4DayOfWeek = "Monday" }
              3 { $R4DayOfWeek = "Tuesday" }
              4 { $R4DayOfWeek = "Wednesday" }
              5 { $R4DayOfWeek = "Thursday" }
              6 { $R4DayOfWeek = "Friday" }
              7 { $R4DayOfWeek = "Saturday" }
            }
            Write-Verbose "Start Day                          : $($R4WeekDay) = $($R4DayOfWeek)"
            If ($R4WeekOrder -ge 1)
            {
              $R4Increment = 0
              $R4Date = (Get-Date -Year $Year -Month $Month -Day 1 -Hour $($R4StartHour) -Minute $($R4StartMin) -Second $($R4StartSec))
              Do
              {
                $R4Increment++
                $R4CalcDate = $R4Date.AddDays($R4Increment)
                $R4CalcDayofWeek = $R4CalcDate.DayOfWeek
              }
              Until ($R4CalcDayofWeek -like $R4DayOfWeek)
              $R4CalcDateTime = $R4CalcDate
              If ($R4WeekOrder -eq 1)
              {
                $R4DateTime = $R4CalcDateTime
              }
              elseif ($R4WeekOrder -eq 2)
              {
                $R4DateTime = $R4CalcDateTime.AddDays(7)
              }
              elseif ($R4WeekOrder -eq 3)
              {
                $R4DateTime = $R4CalcDateTime.AddDays(14)
              }
              elseif ($R4WeekOrder -eq 4)
              {
                $R4DateTime = $R4CalcDateTime.AddDays(21)
              }
            }
            elseif ($R4WeekOrder -eq 0)
            {
              $R4Decrement = 0
              $R4Date = (Get-Date -Year $Year -Month $Month -Day 1 -Hour $($R4StartHour) -Minute $($R4StartMin) -Second $($R4StartSec)).AddMonths(1)
              Do
              {
                $R4Decrement++
                $R4CalcDate = $R4Date.AddDays(-$R4Decrement)
                $R4CalcDayofWeek = $R4CalcDate.DayOfWeek
              }
              Until ($R4CalcDayofWeek -like $R4DayOfWeek)
              $R4DateTime = $R4CalcDate
            }
            Write-Verbose "Scheduled Maintenance Date/Time    : $($R4DateTime)"
            return $R4DateTime
          }
        }
      }
      $RecurrenceType5 = ($SortedDateArray | Where-Object { $_.RecurrenceType -eq 5 })
      If ($RecurrenceType5 -ne $null)
      {
        foreach ($R5RecurrenceType in $RecurrenceType5)
        {
          Write-Verbose "RecurrenceType 5                   : $($R5RecurrenceType.IsEnabled)"
          If ($R5RecurrenceType.IsEnabled -eq $true)
          {
            $R5Schedule = Get-CMSchedule -String $R5RecurrenceType.ServiceWindowSchedules
            Write-Verbose "Schedule                           : $($R5Schedule.StartTime)"
            $R5StartTime = [Management.ManagementDateTimeConverter]::ToDateTime($R5Schedule.StartTime)
            Write-Verbose "Schedule Start Time                : $($R5StartTime)"
            $R5StartHour = $R5StartTime.Hour
            Write-Verbose "Start Hour                         : $($R5StartHour)"
            $R5StartMin = $R5StartTime.Minute
            Write-Verbose "Start Minute                       : $($R5StartMin)"
            $R5StartSec = $R5StartTime.Second
            Write-Verbose "Start Second                       : $($R5StartSec)"
            $R5MonthSpan = $R5Schedule.ForNumberOfMonths
            Write-Verbose "For Number of Months               : $($R5MonthSpan)"
            $R5MonthDay = $R5Schedule.MonthDay
            Write-Verbose "Day of Month                       : $($R5MonthDay)"
            If ($R5Schedule.MonthDay -ge 1)
            {
              If ($R5MonthSpan -eq 1)
              {
                $R5DateTime = ((Get-Date -Year $Year -Month $Month -Day $($R5MonthDay) -Hour $($R5StartHour) -Minute $($R5StartMin) -Second $($R5StartSec))).DateTime
              }
              elseif ($R5MonthSpan -gt 1)
              {
                $R5DateTime = ((Get-Date -Year $Year -Month $Month -Day $($R5MonthDay) -Hour $($R5StartHour) -Minute $($R5StartMin) -Second $($R5StartSec)).AddMonths($R5MonthSpan)).DateTime
              }
            }
            elseif ($R5Schedule.MonthDay -eq 0)
            {
              $R5DateTime = ((Get-Date -Year $Year -Month $Month -Day 1 -Hour $($R5StartHour) -Minute $($R5StartMin) -Second $($R5StartSec)).AddMonths($R5MonthSpan).AddDays(-1)).DateTime
            }
            Write-Verbose "Scheduled Maintenance Date/Time    : $($R5DateTime)"
            return $R5DateTime
          }
        }
      }
    }
  }
}

Posted in Maintenance Window, PowerShell, System Center Configuration Manager, WMI.

Leave a Reply

Your email address will not be published. Required fields are marked *