Use a powershell script on your SCCM server to easily import your Device Collections based on OUs and then they will stay in "sync" based on your schedule.


Example OU Structure


blob1478183105964.png


Example Device Collections that are created


blob1478182922924.png


Example Schedule


blob1478183019929.png


The PowerShell Script

   

<#
    Use at your own risk!  But Zimco has never had it do anything bad.

    Original Script by Kaido Järvemets from http://cm12sdk.net/?p=1371
    Edited to include code by Ed Wilson from http://blogs.technet.com/b/heyscriptingguy/archive/2014/02/28/use-wmi-and-powershell-to-create-collection-query-rules.aspx
	Edited for K-12 Education customizations by Zimco SCCM Gurus http://www.zimco.net/ - 2016
	
#>

# Edit these paramaters to align with your K-12 school district.
Param(
    $StartOU = 'OU=BaseOU,DC=domain,DC=local',
    $SiteServer = $env:COMPUTERNAME,
    $SiteCode = '001', #Must be a string
    $ReverseOUFriendly = 1, #Enter 1 if you'd like to reverse the description - Jon Joles Recommends it!
    $ReverseOuFriendlyPrune = "local-domain-BaseOU-" #Redundant description to prune off - Jon Joles Recommends it!
)
# PLEASE NOTE - If the Collections don't import in the format you'd like 
# the first time you can always delete them in SCCM and reimport. #RetryFriendly :)
# Successive runs will only import new OUs.


$Search = [ADSISearcher]"(|(ObjectClass=organizationalUnit)(ObjectClass=Container))"
$Search.SearchRoot = "LDAP://$StartOU"
$Search.SearchScope = "OneLevel"
$Result = $Search.FindAll()

Function Get-OUChildOUs
{
    PARAM(
         $DN
    )

    $SearchSubOUs = [ADSISearcher]"((ObjectClass=organizationalUnit))"
    $SearchSubOUs.SearchRoot = $DN
    $SearchSubOUs.SearchScope = 'OneLevel'
    $SecondResult = $SearchSubOUs.FindAll()
	
    If($SecondResult.Count -ne 0){
        foreach($item in $SecondResult)
        {
            Set-CollectionQueries -OU $Item.Properties.distinguishedname
            Get-OUChildOUs -DN $Item.Path     
        }
    }
}

Function Set-CollectionQueries
{
    PARAM(
        $OU
    )

    
    $OUfriendly = (($OU -replace 'OU=') -replace 'DC=') -replace ',', '-'
    $OUsplit = $OUfriendly.split('-')
    if($ReverseOUFriendly -eq 1)
    {
        $OUfriendlyArr = $OUfriendly -split '-'
        [array]::Reverse($OUfriendlyArr)
        $OUfriendly = $OUfriendlyArr -join '-'
        $OUfriendly = $OUfriendly -replace $ReverseOuFriendlyPrune, ""
    }
    
    $CollectionExists = GWMI -ComputerName $SiteServer -Namespace "ROOT\SMS\Site_$($SiteCode)" -Class 'SMS_Collection' -Filter "Name = '$($OUfriendly)'"

    If (!$CollectionExists.CollectionID)
    {
        
    
        $OUquery = $OUsplit[$OUsplit.count - 2]+'.'+$OUsplit[$OUsplit.count - 1]
        $i = $OUsplit.count - 3
        While ($i -ne -1)
        {
            $OUquery = $OUquery+'/'+$OUsplit[$i]
            $i--
        }
        

        $CMCollection = ([WMIClass]"root\sms\site_$($SiteCode):SMS_Collection").CreateInstance()
        $CMCollection.Name = $OUfriendly
        $CMCollection.Comment = "Direct descendants of $($OUfriendly)"
        $CMCollection.LimitToCollectionID = 'SMS00001'
        $CMCollection.RefreshType = 4

        $CMCollection.Put()

        $CMRule = ([WMIClass]"root\sms\site_$($SiteCode):SMS_CollectionRuleQuery").CreateInstance()
        $CMRule.QueryExpression = "select * from SMS_R_System where SMS_R_System.SystemOUName = '$($OUquery)'"
        $CMRule.RuleName = $OUfriendly
 
        $CMCollection.AddMembershipRule($CMRule)

        $CMSchedule = ([WMIClass]"root\sms\site_$($SiteCode):SMS_ST_RecurInterval").CreateInstance()
        $CMSchedule.DaySpan = '1'
        $CMSchedule.StartTime = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime((Get-Date).ToString())
    
        $CMCollection.RefreshSchedule = $CMSchedule

        $CMCollection.Put()
    
        $CMCollection.RequestRefresh()
    
    }
}


Foreach($item in $Result)
{
    Set-CollectionQueries -OU $Item.Properties.distinguishedname
	Get-OUChildOUs -dn $item.Path
}

# Pause to allow for viewing of any errors when clicked to run.
pause