Configuration Manager PowerShell - Creating Collections from AD OUs
Is there an easy way to create collections based on my Active Directory OU's?
Customers often create collections in Configuration Manager to model their AD environment. In some environments, with lots of OU's, this can get an bit overwhelming. If you are like me, you see this as an opportunity to leverage some automation … PowerShell to the rescue!
The first thing that needs to be done is to get a list of OU's you want to create collections for. This could be for Users or Devices. In the example, I will be creating device collections.
Gathering the OU Information
AD provides us with a built-in tool that lets us quickly get a list of OU's in our domain using dsquery.
dsquery ou
This command will return a list of OU's in AD, all of them (up to the default 100 limit). The only problem, they are in distinguished name (DN) format. This is great, if we were building LDAP queries, but in Configuration Manager, to build collections based on OU, we need to use the canonical name (CN, not to be confused with container name in AD).
Let's look for a way to convert these DN's to CN's. We can use some PowerShell manipulation to help us out.
Let's take our dsquery, and pipe it out to a couple of commands to help us out
This first command allows us to strip the Quotation marks from the beginning and end of each line.
dsquery ou | % {$_.trimstart('"').trimend('"')}
Next we want to add a Split command to break each DN apart into its on elements.
dsquery ou | % {$_.trimstart('"').trimend('"')} | % {$_.Split(",")}
We add the next element, the Switch command. This is like a Select Case command in VBS. This allows us to read each split line, and decide what to do with it. If it begins with:
-
- OU=, we remove the OU= and add a leading slash (/) and store it in $x
- DC=, we add a leading period (.) and store it in $y
dsquery ou | % {$_.trimstart('"').trimend('"')} | % {$_.Split(",") | % {switch -Wildcard ($_) { "OU=*" {$x = '/' + $_.substring(3) + $x} ; "DC=*" {$y = $y + '.' + $_.substring(3)}}}
Then we need to concatenate our OU's and DC's together and clear our variables with the next step. We also need to remove the leading period (.) from the first DC.
dsquery ou | % {$_.trimstart('"').trimend('"')} | % {$_.Split(",") | % {switch -Wildcard ($_) { "OU=*" {$x = '/' + $_.substring(3) + $x} ; "DC=*" {$y = $y + '.' + $_.substring(3)}}} ; $ou = $y.TrimStart('.') + $x ; $x='' ; $y=''
Finally, we need to store the data in an array and then write it out to a text file.
$ous=@(); dsquery ou | % {$_.trimstart('"').trimend('"')} | % {$_.Split(",") | % {switch -Wildcard ($_) { "OU=*" {$x = '/' + $_.substring(3) + $x} ; "DC=*" {$y = $y + '.' + $_.substring(3)}}} ; $ou = $y.TrimStart('.') + $x ; $x='' ; $y='' ; $ous = $ous + $ou} ; $ous | Out-File C:\test.txt
The results text file will look like this.
You can edit it to remove the lines from OU's you do not wish to create as collections.
Creating Collection
For this next step, we are going to use our familiar friend, New-CMDeviceCollection. We want to use our text file as input. In this case I am going to use the same Canonical Name from AD as the name of the collection. This allows the collections to line up nicely in the console and also when trying to find them in Reporting Services.
We are going to use the Get-Content (gc) PowerShell command to read the text file and pass output to are ConfigMgr CmdLet.
gc C:\test.txt | % {New-CMDeviceCollection -name $_ -limitingcollectionname "All Systems"}
You can change the limiting collection are get real fancy and make each collection limited to its OU Parent (maybe in a different blog posting). Here we are being very straight forward and limiting our collections to All Systems.
The results of this will be a set of collections in the Device Collections node with the canonical name of the OU. We created the collection, so now we need to add membership rules.
Adding Membership Rules
This can accomplished using the Add-CMDeviceCollectionQueryMembershipRule (don't try to say that 3 times fast. Thank goodness for autocomplete in PowerShell!).
gc C:\test.txt | % {Add-CMDeviceCollectionQueryMembershipRule -collectionname $_ -queryexpression ('select * from SMS_R_System where SMS_R_System.SystemOUName like "' + $_ + '"') -RuleName $_}
gc C:\test.txt
Pass the output to a ForEach loop to be consumed in the CmdLet
| % {Add-CMDeviceCollectionQueryMembershipRule
Give the CmdLet the name of the Collection (which is the canonical name in the text file)
-collectionname $_
The expression to be used to create the Query Rule (which again uses the canonical name from the text file)
-queryexpression ('select * from SMS_R_System where SMS_R_System.SystemOUName like "' + $_ + '"')
Then a name for the rule (using the canonical name)
-RuleName $_}
Microsoft provides programming examples for illustration only,
without warranty either expressed or implied, including, but not
limited to, the implied warranties of merchantability and/or
fitness for a particular purpose.
This sample assumes that you are familiar with the programming
language being demonstrated and the tools used to create and debug
procedures. Microsoft support professionals can help explain the
functionality of a particular procedure, but they will not modify
these examples to provide added functionality or construct
procedures to meet your specific needs. If you have limited
programming experience, you may want to contact a Microsoft
Certified Partner or the Microsoft fee-based consulting line at
(800) 936-5200.
For more information about Microsoft Certified Partners, please
visit the following Microsoft Web site:
https://partner.microsoft.com/global/30000104
Comments
Anonymous
January 01, 2003
This is great. Thank you....huge timesaver. Question, however......how can you get all OU's in an environment versus just the default first 100? Is there a way to specify "beginning from", or something?Anonymous
January 01, 2003
I meant to include something about the -limit command and must have forget. Thanks for pointing it out! You could probably use the -filter argument for dsquery to narrow the results down further. technet.microsoft.com/.../cc754232(v=WS.10).aspx I prefer to just edit the notepad, but if you have a large number of OU's or that all exists in a sub OU, then -filter might be the way to go.Anonymous
January 01, 2003
Excellent! Thanks for sharing!Anonymous
January 01, 2003
Ok I just taught myself the -limit command, but still wondering about a starting point versus all.Anonymous
January 01, 2003
I figured out another way to get CanonicalNames Using PowerShell 3.0. Here's a sample script. $ScriptParent = "C:Temp" $CollectionCSV = $ScriptParent + "CollectionList.csv" $SearchBase = "OU=SpecificOU,DC=YourDomain,DC=LOCAL" Get-ADOrganizationalUnit -Filter * -SearchBase $SearchBase -SearchScope OneLevel -Properties CanonicalName | Select-Object CanonicalName | Export-CSV -LiteralPath $CollectionCSV -NoTypeInformation