How do I use WMI to get the current OU of a computer and list all other computers in that OU?

user2369812 picture user2369812 · Dec 9, 2014 · Viewed 18.4k times · Source

I'm using WMI and am trying to find a powershell script that will allow me get the OU of the local computer and then get a full list of computers in that OU.

Answer

Bacon Bits picture Bacon Bits · Dec 9, 2014

Here you go:

$ComputerName = '<Name of Computer>';
$Computer = Get-WmiObject -Namespace 'root\directory\ldap' -Query "Select DS_distinguishedName from DS_computer where DS_cn = '$ComputerName'";
$OU = $Computer.DS_distinguishedName.Substring($Computer.DS_distinguishedName.IndexOf('OU='));
$ComputersInOU = Get-WmiObject -Namespace 'root\directory\ldap' -Query "Select DS_cn, DS_distinguishedName from DS_computer where DS_distinguishedName like '%$OU'";

I think this also finds computers in child OUs, but I'm not sure how to limit it to a single OU without doing a ton of queries. The query syntax is just rather sparse. Eliminating the child OU objects after retrieval of the full list might be the only way to do it with any semblance of performance.

Fair warning: This is slow. Really slow. Like "Oh, crap did I break something?!" slow. I pointed it at a computer that shares an OU with less than 20 other computers, and it takes nearly a minute to run. Even the first fetching of just the single computer takes more than 1 second.

Here's what I would recommend:

$ComputerName = '<Name of Computer>';
Import-Module -Name ActiveDirectory -Cmdlet Get-ADComputer, Get-ADOrganizationalUnit;
$Computer = Get-ADComputer $ComputerName;
$OU = $Computer.DistinguishedName.SubString($Computer.DistinguishedName.IndexOf('OU='));
$ComputersInOU = Get-ADComputer -Filter * -SearchScope OneLevel -SearchBase (Get-ADOrganizationalUnit $OU).DistinguishedName;

That takes 2 seconds, including loading the Active Directory module. With it already loaded, that takes less than 200 milliseconds.

If you don't have access to the ActiveDirectory PowerShell module, then you can use an [ADSISearcher]. These are also a pain to use because of how results are presented, but they're even faster than the ActiveDirectory module, which is basically just a wrapper for this.

$ComputerName = '<Name of Computer>';
$ADSISearcher = New-Object System.DirectoryServices.DirectorySearcher;
$ADSISearcher.Filter = '(&(name=' + $ComputerName + ')(objectClass=computer))';
$ADSISearcher.SearchScope = 'Subtree';
$Computer = $ADSISearcher.FindAll();

$OU = $($Computer.Properties.Item('distinguishedName')).Substring($($Computer.Properties.Item('distinguishedName')).IndexOf('OU='));
$OUADsPath = 'LDAP://' + $OU;

$ADSISearcher = New-Object System.DirectoryServices.DirectorySearcher;
$ADSISearcher.Filter = '(objectClass=computer)';
$ADSISearcher.SearchScope = 'OneLevel';
$ADSISearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry($OUADsPath);
$ComputersInOU = $ADSISearcher.FindAll();

This runs in about 50 milliseconds.

However, beware that the ADSI system is known to contain memory leaks if it's not called correctly or if FindAll() is called and the results are never used. I myself have created objects with this method and then not disposed of them and left my shell process open overnight, and when I came in the next morning my system was all but unresponsive because all the memory had been consumed. The ActiveDirectory module avoids these problems entirely, and is even more code light, so unless you really need those extra few milliseconds than I would favor that module.