having to populate a device collection in SCCM can be troubling at times. Adding each machine or several machines at a time is very time consuming. Even if you try to add a machine 1 at a time through a script can take a really long time if you have hundred or thousand machines.
Instead of adding the device directly, I recommend writing out a query and adding the machine(s) through a query.
1 thing to be aware of is if you add several machines (over 500) you may receive a quota limitation error. However if you take that query directly and add it straight to SCCM it should add without a problem. I believe this is a limitation within the SCCM module and not within SCCM directly.
That being said 1 thing that I did to work around the quota issue is take all the servers and separate them into groups.
Let’s say you have 1200 servers and you want to add all of them into a collection. We can split them up in groups of 500.
So you have 500 + 500 + 200 (3 groups total)
Each group will be added into the query and then keep looping until all are added.
Here is an example on how I did this.
In the example below I was able to add over 1400 servers into my collection in under 5 seconds. If I were to try and add each machine individually, it would take well over 20 minutes to run.
CLS
#Limit each group to 500 machines per group
$Limit = 500
#=================== Clear Variables
Clear-Variable Servers, groupCount -Force -Confirm:$False -ErrorAction SilentlyContinue
#=================== Get all Servers
$properties = @('distinguishedName', 'dNSHostName', 'name', 'operatingSystem')
$Servers = (Get-ADObject -LDAPFilter "(&(objectClass=Computer)(objectCategory=Computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(operatingSystem=*Server*)(!(servicePrincipalName=*MSClusterVirtualServer*)))" -Properties $properties)
#===================
$SCCMServer = "SCCMServer"
$SessionsRunning = get-pssession
#Create a new Session to the SCCM Server
if(!($SessionsRunning.ComputerName -like $SCCMServer) -and ($env:COMPUTERNAME -ne $SCCMServer))
{
$Session = New-PSSession -ComputerName $SCCMServer -Authentication Kerberos
}
#Calculate how many groups we will have
$groupCount = [Math]::Ceiling($Servers.Count / $Limit)
Invoke-Command -Session $Session -ScriptBlock {
#Get Site Code & Path
[string]$SiteCode = (((Get-WmiObject -Namespace "root\SMS" -Class "SMS_ProviderLocation") | Select-Object | Where-Object { $_.__RELPATH -like "*$env:COMPUTERNAME*"}) | Select-Object -ExpandProperty SiteCode)
[string]$SitePath = $SiteCode + ":"
#Import our Module. If it fails loading, it will loop and try to reload until it succeeds.
While($True)
{
CLS
Try
{
Import-module (Join-Path (Split-Path $env:SMS_ADMIN_UI_PATH) "ConfigurationManager.psd1") -Force -ErrorAction Stop #-Verbose
Break
}
Catch
{
(Get-Module -Name "ConfigurationManager") | Remove-Module
Start-Sleep -Milliseconds 100
}
}
#If The module is imported without any issues, but we don't have a drive mapped, check to see if the provider is available
if($NULL -eq ((Get-PSDrive -ErrorAction SilentlyContinue) | Select-Object | Where-Object { $_.Provider -like "*CMSite*" }))
{
#Get-PSProvider
((Get-PSProvider -ErrorAction SilentlyContinue) | Select-Object | Where-Object { $_.name -like "*CMSite*" }) | fl *
#Create the Drive
Try
{
New-PSDrive -Name $SiteCode -PSProvider "AdminUI.PS\CMSite" -Root "$ENV:COMPUTERNAME" -Description "SCCM Primary Site" -ErrorAction Stop
}
Catch
{
Write-Error $_.Exception
}
}
#Set our Path to the CMSite
if((Get-Location -ErrorAction SilentlyContinue).Path -ne $($SitePath + "\"))
{
Set-location $sitepath
}
#=================== Create new Device collection
$CollectionName = "Test Collection"
$LimitingCollectionName = "All Systems" # The collection to limit membership
$Comment = "This is just a TEST"
New-CMDeviceCollection -Name $CollectionName -LimitingCollectionName $LimitingCollectionName -Comment $Comment -ErrorAction SilentlyContinue | Out-Null
for ($i = 1; $i -le $using:groupCount; $i++)
{
#=================== Create our Query
#Clear variables
Clear-Variable Group, endIndex, startIndex, FinalQuery, Query -Force -Confirm:$False -ErrorAction SilentlyContinue
# Create our start and end indexes
$startIndex = ($i - 1) * $using:Limit
$endIndex = [Math]::Min($startIndex + $($using:Limit - 1), $using:Servers.Count - 1)
$Group = ($($using:Servers.name)[$startIndex..$endIndex])
#The start of our Query
$Query = "SELECT SMS_R_SYSTEM.Name from SMS_R_System where SMS_R_System.Name in ("
#We will Append each server into the query string
foreach ($server in $Group)
{
$Query += '"' + $server + '",'
}
#Clean up the Query
[String] $FinalQuery = $Query.TrimEnd(",") + ")"
#=================== Add query to Collection
#Let's get the Collection Info we want to add all these machines to
$collection = Get-CMDeviceCollection -Name $CollectionName
# Add our Query to the membership rule
Try
{
Add-CMDeviceCollectionQueryMembershipRule -CollectionId $collection.CollectionID -QueryExpression $FinalQuery -RuleName "Test" -ErrorAction Stop
}
Catch
{
Write-Error $_.Exception
}
# Update the collection membership
Invoke-CMCollectionUpdate -Name $CollectionName
}
}