SCCM/MECM | Phased Collections

References

https://stackoverflow.com/questions/56990719/split-array-by-percentage

Overview

Not to be confused with native MECM Phased Collections, the solution I’ve configured here is designed to create smaller collections based on a single source collection. The smaller collections can be be split as required (For example, 10 collections of 10%, two collections of 25% and one collection of 50% etc.)

I prefer this method to native MECM Phased Collections, as it offers more control over when and how deployments occur. However, I do recommend you check out the built-in feature I’ve listed above, as you might find it useful.

How to use

Configure your variable set:

  • $SiteServerName – Your SCCM/MECM Site Server Name
  • $SiteCode – Your SCCM/MECM Site Code (Include the colon suffix)
  • $CollectionPrefix – Define any name for your final Collections. Each Collection will include a numbered suffix starting from one.
  • $SourceCollectionID – Collection ID of the Collection you would like to base your resultant collections from (For example, the options from the script below uses the Collection ID from the “All Systems” Collection)
  • $CollectionDestinationFolder – Enter a pre-existing folder name here if you’d like the Collections to be stored under a specific folder.
  • $DevicePercentageSplitType – Select your Percentage Split type based on the examples defined in the script (For example, $Split1, $Split2, $Split3 or $Split4. Feel free to define your own here)

If you have any questions, feel free to post a comment below.

Script
#MECM Phased Collections
#The MECM Admin Console must be installed on the host machine.
#References | https://stackoverflow.com/questions/56990719/split-array-by-percentage
#Contact | Josh Woods
###Editable Variables###
$SiteServerName = "SCCMServerName"
$SiteCode = "AAA:" #(Include colon suffix)
$CollectionPrefix = "Test"
$SourceCollectionID = "SMS00001"
$CollectionDestinationFolder = "Test Collections"
#Choose Percentage Split type from the following list and Define under the $DevicePercentageSplitType Variable (See the example below)
$Split1 = 5, 10, 20, 25, 40
$Split2 = 10, 30, 60
$Split3 = 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
$Split4 = 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
#Define Selected Split Here
$DevicePercentageSplitType = $Split1
###Editable Variables End###
#ChatGPT helped me out a little with this snippet. The MECM Install path is defined as an environment variable. Pretty cool.
# Check if MECM Admin Console path is defined as an environment variable
$ConsolePath = [System.Environment]::GetEnvironmentVariable('SMS_ADMIN_UI_PATH', [System.EnvironmentVariableTarget]::Machine)
if ($ConsolePath -ne $null) {
#Remove i386 from path
$ConsolePath = $ConsolePath.Replace("\i386","")
#Set MECM Location to run cmdlets
Set-Location $ConsolePath
Import-Module .\ConfigurationManager.psd1
Set-Location $($SiteCode)
} else {
Write-Host "The MECM Admin Console is not installed. Please install the Admin Console before running this script."
Read-Host "Select any key to exit"
Exit 1
}
#Get full list of hostnames and ResourceIDs
$MasterList = Get-ciminstance -Namespace ROOT\SMS\SITE_SRH -ComputerName $SiteServerName -Query "SELECT Name, ResourceID FROM SMS_CM_RES_COLL_$SourceCollectionID" | Select-Object -expandproperty ResourceID
###################################################################################################################
# Section Sourced from | https://stackoverflow.com/questions/56990719/split-array-by-percentage
$Index = 0
$BatchList = foreach ($PL_Item in $DevicePercentageSplitType)
{
$BatchSize = [math]::Round($MasterList.Count * $PL_Item / 100, 0)
# the leading comma forces PoSh to NOT unroll the array
# instead, it is stored as whole
,$MasterList[$Index..($Index + $BatchSize – 1)]
$Index = $Index + $BatchSize
}
$BatchList[0] -join ','
'=' * 30
$BatchList = $BatchList.ForEach({$_ -join ','})
###################################################################################################################
foreach ($Batch in $BatchList) {
#Create increment number for collection
$CollectionNumber = $CollectionNumber + 1
#Create Collections
New-CMDeviceCollection -Name "$CollectionPrefix`_$CollectionNumber" -LimitingCollectionName "All Systems"
#Splits comma delimited values into a useable array
$SplitDevices = $Batch -split ","
#Count number of Objects
$ObjectCount = $SplitDevices.count
#Reset Count
$Count = $null
#Creating the Output Array
$Array1Output = @()
foreach ($Resource in $SplitDevices) {
#Output to console
Clear-Host
Write-Host "`n"
$Count = $Count + 1
Write-Host " $Count/$ObjectCount"
$Array1Output += [int32]$Resource
}
#Add collection query
Get-CMCollection -Name "$Collectionprefix`_$CollectionNumber" | Add-CMDeviceCollectionDirectMembershipRule -ResourceId $Array1Output
#Move Collection to selected folder
$TargetFolder = Get-CMDeviceCollectionFolder -Name $CollectionDestinationFolder
$Collection = "$Collectionprefix`_$CollectionNumber"
Move-CMObject -InputObject $Collection -DestinationFolder $TargetFolder
}