Posted in : Microsoft, System Center

3 years ago

Reading SCCM Maintenance Windows is not an easy thing to do from WMI as they are stored as 16 char hex values
To get the schedule hex values simply run the below line, but the result might not be easy to understand

Get-WmiObject -Namespace 'ROOT\ccm\Policy\Machine\RequestedConfig' -Class CCM_ServiceWindow | Select -ExpandProperty Schedules

A server might return the following values.

02C24AC0381A2000
02C24AC0381C2000
02C24AC0381B2000
02C24AC0101E2000
026B3B80601C2001
02C24AC0381D2000
00084AC028592000
00074AC0005F2000

Now, how do we read this? The easiest thing is to run Convert-CMSchedule but that unfortunately only works if the configuration manager console is installed.
Another way to do is to invoke a wmi method from a site server using the SMS_ScheduleMethods class.
None of these are portable, so I created my own function to decode these. The below function returns the same data as Convert-CMSchedule but is free from any module as it converts the hex to a readable format.
The function also accepts valuefrompipeline

PS C:\Windows\system32> Get-WmiObject -Namespace 'ROOT\ccm\Policy\Machine\RequestedConfig' -Class CCM_ServiceWindow | Select -ExpandProperty Schedules | ConvertFrom-CMSchedule | select -first 2
SmsProviderObjectPath : SMS_ST_RecurWeekly
Day                   : 2
DayDuration           : 0
ForNumberOfWeeks      : 1
HourDuration          : 7
IsGMT                 : False
MinuteDuration        : 0
StartTime             : 2009-05-01 22:00:00
PSComputerName        : Server1
SmsProviderObjectPath : SMS_ST_RecurWeekly
Day                   : 2
DayDuration           : 0
ForNumberOfWeeks      : 1
HourDuration          : 7
IsGMT                 : False
MinuteDuration        : 0
StartTime             : 2013-04-02 22:00:00
PSComputerName        : Server2

 

Function ConvertFrom-CMSchedule
{
    <#
    .Synopsis
        Converts CMSchedule hex string to CMScheduleObject
    .NOTES
        Name: ConvertFrom-CMSchedule
        Author: Vikingur Saemundsson
        Date Created: 2017-04-18
        Version History:
            2017-04-18 - Vikingur Saemundsson
                Initial Creation
        Xenit AB
    #>
    [cmdletbinding()]
    Param(
        [Parameter(
            ValueFromPipeline=$true,
            Mandatory=$True
        )]
        $Schedule
    )
    Begin{
    }
    Process{
        $Start = $Schedule.Substring(0,8)
        $Recurrence = $Schedule.Substring(8,8)
        If($Start -eq '00012000'){
            $Type = 'Simple'
        }
        Else{
            $Type = 'Custom'
            #Convert to binary string
            $BStart = [Convert]::ToString([int64]"0x$Start".ToString(),2)
            #Pad to 32 chars
            If($BStart.Length -lt 32){0..(31-$BStart.Length) | ForEach-Object{$Bstart = "0$BStart"}}
            #Collect timedata
            [String]$StartMinute = [Convert]::ToInt32($BStart.Substring(0,6),2)
            If($StartMinute.Length -eq 1){$StartMinute = "0$StartMinute"}
            [String]$StartHour   = [Convert]::ToInt32($BStart.Substring(6,5),2)
            If($StartHour.Length -eq 1){$StartHour = "0$StartHour"}
            [String]$StartDay    = [Convert]::ToInt32($BStart.Substring(11,5),2)
            If($StartDay.Length -eq 1){$StartDay = "0$StartDay"}
            [String]$StartMonth  = [Convert]::ToInt32($BStart.Substring(16,4),2)
            If($StartMonth.Length -eq 1){$StartMonth = "0$StartMonth"}
            [String]$StartYear   = [Convert]::ToInt32($BStart.Substring(20,6),2)+1970
            $StartString = "$StartYear-$StartMonth-$StartDay $StartHour`:$StartMinute`:00"
        }
        #Convert to binary string
        $BRec = [Convert]::ToString([int64]"0x$Recurrence".ToString(),2)
        #Pad to 32 chars
        If($BRec.Length -lt 32){0..(31-$BRec.Length) | ForEach-Object{$BRec = "0$BRec"}}
        [bool]$GMT = [Convert]::ToInt32($BRec.Substring(31,1),2)
        $DayDuration = 0
        $HourDuration = [Convert]::ToInt32($BRec.Substring(0,5),2)
        $MinuteDuration = [Convert]::ToInt32($BRec.Substring(5,5),2)
        If($HourDuration -gt 24){
            $h = $HourDuration % 24
            $DayDuration = ($HourDuration-$h)/24
            $HourDuration = $h
        }
        $RecurType = [Convert]::ToInt32($BRec.Substring(10,3),2)
        Switch($RecurType){
            1{
                $path = 'SMS_ST_NonRecurring'
                ##??
            }
            2{
                $path = 'SMS_ST_RecurInterval'
                $MinuteSpan = [Convert]::ToInt32($BRec.Substring(13,6),2)
                $HourSpan = [Convert]::ToInt32($BRec.Substring(19,5),2)
                $DaySpan = [Convert]::ToInt32($BRec.Substring(24,5),2)
                $Ret = '#TYPE IResultObject#SMS_ST_RecurInterval
                "SmsProviderObjectPath","DayDuration","DaySpan","HourDuration","HourSpan","IsGMT","MinuteDuration","MinuteSpan","StartTime","PSComputerName","PSShowComputerName"
                "SMS_ST_RecurInterval","{0}","{1}","{2}","{3}","{4}","{5}","{6}","1970-02-01 00:00:00","{7}","False"' -f $DayDuration,$DaySpan,$HourDuration,$HourSpan,$GMT,$MinuteDuration,$MinuteSpan,$env:COMPUTERNAME | ConvertFrom-Csv
            }
            3{
                $path = 'SMS_ST_RecurWeekly'
                $Day   = [Convert]::ToInt32($BRec.Substring(13,3),2)
                $ForNumberOfWeeks  = [Convert]::ToInt32($BRec.Substring(16,3),2)
                $ret = '#TYPE IResultObject#SMS_ST_RecurWeekly
                "SmsProviderObjectPath","Day","DayDuration","ForNumberOfWeeks","HourDuration","IsGMT","MinuteDuration","StartTime","PSComputerName","PSShowComputerName"
                "SMS_ST_RecurWeekly","{0}","{1}","{2}","{3}","{4}","{5}","{6}","{7}","False"' -f $Day,$DayDuration,$ForNumberOfWeeks,$HourDuration,$GMT,$MinuteDuration,$StartString,$env:COMPUTERNAME | ConvertFrom-Csv
            }
            4{
                $path = 'SMS_ST_RecurMonthlyByWeekday'
                $Day   = [Convert]::ToInt32($BRec.Substring(13,3),2)
                $ForNumberOfMonths = [Convert]::ToInt32($BRec.Substring(16,4),2)
                $WeekOrder = [Convert]::ToInt32($BRec.Substring(20,3),2)
                $ret = '#TYPE IResultObject#SMS_ST_RecurMonthlyByWeekday
                "SmsProviderObjectPath","Day","DayDuration","ForNumberOfMonths","HourDuration","IsGMT","MinuteDuration","StartTime","WeekOrder","PSComputerName","PSShowComputerName"
                "SMS_ST_RecurMonthlyByWeekday","{0}","{1}","{2}","{3}","{4}","{5}","{6}","{7}","{8}","False"' -f $Day,$DayDuration,$ForNumberOfMonths,$HourDuration,$GMT,$MinuteDuration,$StartString,$WeekOrder,$env:COMPUTERNAME | ConvertFrom-Csv
            }
            5{
                $path = 'SMS_ST_RecurMonthlyByDate'
                $MonthDay  = [Convert]::ToInt32($BRec.Substring(13,5),2)
                $ForNumberOfMonths = [Convert]::ToInt32($BRec.Substring(18,4),2)
                $Ret = '#TYPE IResultObject#SMS_ST_RecurMonthlyByDate
                "SmsProviderObjectPath","DayDuration","ForNumberOfMonths","HourDuration","IsGMT","MinuteDuration","MonthDay","StartTime","PSComputerName","PSShowComputerName"
                "SMS_ST_RecurMonthlyByDate","{0}","{1}","{2}","{3}","{4}","{5}","{6}","{7}","False"' -f $DayDuration,$ForNumberOfMonths,$HourDuration,$GMT,$MinuteDuration,$MonthDay,$StartString,$env:COMPUTERNAME | ConvertFrom-Csv
            }
            Default{
                Throw "Invalid type"
            }
        }
        $Ret
    }
    End{
    }
}

The script is reverse engineered from https://msdn.microsoft.com/en-us/library/cc143505.aspx

Tags : MaintenanceWindows, PowerShell, SCCM

Add comment

Your comment will be revised by the site if needed.