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.
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).

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.

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
}
}
}
}
}
}
