This content originally appeared on moyerteam.com (no longer active) and has been reproduced with permission of the author, Jamie Moyer.
There are several means to add users / computers to a collection in ConfigMgr. I had a question come in around how to speed up the addition of direct user rules to a given collection. This blog will discuss some options.
There is the Add-CMUserCollectionDirectMembershipRule cmdlet. This cmdlet adds a rule that adds a specific user resource to a given collection.
This cmdlet works great, but if you need to add a lot of rules, its slow.
The alternative?
Use the AddMembershipRules Method in the SMS_Collection class to add an array of rules vs. one at a time.
How much of a difference does it make? See my test results below, adding 18 rules to a test collection:
Add-CMUserCollectionDirectMembershipRule cmdlet: 24 seconds
AddMembershipRules: 3 seconds

Sample Script:
PARAM ([string]$SccmServer = "cm01",
[string]$CollectionID = "CAS0000E",
[array] $UserNames = 'LITWARE\!ac,LITWARE\Administrator'
)
Function Main
{
$sccmProviderLocation = Get-WmiObject -query "select * from SMS_ProviderLocation where ProviderForLocalSite = true" -Namespace "root\sms" -computername $SccmServer
$SiteCode = $sccmProviderLocation.SiteCode
$SccmNamespace = "root\sms\site_$SiteCode"
Add-SCCMDirUserCollectionRules -SccmServer $SccmServer -SccmNamespace $SccmNamespace -CollectionID $CollectionID -UserNames $UserNames.split(',')
}
Function Add-SCCMDirUserCollectionRules
{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)][string] $SccmServer,
[Parameter(Mandatory=$true)][string] $SccmNamespace,
[Parameter(ValueFromPipelineByPropertyName=$true)][String] $CollectionID,
[Parameter(ValueFromPipeline=$true)][array] $UserNames
)
$coll = [wmi]"\\$($SccmServer)\$($SccmNamespace):SMS_Collection.CollectionID='$CollectionID'"
$ruleClass = [WMICLASS]"\\$($SccmServer)\$($SccmNamespace):SMS_CollectionRuleDirect"
[array]$rules = $null
#1. Iterate over passed in array of users
#2. Call Get-SCCMUserInfo function to get resourceid and name
#3. Create a new instance of SMS_CollectionRuleDirect for each user
#4. Add the instance of SMS_CollectionRuleDirect to the $rules array
$UserNames | ForEach-Object {
$userinfo = Get-SCCMUserInfo -SccmServer $SccmServer -SccmNamespace $SccmNamespace -UserName $_
if($userinfo -ne $null)
{
$NewRuleName=$UserRule.name
$NewRuleResourceID = $UserRule.ResourceID
$newRule = $ruleClass.CreateInstance()
$newRule.RuleName = $($userinfo.name)
$newRule.ResourceClassName = "SMS_R_User"
$newRule.ResourceID = $($userinfo.resourceid)
$rules += $newRule
}
}
#Add all the rules in the array
#See: http://msdn.microsoft.com/en-us/library/hh949023.aspx
$coll.AddMembershipRules($rules) | Out-Null
#Refresh the collection
$coll.requestrefresh() | Out-Null
}
Function Get-SCCMUserInfo
{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)][string] $SccmServer,
[Parameter(Mandatory=$true)][string] $SccmNamespace,
[Parameter(ValueFromPipeline=$true)][string]$UserName
)
$userInfo = gwmi -ComputerName $SccmServer -Namespace $SccmNamespace -Class "SMS_R_User" -Filter "UniqueUserName = '$($($UserName).replace('\','\\'))'" | select name,resourceid
$userInfo
}
#call main
. main
UPDATE: But I want to add device rules vs. user rules….
PARAM ([string]$SccmServer = "cm02",
[string]$CollectionID = "PRI0243",
[array] $MachineNames = 'CM01,CM02'
)
Function Main
{
$sccmProviderLocation = Get-WmiObject -query "select * from SMS_ProviderLocation where ProviderForLocalSite = true" -Namespace "root\sms" -computername $SccmServer
$SiteCode = $sccmProviderLocation.SiteCode
$SccmNamespace = "root\sms\site_$SiteCode"
Add-SCCMDirectMachineRules -SccmServer $SccmServer -SccmNamespace $SccmNamespace -CollectionID $CollectionID -MachineNames $MachineNames.split(',')
}
Function Add-SCCMDirectMachineRules
{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)][string] $SccmServer,
[Parameter(Mandatory=$true)][string] $SccmNamespace,
[Parameter(ValueFromPipelineByPropertyName=$true)][String] $CollectionID,
[Parameter(ValueFromPipeline=$true)][array] $MachineNames
)
$coll = [wmi]"\\$($SccmServer)\$($SccmNamespace):SMS_Collection.CollectionID='$CollectionID'"
$ruleClass = [WMICLASS]"\\$($SccmServer)\$($SccmNamespace):SMS_CollectionRuleDirect"
[array]$rules = $null
#1. Iterate over passed in array of users
#2. Call Get-SCCMMachineInfo function to get resourceid and name
#3. Create a new instance of SMS_CollectionRuleDirect for each machine
#4. Add the instance of SMS_CollectionRuleDirect to the $rules array
$MachineNames | ForEach-Object {
$MachineInfo = Get-SCCMMachineInfo -SccmServer $SccmServer -SccmNamespace $SccmNamespace -MachineName $_
if($MachineInfo -ne $null)
{
$NewRuleName=$UserRule.name
$NewRuleResourceID = $UserRule.ResourceID
$newRule = $ruleClass.CreateInstance()
$newRule.RuleName = $($MachineInfo.name)
$newRule.ResourceClassName = "SMS_R_System"
$newRule.ResourceID = $($MachineInfo.resourceid)
$rules += $newRule
}
}
#Add all the rules in the array
#See: http://msdn.microsoft.com/en-us/library/hh949023.aspx
$coll.AddMembershipRules($rules) | Out-Null
#Refresh the collection
$coll.requestrefresh() | Out-Null
}
Function Get-SCCMMachineInfo
{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)][string] $SccmServer,
[Parameter(Mandatory=$true)][string] $SccmNamespace,
[Parameter(ValueFromPipeline=$true)][string]$MachineName
)
$MachineInfo = gwmi -ComputerName $SccmServer -Namespace $SccmNamespace -Class "SMS_R_System" -Filter "Name = '$($MachineName)'" | select name,resourceid
$MachineInfo
}
#call main
. main