SCCM 2012 R2 – Automatically populate and balance device collections with PowerShell 3.0

I had to recently throw a script together to address a need to automatically populate collections so that software could be deployed to a large environment in stages.

The designed solution was very interesting. Rather than deploying to 80,000 devices in a single hit, it was decided to deploy to deploy to 50 pilot users on Monday, 3,000 on Tuesday, 3,000 on Wednesday, 3,000 on Thursday and then all remaining machines on Friday.

To accomplish this, maintenance windows were introduced. This left one problem, how do you make sure the groups stay populated without someone having to manually count out 3,000 machine per group? It also got pointed out that a large hardware migration was imminent and trying to keep track of which ones were and were not in the group was going to get very complicated. Many ideas were thrown around including using exclude queries, but they all had the same downside… They required someone to regularly maintain these collections.

I turned to my favourite place for the solution, PowerShell.

This script is a very manual script and you will need to make sure that you have device collections to match the ones in the script. The only one that can be easily changed is the source collection. When I have time to sit down and refine this script I will post it here so that you can transfer it to your site easier

I am not going to go into details on each section of the script at this point but I will post it here if anyone wants something like this

###############################################################################
####                                                                       ####
####                                                                       ####
####                Maintenance Windows Collection Autofill                ####
####                            Version 1.0.1                              ####
####                       Created by: Liam Matthews                       ####
####                                                                       ####
####                                                                       ####
###############################################################################
 
################################Release notes##################################
####                                                                       ####
################################### 1.0.0 #####################################
#### - Initial release                                                     ####
####                                                                       ####
################################### 1.0.1 #####################################
#### - Changed source collection to be a variable                          ####
####                                                                       ####
###############################################################################
 
##User defined variables
$SCCMSiteCode = "A00"
$LogLocation = "E:\Scripts\"
$Component = "Maintenance Windows Collection Autofill"
$LogFileName = "Maintenance Windows Collection Autofill.log"
$SourceCollection = "Maintenance Windows - Scope"
 
##Machine Variables
$SCCMModule = $env:SMS_ADMIN_UI_PATH.Replace("\bin\i386","\bin\configurationmanager.psd1")
$CurrentUser = [Environment]::UserName
$SCCMDrive = $SCCMSiteCode + ":"
$StartTime = Get-Date -Format "HH:mm:ss.fff"
$StartDate = Get-Date -Format "MM-dd-yyyy"
$StartDateTime = Get-Date -Format "HHmmss"
$TZbias = (Get-WmiObject -Query "Select Bias from Win32_TimeZone").bias
 
##Remove log file if larger than 1MB
Get-Item 'E:\Scripts\Maintenance Windows Collection Autofill.log' | Where {$_.Length -gt 1048576} | Remove-Item
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Starting new thread+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for Configuration 1Manager PowerShell module"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of  current PowerShell modules
$CurrentModules = Get-Module
 
IF ($CurrentModules.Name -contains "ConfigurationManager")
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "2"
    $LogText = "Configuration Manager PowerShell module has already been imported. This is unexpected but script will continue"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Configuration Manager PowerShell module is not currently present. Attempting import now"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Checking if SCCM PowerShell module is available for import
    IF (Test-Path $SCCMModule)
    {
        ##Import SCCM PowerShell module
        Import-Module $SCCMModule
 
        ##Get list of  current PowerShell modules
        $CurrentModules = Get-Module
             
        IF ($CurrentModules.Name -contains "ConfigurationManager")
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Configuration Manager PowerShell module has been imported successfully"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
        }
        ELSE
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "3"
            $LogText = "Configuration Manager PowerShell module has failed to import. The script will now Exit"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
             
            Exit
        }
    }
    ELSE
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "3"
        $LogText = "Configuration Manager PowerShell module does not exist on the current machine. The script will now exit"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
         
        Exit
    }
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for SCCM Drive: $SCCMSiteCode"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of PowerShell drives
$PSDrives = Get-PSDrive | Select Name
 
IF ($PSDrives.Name -contains $SCCMSiteCode)
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "$SCCMSiteCode drive exists"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Connecting to drive $SCCMSiteCode"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Connect to SCCM site
    CD $SCCMDrive
 
    ##Get current drive location
    $CurrentPSDrive = (get-location).Drive.Name
 
    IF ($CurrentPSDrive -eq "$SCCMSiteCode")
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "1"
        $LogText = "Successfully connected to $SCCMSiteCode"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
    }    
    ELSE
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "3"
        $LogText = "Unable to connect to $SCCMSiteCode. The script will now exit"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
        Exit
    }
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "3"
    $LogText = "SCCM drive $SCCMSiteCode does not exist. Module may have issues or variable may be incorrect. The script will now exit"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    Exit
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Searching for SCCM CAS site"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get SCCM CAS Server
$CASServerName = Get-CMSite | Where {$_.Type -eq "4"}
$CasServerName = $CASServerName.ServerName
 
IF ($CASServerName -ne $Null)
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Found SCCM CAS site $CASServerName"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "3"
    $LogText = "Unable to locate SCCM CAS site. The script will now exit"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    Exit
}
 
#Create blank array
$Devicestoadd = @()
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Identifying source collection"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get scope device collection
$MaintenanceWindowCollection = Get-CMDeviceCollection -Name $SourceCollection
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Getting list of devices in source collection"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##List devices in scope collection
$CollectionMembers = Get-WmiObject -ComputerName $CASServerName -Class SMS_CM_RES_COLL_$($MaintenanceWindowCollection.CollectionID) -Namespace root\sms\site_$SCCMSiteCode
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Identifying device membership"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
foreach ($CollectionMember in $CollectionMembers)
{
    ##List all collections that devices are members of
    $AllCollections = @()
    $ResID = (Get-CMDevice -Name $($CollectionMember.Name)).ResourceID
    $Collections = (Get-WmiObject -ComputerName $CASServerName -Class sms_fullcollectionmembership -Namespace root\sms\site_$SCCMSiteCode -Filter "ResourceID = '$($ResID)'").CollectionID
    foreach ($Collection in $Collections)
    {
        $DeviceCollectionMembership = Get-CMDeviceCollection -CollectionId $Collection | select Name, CollectionID
        $AllCollections += $DeviceCollectionMembership
    }
 
    IF ($AllCollections.name -Like "Maintenance Windows - Phase*")
    {
        ##Output to Log
        $Time = Get-Date -Format "HH:mm:ss.fff"
        $Date = Get-Date -Format "MM-dd-yyyy"
        $Type = "1"
        $LogText = $CollectionMember.Name + " is already part of a maintenance window collection"
        $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
        Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
    }
    ELSE
    {
        IF ($Devicestoadd.name -contains $CollectionMember.Name)
        {
        }
        ELSE
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = $CollectionMember.Name + " is not part of a maintenance window collection. Adding it to list to manage"
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
             
            ##Add device to array
            $Devicestoadd += $CollectionMember
        }
    }
 
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Getting list destination collections"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Identify destination collections
$Phase2Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 2"
$Phase3Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 3"
$Phase4Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 4"
$Phase5Collection = Get-CMDeviceCollection -Name "Maintenance Windows - Phase 5"
 
$Phase2CollectionCount = $Phase2Collection.LocalMemberCount
$Phase3CollectionCount = $Phase3Collection.LocalMemberCount
$Phase4CollectionCount = $Phase4Collection.LocalMemberCount
$Phase5CollectionCount = $Phase5Collection.LocalMemberCount
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase2Collection.Name + " currently has " + $Phase2CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase3Collection.Name + " currently has " + $Phase3CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase4Collection.Name + " currently has " + $Phase4CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = $Phase5Collection.Name + " currently has " + $Phase5CollectionCount + " Members"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
IF ($Devicestoadd.Count -eq "0")
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "No new devices to add to collections"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = "Adding devices to maintenance window collections"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    foreach ($Devicetoadd in $Devicestoadd)
    {
        #Find collections to add device to
        IF (($Phase2CollectionCount -lt "3000") -and ($Phase2CollectionCount -le $Phase3CollectionCount) -and ($Phase2CollectionCount -le $Phase4CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase2Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase2Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            #Manually increase collection count
            $Phase2CollectionCount += 1
 
        }
        ELSEIF (($Phase3CollectionCount -lt "3000") -and ($Phase3CollectionCount -le $Phase2CollectionCount) -and ($Phase3CollectionCount -le $Phase4CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase3Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase3Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            #Manually increase collection count
            $Phase3CollectionCount += 1
        }
        ELSEIF (($Phase4CollectionCount -lt "3000") -and ($Phase4CollectionCount -le $Phase2CollectionCount) -and ($Phase4CollectionCount -le $Phase3CollectionCount))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase4Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase4Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            ##Manually increase collection count
            $Phase4CollectionCount += 1
        }
        ELSEIF (($Phase4CollectionCount -ge "3000") -and ($Phase4CollectionCount -ge "3000") -and ($Phase4CollectionCount -ge "3000"))
        {
            ##Output to Log
            $Time = Get-Date -Format "HH:mm:ss.fff"
            $Date = Get-Date -Format "MM-dd-yyyy"
            $Type = "1"
            $LogText = "Adding device: " + $CollectionMember.Name + " to device collection: " + $Phase5Collection.Name
            $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
            Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
            #Add device to collections
            Add-CMDeviceCollectionDirectMembershipRule -CollectionName $Phase5Collection.Name -ResourceID $Devicetoadd.ResourceID
 
            ##Manually increase collection count
            $Phase5CollectionCount += 1
        }
    }
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase2Collection.Name + " now has " + $Phase2CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase3Collection.Name + " now has " + $Phase3CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase4Collection.Name + " now has " + $Phase4CollectionCount + " Members"
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
    ##Output to Log
    $Time = Get-Date -Format "HH:mm:ss.fff"
    $Date = Get-Date -Format "MM-dd-yyyy"
    $Type = "1"
    $LogText = $Phase5Collection.Name + " now has " + $Phase5CollectionCount + " Members "
    $LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
    Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Thread finished+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($Component)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"

2 thoughts on “SCCM 2012 R2 – Automatically populate and balance device collections with PowerShell 3.0

  1. We solved the problem by making custom collections populated from AD, based on computer name. Computers ending with a ‘1’ went in group 1, computers ending with 2 went into group 2… etc.

    • If that works for your environment then that’s great, every environment is different and it’s important for people to understand this and tailor a solution to suite

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s