Creating Maintenance Window Collections

It’s been a long held tradition to not create deployments for collections that have Maintenance windows and there are in fact a LOT of other rules that come along with using maintenace windows in a production environment.

Almost a year or so ago I worked with a friend of mine to create some pretty cool stuff in the patching space and while it was cool and it ‘worked’ it was never going to see the light of day becuase it was unpolished an unfinished. So I’ve worked to correct that and I think the first piece of it is finally ready.

Let’s start with methodology. When I think about maintenance windows I think about a few things I think about naming standards and I think about re-usability. This is going to be a series of posts and will hopefully help drive some thoughts around patching that I’ve been having. When we create collections we should always have some type of naming standard the one that I’ve settled on, and you can change it if you don’t like it, is ‘MAINT – SERVER – ****’ for maintenance window collections and ‘SUM – SERVER – REQUIRED’ for a deployment collection. When creating a patch solution I tend to follow the same strategy as long term the idea simply works and I’ll explain why. First we decided how many days a month does it take us to patch everything. Is it 10 days? 30 days? 20 days? What’s the number. Once we decide that number we decide OK when do we START patching and then from there we build out our maintenance windows, but thats for another post. This post just deals with programatically creating collections.

For the sake of this example I am only creating maintenace window collections for servers, and I feel that I can patch all my servers in 10 days. Additionally (because I can) – I would like to break each of those 10 days up in to four hour maintenance windows. That means that to create 10 days worth of Maintenance window collections I need 60 yes 60 collections. That would really suck to create by hand wouldn’t it. So enter PowerShell. The below script will use information that you provide it to create collections for a specific number of days. Example Usage:

Create-MaintenanceCollections.Ps1 -LimitingCollectionID “SMS00001” -NumberofDays 3 -FolderPath “PR1:\DeviceCollection\Software Updates\Maintenance Collections”

If the above was executed 18 collections would be created that used SMS00001 as their limiting collection ID, and those collections would be created in “Device Collection\ Software Updates\ Maintenance Collections”

This is pretty useful because now I dont’ have to create a TON of crappy collections. The below code is also useful for creating other collections of a specific count if you simply change some of the examples around. You can download this code from GitHub:

https://github.com/JordanTheITGuy/ProblemResolution/blob/master/PowerShell/MaintenanceWindow/Create-MaintenaneCollections.ps1

NOTE This script assumes you have imported the ConfigurationManager Module and are in the PS Site drive.

<#
.SYNOPSIS
    This scripts creates maintenance window collections for servers based on provided criteria. 

.DESCRIPTION
    Use this script to create and move maintenace window collections to a desired location in your configuration manager environment. 
        

.EXAMPLE
    This script uses some parameters here is an example of usage:
    PR1:\> C:\scripts\Create-MaintenanceCollections.Ps1 -LimitingCollectionID "SMS00001" -NumberofDays 5 -FolderPath "PR1:\DeviceCollections\SUM - PatchingCollections\Maintenance Collections"

.NOTES
    FileName:    Create-MaintenanceCollections.PS1
    Author:      Jordan Benzing
    Contact:     @JordanTheItGuy
    Created:     2019-04-09
    Updated:     2019-04-09

    Version 1.0.1 - It works and creates stuff with no parameters and is cardcoded
    Version 1.0.2 - Added the ability to utilize Parameters
    Version 1.0.3 - Added verbosity to show each step as it goes along and some error checking. 

#>


param(
    [parameter(Mandatory = $true)]
    [string]$LimitingCollectionID,
    [parameter(Mandatory = $true)]
    [int]$NumberofDays,
    [Parameter(Mandatory = $true)]
    [string]$FolderPath
    )
#Ensure the Configuration Manager Module is loaded if it's not loaded see blog post on how to load it at www.scconfigmgr.com
Write-Verbose -Message "Confirming that the configuration manager module is loaded" -Verbose
if(!(Get-Module -Name ConfigurationManager)){
    Write-Error "YOU MUST HAVE THE CONFIGMGR MODULE LOADED"
    break
}
#Ensure the current location is the configuration manager provider PSdrive - if its not then break see how to connect to this location at www.scconfigmgr.com
Write-Verbose -Message "The configuration manager module IS LOADED continue" -Verbose
if(((Get-location).Path.Substring(0,4)) -ne "$(((Get-WmiObject -namespace "root\sms" -class "__Namespace").Name).substring(8-3)):"){
    Write-Error "YOU MUST BE IN THE CONFIGMGR DRIVE LOCATION"
    break
}
#Ensure the folder path you would like to move the collections to exists
Write-Verbose -Message "Now testing if the location to move the collections to exists and is written out properly." -Verbose
if(!(Test-Path -Path $FolderPath)){
    Write-Error -Message "The Path does not exist please re-run the script with a valad path"
    break
}
Write-Verbose "The location to move the collections to EXISTS and IS written out properly." -Verbose
#Set the naming standard for the collection name you MAY change this it's highly reccomended that you do NOT.
$MWName = "MAINT - SERVER - D"
Write-Verbose "The naming standard for your maintenance collections will be $($MWNAME) with the day after patch tuesday and window indication afterwords"
#Set the date counter to 0
$DayCounter = 0
#Create a list to store the collection names in. 
$list = New-Object System.Collections.ArrayList($null)
#Create a CMSchedule object - This sets the refresh on the collections you may change the below line otherwise collections will refresh weekly on saturday.
$Schedule = New-CMSchedule -Start (Get-Date) -DayOfWeek Saturday -RecurCount 1
Do
{
    #Add one to the day counter
    $DayCounter++
    #Create the new string - Collection name plus the count of days after patch tuesday.
    $NewString = $MWName + $DayCounter
    #Store the string into the list
    $List.add($NewString) | Out-Null
}
#Do this until the number of days you would like to have MW's for is reached.
while($DayCounter -ne $NumberofDays)
Write-Verbose "Created Day Names" -Verbose
#Create the Full list object - this will now add in the MW information (6 created per day each one is 4 hours long allowing you to patch anytime of the day)
$FullList = New-Object System.Collections.ArrayList($null)
#For each DAY/COLLECTION in the previous list CREATE 6 maintenance window collection names. 
foreach($Object in $list)
    {
        #Set the window counter back back to 0
        [int32]$WindowCounter = 0
        do 
            {
                #Add one to the window counter
                $WindowCounter++ 
                #Create the new collection name and add the nomenclature of W3 to it. 
                $NewCollection = $Object + "W" + $($WindowCounter.ToString())
                #Compile and store the finalized list name. 
                $FullList.Add($NewCollection) | Out-Null
            }
        #Do this until you reach 6 of them - you can of course change that if you really wanted to... but why? 
        while ($($WindowCounter.ToString()) -ne "6")
    }
#For each collection name in the FULL list of (MAINT - SERVER - D1W1 (example)) - create a collection limited to the specified limit and refresh weekly on Saturday.
Write-Warning -Message "The Action you are about to perfom will create $($FullList.Count) collections do you want to continue?" -WarningAction Inquire
Write-Verbose -Message "Created all MW Collection Names now creating the MW Collections" -Verbose
ForEach($CollectionName in $FullList)
    {
        try{
        #Create the collection
        Write-Verbose -Message "Now creating $($collectionName)" -Verbose
        #Change the below information to change information about the collection. 
        $Object = New-CMCollection -collectionType Device -Name $CollectionName -LimitingCollectionId $LimitingCollectionID -RefreshSchedule $Schedule -RefreshType Periodic
        #Move the collection to its final destination.
        Move-CMObject -FolderPath $FolderPath -InputObject $Object
        Write-Verbose -Message "Successfully created and moved $($collectionName) to its destination" -Verbose
        }
        catch
        {
            Write-Error -Message $_.Exception.Message
        }
    }
Write-Output -InputObject $("Completed the script succesfully")

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: