PowerShell & SCCM – Maintenance windows for patching (& finding machines with more than one)

SCCM Maintenance Windows

In my environment, we use SCCM maintenance windows (MW) for patching. Every weekend there is a large change window in which we can do work and we have dozens of servers defined for each hour during that window to receive their patches. We also have production servers going different weekends than dev/qa servers. It is important that our servers ONLY patch during the defined hour and weekend. We don’t want them all going at the same time either, this is why they are segregated by the hour.

To accomplish segregated patching like this, we created an Active Directory group for each maintenance window hour  and weekend.



We then created collections named exactly like the AD groups…


… and after creating the matching collection, configure it to use a query rule [this is the piece that ties it to the AD group if you wish to control these groups via AD]

Add Rule > Query Rule > Edit Query Statement > Criteria Tab > click the star



And finally we applied a maintenance window to the collection.


This takes a little bit of work to at the beginning, but with this method, all we have to do is ensure the computer object in AD is added to the appropriate MW group and it will acquire the appropriate window in SCCM – ensuring that it will not patch during any other time. This is great for environments where you need to control specifically when machines patch – typically servers.



PowerShell Script

This method has led me to realize a machine could have multiple maintenance windows. If they are a member of several collections and the collections all have a MW – the machine inherits all of the maintenance windows and can patch during any of them. This could be on purpose… but in our environment, we definitely don’t want that. Mistakes could be made though – and that’s where PowerShell comes in handy.

In the post above, you will notice that all of my MW groups have similar formatting and they all start with ‘SCCM-MW’. It is important that you find a naming standard that works for you and stick to it. I also suggest starting them all with something similar like what I have – it makes them easier to find and query.



The following script can be setup to run on a reoccurring schedule on your favorite utility server. Make sure you have the active directory module and also go through it and edit the email adddresses. It queries AD for all “Windows servers” and checks to see if they are a member of more than one group that starts with ‘SCCM-MW’ – if they are, it will email you a report. It will then check to see if there are any machines NOT in a MW and add them to the report as well.

*I found it best to create a group called ‘SCCM-MW_Unassigned’ and give it a maintenance window with no time. Then assign all new machines to this group until they are assigned to an appropriate MW. If you deploy updates to this ‘unassigned’ group they will still pull the updates into Software Center but they will not install automatically (perfect!)


Import-Module activedirectory
#$ErrorActionPreference = "silentlycontinue"

$msg = $null

# Function 01 #

function FunSendEmailAlert{
    $subject = "** SCCM Alert - One or more servers have issues with their maintenance windows **"
    $body = "There are one or more servers with either:<br> - Multiple maintenance windows (MWs) or; <br> - They do not have ANY maintenance windows.<br><br>"
    $body = $body + "(If you do not wish to have the server in a maintenance window, add it to <b>SCCM-MW_Unassigned</b>)<br><br><br>"
    $body = $body + "<font color=red>$msg</font>"
    Send-MailMessage -from "SCCM-Alerts@yourcompany.com" -to "servergroup@yourcompany.com" -subject $subject -body $body -BodyAsHtml -smtpserver "mail-server.yourcompany.com"


#get all windows servers
$wincomps = Get-ADComputer -Filter {operatingsystem -like '*windows server*'} -Properties memberof

#get servers with multiple MWs
$comps = $wincomps | Where-Object {$_.memberof -like "*SCCM-MW*"} | select SamAccountName | Sort

foreach ($c in $comps) {
    $var = $c.samaccountname
    $groups = Get-ADPrincipalGroupMembership $var | select name
    $count = 0
        foreach ($g in $groups) {
            if ($g.name -like "*SCCM-MW*")
                {$count = $count + 1}
If ($count -gt 1)
    {"$var is a member of multiple SCCM Maintenance Windows"
    $script:msg += "$var is a member of multiple SCCM MWs<br>"}


#get servers not in a MW
$comps2 = $wincomps | Where-Object {!($_.memberof -like "*SCCM-MW*")} | select SamAccountName | sort

foreach ($c in $comps2) {
    $var2 = $c.SamAccountName
    "$var2 is NOT a member of any SCCM Maintenance Windows"
    $script:msg += "$var2 is NOT a member of any SCCM MWs<br>"}
#send email if there are any machines with multiple MWs or NO MWs
If ($msg -ne $null)




  1. Hi, Adam. Found your blogs tonight – it’s good to see practical info. for real scenarios.

    A question on this one….

    Other than mirroring one another for organizational purposes, do your AD OUs and your SCCM collections have any relationship or connection to each other?

    • Thanks for the comment. I am sorry for the late response…

      Other than “We then created collections based off the membership of that group in SCCM”, I don’t believe I have any other relationships between the two.

      To accomplish this, we configured the MW collection to have a query rule that looked for AD group name. This is actually kind of confusing to figure out at first so I will add that piece to the post (I should have included it in the first place!)

      Also, side note: We use SCCM on a much smaller scale and only patch our servers. Due to processes and teams that were formed well before my time, the desktops are on a completely different system for patching.

Leave a Reply

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