Orchestrator 2012: Check SCCM maintenance window and set SCOM maintenance mode

Everyone who uses System Center Configuration Manager 2012 and System Center Operations Manager 2012 knows the problem of setting the server into maintenance mode when patching or software deployment needs to take place.

With System Center Orchestrator 2012 you get the integration packs for both systems and the option to create a workflow for this task. My intetion for this was to use the maintenance windows which are defined on the collections. During this timeframe software updates and deployments can be performed on the servers incl. reboots. So it would be good to set the servers into maintenance mode in SCOM. I only focussed on general maintenance mode windows not OSD ones and non recurring windows.

Here is the summary of the workflow I have created:
The workflow runs every 2 minutes. It reads a text file on the runbook server with all collection ids it should check, then checks if the collection has a maintenance window defined, that will start within the next 10-15 minutes. If yes, then it gets the collection members in SCCM, gets the FQDN for the server and starts the maintenance mode in SCOM. If successful it writes a log file otherwise it tries again to set the maintenance mode with the Netbios name.

Diagram:

set sccm maintenance window

Most of the parts are standard activities, so I only describe the “Get Maintenance Window” activity, which runs a PowerShell script on the Runbook server. This activity needs to run with a user that has SCCM permissions, otherwise it will provide no result. It only will have output data, if the maintenance window will occur within the next 10-15 minutes. So the link to the Get Collection Members activity should have the following include entry: Pure Output from Get Maintenance Window matches pattern .+

Here is the command line for the Get Maintenance Window activity:

cmd.exe /c | c:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe –c “function WMI-DateStringToDate($time) {  [System.Management.ManagementDateTimeconverter]::ToDateTime($time);};$collsettings = ([WMIClass] ‘\\SCCM Server FQDN\root\SMS\site_SCCMSiteCode:SMS_CollectionSettings’).CreateInstance();if($collsettings -is [Object]){$collsettings.CollectionID = ‘Link to Line Text of previous activity’;$collsettings.get();$windows=$collsettings.ServiceWindows;if ($windows -is [Object]){$now=Get-Date;Foreach ($window in $windows){$Time=WMI-DateStringToDate($window.StartTime);if (($window.IsEnabled -eq $True) -and ($window.ServiceWindowType -eq ‘1’) -and ($window.RecurrenceType -eq ‘1’)){if (($now.AddMinutes(15).compareto($Time) -eq ‘1’) -and ($now.AddMinutes(10).compareto($Time) -eq ‘-1’)){$Duration=$window.Duration+15;write-host ($Time.ToString(),$Duration) -separator ‘;’}}}}};”

Attention! The command line should not have line breaks! Otherwise it will not work within this activity.
For better readability I post the script here also with line breaks and comments:

param($SMSSiteCode, $SMSManagementServer, $COLLECTION_ID)
# convert WMI date to DateTime format
function WMI-DateStringToDate($time)
{ [System.Management.ManagementDateTimeconverter]::ToDateTime($time)}
# get collection settings (incl. Maintenance Windows)
$collsettings= ([WMIClass] \\$SMSManagementServer\root\SMS\site_$($SmsSiteCode):SMS_CollectionSettings).CreateInstance()
if($collsettings -is [Object])
{
$collsettings.CollectionID =$COLLECTION_ID
$collsettings.get()
$windows=$collsettings.ServiceWindows
if ($windows -is [Object])
{
$now=Get-Date
Foreach ($window in $windows)
{
$Time=WMI-DateStringToDate($window.StartTime)
# only check general maintenance and non recurring windows
if (($window.IsEnabled -eq$True) -and ($window.ServiceWindowType -eq‘1’) -and ($window.RecurrenceType -eq‘1’))
{
# check if starttime is within the next 10-15 min.
if (($now.AddMinutes(15).compareto($Time) -eq‘1’) -and ($now.AddMinutes(10).compareto($Time) -eq‘-1’))
{
# add 15 min to duration as buffer
$duration=$window.Duration+15;
write-host ($Time.ToString(),$Duration) -Separator ‘;’
}
}
}
}
}

Another thing to mention: Please add an exclude to the link between “Get Collection Member” and “Get FQDN” for your Management Servers: Member Name from Get Collection Member equals SCOMMGServerName.
Then they will not be set into maintenance mode if they are members of the checked collections.

Update

I found some problems with the daylight saving settings on the runbook server. We use UTC maintenance windows in SCCM. With daylight saving the local time of the runbook server gets adjusted but the maintenance window stays in standard UTC. The script compares the local time with the maintenance window. With the old version it sets the maintenance window at the wrong time when daylight saving is enabled.

Therefore I had to adjust the script. Here is the new version. The italic entries are new.

cmd.exe /c | c:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe –c “function WMI-DateStringToDate($time) {  [System.Management.ManagementDateTimeconverter]::ToDateTime($time);};$collsettings = ([WMIClass] ‘\\SCCM Server FQDN\root\SMS\site_SCCMSiteCode:SMS_CollectionSettings’).CreateInstance();if($collsettings -is [Object]){$collsettings.CollectionID = ‘Link to Line Text of previous activity’;$collsettings.get();$windows=$collsettings.ServiceWindows;if ($windows -is [Object]){$now=Get-Date;$universal=$now.ToUniversalTime().AddHours(([System.TimeZoneInfo]::Local).baseutcoffset.hours);$diff=($now.subtract($universal)).Hours;Foreach ($window in $windows){$Time=WMI-DateStringToDate($window.StartTime);if (($window.IsEnabled -eq $True) -and ($window.ServiceWindowType -eq ‘1’) -and ($window.RecurrenceType -eq ‘1’)){if (($now.AddMinutes(15).compareto($Time.AddHours($diff)) -eq ‘1’) -and ($now.AddMinutes(10).compareto($Time.AddHours($diff)) -eq ‘-1’)){$Duration=$window.Duration+15;write-host ($Time.ToString(),$Duration) -separator ‘;’}}}}}”

Here is the link to the runbook.