Active Directory OU picker revisited

A PowerShell scripter’s work is never done. As you develop scripts over the years your skills improve and the way you write scripts changes. Sometimes you find that scripts you once created are due for an overhaul. The very first script I posted in my blog (in effect my very first post ever) was a function that creates a GUI or windows form showing your domain’s OU structure. While the script proved to be useful to a lot of people (thanks for the positive feedback, readers) there were some limitations that needed to be addressed:

  • ActiveDirectory module was required
  • Long startup duration
  • Only the current domain could be browsed

Some of those limitations were pointed out to me in the comments with some helpful insights on how to remedy them. So I decided to revisit my earlier concept and create a brand-new version with some new features that make it look even closer to the ADUC console (Active Directory Users and Computers). First let’s look at how I eliminated said limitations:

Limitation reduction

  • ActiveDirectory module was required:
    I rewrote the script to utilize .NET and LDAP queries so it’ll run on any domain machine. This also greatly improves the speed of the script.
  • Long startup duration:
    Instead of creating the entire directory tree at once it now only shows one level. Once a node get’s expanded the next level is quickly created. Credit goes to @SuperCheeta for this solution. I’ve further improved his solution so the script now checks each OU for at least one child OU and if it exists it creates a dummy node to conjure up the expansion sign [+]. That way only OU’s (or folders) with children get the expansion sign.
  • Only the current domain could be browsed:
    By default the current user’s domain is displayed, but by using the context menu on the root node or the domain node you can switch domains. A smaller form will open where you can select the appropriate domain in a tree view, same as in ADUC. Here’s how it looks:

    You can also run the function with the -Domain parameter to specify a domain other than default.

New features

As you can see from the samples above I’ve replaced the New OU button I had in the previous version for a context menu item. The context menu itself is adaptive: you’ll get a different contents depending on the type of node you’re on. The container nodes have no context menu because you can’t create an OU in them.

Also, it’s no longer required to run the script with admin privileges to create an OU. It now uses ADSI to create OU’s instead of the command line tool. If the current user account does not have sufficient rights to create an OU the user will be prompted for different credentials.

If, for some reason, you want to hide the script’s ability to create a new OU you can utilize the -HideNewOUFeature parameter. It will stop the option from showing up in the context menu.

I’ve created a checkbox called Advanced Features which doesn’t really give you advanced features, but it’s named after it’s ADUC counterpart. When enabling Advanced Features in ADUC the console shows some folders (containers) that are otherwise hidden. The same concept applies to the Choose-ADOrganizationalUnit function. I put this feature in to mimic ADUC as closely as possible. I’m not sure if anyone will ever use it but it’s there anyway. The previous version showed these by default so it still counts as an improvement, right?

The output has been changed from a single string containing the distinguished name of the chosen OU to a PowerShell object that contains both the name and the distinguished name. Why you ask? … Because I can, that’s why! ;^)


Better, faster and snappier! That’s what I was going for and I’m pretty sure you’ll agree that I pulled it off. Please give the new function a whirl and don’t be shy on the comments! You can view/download it here: link

MicaH out!


15-06-2016: as per request I’ve added a parameter called -MultiSelect which will add checkboxes to all nodes, so you can now select multiple objects!! Also running the function from a non domain-joined computer is now supported by using the -Domain and -Credential parameters.

15-11-2016: I’ve added a button to the Change Domain GUI named Change Forest. It will allow you to connect to a different forest an pick a domain from there. Thanks @Ronald for your feedback. It’s a great addition to the featureset.


About MicaH

I'm a Technical Specialist at PepperByte BV (the Netherlands).
This entry was posted in Powershell, Sapien Powershell Studio and tagged , , , , , , , , , , . Bookmark the permalink.

86 Responses to Active Directory OU picker revisited

  1. Pingback: Active Directory OU picker in powershell | MicaH's IT blog

  2. supercheetah says:

    Nice! I’ll take a closer look at this when I’m back at work (I don’t run Windows at home, just Linux, so no computers right now to closely look at that code).

  3. NewbiePOSHadmin says:

    Is it possible to run this script on a computer that is not joined yet? I’m trying to use it to choose the OU to create the object in before joining it, all at the workstation.

  4. Michael says:

    I can’t seem to get this working with Move-ADobject -targetpath as it’s returning “@{Name=XXX; DistinguishedName=” in front of the string. Any ideas?

    • MicaH says:

      You should be able to do this:

      Move-ADObject -targetpath (Choose-ADOrganizationalUnit).DistinguishedName

      • Michael says:

        Thanks heaps! Your script is very useful to me 🙂

      • MicaH says:

        You’re welcome. Keep in mind that the command I showed you will produce an error if you cancel the OU picker. In a script it’s better to use it like this:

        $OU = Choose-ADOrganizationalUnit
        If ($OU)
            Move-ADObject -targetpath $OU.DistinguishedName
  5. Jason. says:

    Brilliant. I was just about to investigate writing this exact work and here it is. Nice. Any chance you can explain where you got the icons and how you converted them for adding to the ImageList. I understand what you are doing but would like to encode the icons myself as I am very wary of what I cannot see when it comes to production environments.

  6. Really fast in comparison to your older script! Love it just for that! Based on your previous work and some stuff on the internet I made my own OU picker with tick boxes ( Is it possible for you to add check boxes to your script to? So multiple OU’s can be selected at once? This would greatly benefit the usage.

  7. Ronald says:

    Great script but 2 things are bothering me. I cant seem to find out how to correct it.

    First the GUI pops up behind my PowerShell box. How can i make it front and center.
    Second where is the variable it save my choice to so i may use that variable later in the script?

    Thanks so much fantastic work!

    • MicaH says:

      You’re welcome! I’ll have to look into your first question, I never noticed it. To answer your second question: the function outputs a powershell object so you can save it in any variable you want. E.g.:

      $OU = Choose-ADOrganizationalUnit

      This will show the picker and save the output in variable $OU.

      • MicaH says:

        Maybe you could try the following: add the following line to the scriptblock $formChooseOU_Shown:


        The entire scriptblock should look like this:

        	#Build treeview when form is shown
        	$formChooseOU.Cursor = 'WaitCursor'
        		Show-Error ($_ | Out-String)
        		$formChooseOU.Cursor = 'Default'

        Please let me know if this does the trick.

    • Steve says:

      This module is extremely useful, thanks so much for sharing. I’m also having the problem where the OU browser pops up in the background. Have you had an opportunity to look into that? I’ll do some research on my own, but thought I’d ask in case you’ve already resolved this.

      • MicaH says:

        Hi Steve. I haven’t had the time to look into it. Can you post an example on how you call the function?

      • MicaH says:

        Maybe you could try the following: add the following line to the scriptblock $formChooseOU_Shown:


        The entire scriptblock should look like this:

        	#Build treeview when form is shown
        	$formChooseOU.Cursor = 'WaitCursor'
        		Show-Error ($_ | Out-String)
        		$formChooseOU.Cursor = 'Default'

        Please let me know if this does the trick.

  8. Pingback: Skype4B Analog Device Manager Preview and Cheat Snippet |

  9. Todd says:

    Any way to easily scope the root node? I’m building another GUI and including your first version in it now and I was able to change the root node to better scope what OU the users start at and see. setting the root node in v2 seems a little more difficult. Ideally, I’d love to be able to set two root nodes and show what’s under each. any possibility of doing that?

    • MicaH says:

      I don’t see why not. I suppose you could add a RootOU parameter and change the script to start from there. Excellent suggestion, I’ll see what I can conjure up. Might take some time, though, I do have a day job! ;^)

      • Tapochki3D says:

        My simple implementation for Root

        Choose-ADOrganizationalUnit -OURoot “OU=MyOU,DC=comtoso,DC=ru”

        #Add checkboxes so multiple objects can be selected

        function Build-TreeView
        $treeNodes = $Treeview.Nodes[0]
        If ($OUroot)
        { $RootDomainNode = Add-Node -dname $OUroot -name $OUroot -RootNode $treeNodes -Type Domain }

        #Generate rootdomain node and add subdomain nodes
        elseIf ($DomainDN)
        $DomainName = $DomainDN.Replace(‘,DC=’,’.’).TrimStart(‘DC=’)
        $RootDomainNode = Add-Node -dname $DomainDN `
        -name $DomainName -RootNode $treeNodes -Type Domain
        $CurrentDomain = $Forest.Domains | ?{$_.Name -eq $env:USERDNSDOMAIN}
        $Domain = $CurrentDomain.GetDirectoryEntry()
        $RootDomainNode = Add-Node -dname $Domain.distinguishedName `
        -name $CurrentDomain.Name -RootNode $treeNodes -Type Domain
        #Copy the RootDomainNode to parent scope
        New-Variable -Name RootDomainNode -Value $RootDomainNode -Scope 1


  10. Daniel Potter says:

    Love it, I’ve been using your first script slightly modified in a couple projects. I mentioned your blog in a post over at the scripting guys group on facebook.

  11. Yusuf says:

    Great Script 😉
    How can use this Script with HTA?
    From Hta call the “Active Directory OU picker” and Output back to HTA (Variable) to use.

  12. Daniel Potter says:

    Should the credentials part in the form load be not credential? Otherwise there would be no credential prompt.

    If (!($Credential)) {
    $Credential = Get-Credential


  13. Ronald says:

    I can’t select another domain. I have 2 domains which aren’t in the same forest.. Is that possible?

    • supercheetah says:

      The version I created (linked in the previous article in the comments, I’m on mobile right now) looks through the AD forest. To note though, it doesn’t do a lot of things that Micah’s does. I didn’t have a need for it beyond being an OU picker.

      Micah, feel free to steal that code

      • MicaH says:

        My version also looks through the AD forest @supercheetah. @Ronald has an environment with two seperate forests. Both our versions only work on the current forest, which is the forest the machine you’re running the script from is in.

        @Ronald: I’ll look into creating a Change Forest option in the context menu, but I don’t have a test environment for that yet. If I create one, can you test it for me?

      • Ronald Knaap says:

        Sure, I’ll be glad to 🙂

      • MicaH says:

        I think I have it figured out. I’ve added a Change Forest button to the Change Domain GUI. Please re-download the script and test if it works for your environment.

        Alvast bedankt, Ronald. 😉

      • Ronald says:

        Works like a charm! 🙂

        Graag gedaan Michaja 😉

  14. JyrkiH says:

    This is super!! Is it possible to receive a copy of the .psf files? It helps me a lot.

  15. Robert says:

    Hi MicaH,

    Awesome function!

    Is it possible to output a list of computers from the selected OU?

    $computers = Choose-ADOrganizationalUnit
    $computers would then result in a list of computers based on the OU I selected

    • MicaH says:

      Sure it’s possible, but why make that part of the function? The function is designed for picking an OU. You can use that output in a seperate command or function to get the computer accounts. If you’re on a system with RSAT or a domain controller you can simply do this:

      $OU = Choose-ADOrganizationalUnit
      $Computers = Get-ADComputer -Filter * -SearchBase $OU.DistinguishedName

      If not it’s a bit more challenging: you’d have to use ADSIsearcher and use the OU’s distinguished name in the SearchRoot property.

      • Robert says:

        Thank you 🙂

        This is what I ended up with:
        First, store the result of your function in $OUDN
        then use ADSISearcher and fill in the DestinguishedName from your function.

        $OUDN = Choose-ADOrganizationalUnit
        function Get-ADComputer
        	$strFilter = "(objectCategory=computer)"
        	$objDomain = New-Object System.DirectoryServices.DirectoryEntry('LDAP://' + $OUDN.DistinguishedName)
        	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
        	$objSearcher.SearchRoot = $objDomain
        	$objSearcher.PageSize = 1000
        	$objSearcher.Filter = $strFilter
        	$objSearcher.SearchScope = "Subtree"
        	$colProplist = "name"
        	foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) }
        	$colResults = $objSearcher.FindAll()
        	foreach ($objResult in $colResults)
        	{ $objItem = $objResult.Properties; $ }
  16. Sergey says:

    Hi. I can’t download the script. URL “” is not available. Can You reload the script? Thanks

  17. Thomas Novak says:

    Hi Micah,

    great script, I have a small performance issue though. We have a very large number of OU’s in our domains and when I run the script it takes a fairly large amount of time just to list the root objects (~15-30s). Any advice on how to speed up the initial load? Besides that, runs really smooth 🙂


    • MicaH says:

      Wow. How many root objects do you have? And how much time does it take ADUC to show these? I’m not sure it’s possible to further optimize the initial load. I’ll give it some thought and get back to you.

      • Thomas says:

        We have approximately 200 root objects for each domain which is not that big of a number and some of the domains and their controllers are based in countries half across the world but nonetheless ADUC loads them pretty swiftly in about 3 seconds. The number of objects in some of those root objects can be huge though. To be honest, I didn’t take a closer look at the script code but I was automatically assuming you are somehow enumerating through content of those root objects, which would explain the longer load time. Also when I expand any bigger root object it still takes a couple of seconds to open it.

        Thanks for your time

      • MicaH says:

        Hi Thomas. Please re-download the script and test it. I think you’ll find the performance is boosted considerably. Let me know what you think.

  18. Thomas says:

    Hi Micah,

    Yes I think the performance is a little better now although not quite dramatic (~11sec load time) but I have to test it further. I can see that you have added a line


    to load just the two vital properties, which makes a lot of sense 🙂 I will test it for some time and drop an update in the future.

    Thanks for your effort!


  19. Mick says:

    Micah Hi,

    Have just found you PowerShell script, excellent piece of work, and have been playing with it on a test site. I’m having a problem with the creation of OU’s. I run the script, create an OU with sub OU’s and that works fine, if I then close the script, re-run, and try to create another sub OU in the Root OU I created before I get no context menu when right clicking. That also happens to any OU’s that were created within ADUC. Hope that all makes sense.

    Thanks for your time.

  20. Mick says:

    Micah Hi,

    Me again, have manged to work out whats going with the inability to add a sub OU to a root OU but not how to fix i, have a work around but not a satisfactory one.
    Have had a look at the script and when the script is run during the “function add-node” it assigns an image number to each of the object types, so OU would be assigned imgae id 3, its not doing that, all the objects appear with what I think is the default image id 0.
    When you create a new OU it shows the correct image and everything works, when you relaod the script all the objects show the default image and you loose the ability to add sub OU’s to existing OU’s.
    In the ADUC the OU’s show the correct image. Have tried the script on 3 servers, 2008 R2, 2012 R2 and 2016 they all show the same problem, all theses servers are Hyper-V VM’s. When testing the script it was not changed in anyway.

    Many thanks


  21. Busy Orbit says:

    Hi Micah,
    You are lifesaver!!!
    I am new to Powers hell, and i have exactly same requirement to pickup OU from domains.
    i have currently i have script which create the New user for me now i dont want to manually specify the OU where these new account to be create rather i want to use your script to Pickup the OU from the POPUP and pass this to my create AD user script, the problem is that i have downloaded your script in Ps1 format but when i run Choose-ADOrganizationalUnit.ps1 inside the power shell nothing comes, can you tell me how to use this script inside my script.
    thanks in advance, eagerly waiting your reply.

    • MicaH says:

      The .ps1 file only contains a function. Therefor, in order to use it you can use either of two methods:

      * dotsource the script, then call the function.
      * copy the function to your script and call it from there.

      I’d recommend option two. That way your script won’t be dependent on an external script.

  22. Busy Orbit says:

    Thanks for the quick reply MicaH really appreciate that,
    Here is my current script like how i am taking OU information from inputfile in Variable and pass it to Powershell command, but now i want to run your script while will open as POPUP and desired OU selection will be saved in Variable some thing i have mentioned in below script ,

    please see the OU variable and passing it to in Script.

    Import-Module ActiveDirectory
    # Inputfile
    $inputfile = Import-Csv -Path “D:\Userinputfile.csv”
    foreach ($User in $inputfile)
    $Displayname = $User.Display_Name
    $UserFirstname = $User.First_Name
    $UserLastname = $User.Last_name
    $Company = $user.Company
    $Department = $user.Department
    $UPN = $User.UPN
    $Job_Title = $User.Job_Title
    $Office = $
    $mobile = $user.Mobile_Phone_Number
    $telephone = $user.Telephone_Number
    $SAM = $User.Samaccount
    $OU = $User.OU =====================(currently i am taking OU info from Inputfile)
    $ou = “C:\Users\admin\Desktop\Choose-ADOrganizationalUnit.ps1” ===== ( I want to pickup desired OU from your Script.. …….Like this)
    $Description = $User.Description
    $Password = $User.Password
    $Domain = ‘Domain.lcoal’

    New-ADUser -Name $Displayname -DisplayName $Displayname -SamAccountName $SAM
    -UserPrincipalName $UPN -GivenName $UserFirstname -Surname $UserLastname
    -Description $Description -Department $Department -MobilePhone $mobile -OfficePhone $telephone -Company $Company -Title $Job_Title -Office $Office -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force)
    -Enabled $true -Path $OU -ChangePasswordAtLogon $false -server $Domain
    Write-host “AD Account $Displayname is created”

    so I am doing any ting wrong or can you tell me how can i get this done,
    I really appropriate your help here, again eager to waiting your reply…

  23. James says:

    I think this modification here is my issue “The output has been changed from a single string containing the distinguished name of the chosen OU to a PowerShell object that contains both the name and the distinguished name.”
    Getting a bad syntax when attempting to pump in -Path $OU into AddAD-User command
    I threw a write-host $OU in there too see exactly what was being added to $OU variable
    Any idea how can I specify my new-aduser command to ignore the name and only use DN?
    new-aduser -Path $OU -givenname $firstname -surname $lastname -name $fullname -Displayname $fullname -samaccountname $FLID -company COmpany -Userprincipalname $ -AccountPassword (ConvertTo-SecureString “Passwrd” -AsPlainText -Force) -enabled $true -title $JobTitle -Office $Location -EmailAddress $Email -OtherAttributes @{‘admindescription’=$FLID;’extensionAttribute1’=$SAPID}

    • MicaH says:

      New-ADuser -Path $OU.distinguishedname ….

      • James says:

        HOLY CRAP! thanks for the fast reply bud, it worked, were in business!

        Since I got you here… Is there any way that we can filter the picker down to only certain OUs? ie: only show our domain User OU and its various sub-OUs instead of all OUs in the domain?

  24. Mick says:

    James Hi,

    I have done what your asking. I have edited the script quite a bit to remove the Change Forest/Domain. To set it to a specific root OU I bitted the Build-Treeview to have that effect. So far it has not caused me any problems.

    function Build-Treeview
    $treeNodes = $Treeview.Nodes[0]

    $CurrentDomain = $Forest.Domains | ?{$_.Name -eq $env:USERDNSDOMAIN}
    $Domain = $CurrentDomain.GetDirectoryEntry()
    $RootDomainNode = Add-Node -dname $Domain.distinguishedName -name $CurrentDomain.Name -RootNode $treeNodes -Type Domain

    # To set a specific OU
    $RootDomainNode.Name = “OU=CompanyName,$DN”

    #Copy the RootDomainNode to parent scope
    New-Variable -Name RootDomainNode -Value $RootDomainNode -Scope 1




  25. Craig Wright says:

    Hi MicaH,
    Is there any way to get this to also pick AD objects (users, computers, security groups, etc), instead of just OUs?

    • MicaH says:

      It’s definitely possible, Craig! If you want you could build a replica of the ADUC MMC console in PowerShell using the same techniques I’m using for the OU’s. I have no use case for this at the moment and I’m sadly too busy to create this in my spare time, but if you get around to creating this yourself i’d sure like to see it.

  26. Ed Middlebrooks says:

    Hi, I’m trying to write a script that my other IT techs can use to set properties on a linked mailbox in Exchange. Since we sometimes get multiple values returned by using the display name…

    This command:Add-ADPermission -Identity Sourcing -User domain\username -ExtendedRights “Send As”
    Creates this error: There are multiple objects matching the identity “Sourcing”. Please specify a unique value.

    I am trying to use this script to select AD objects, but am getting a bunch of errors. No I haven’t modified it. I also need to return the DN of the acutal object in the OU, not the OU itself. The only other option would be have my other IT guys use ADSI edit, find the mailbox, and find the value and copy the DN, which would be akin to trying to make a horse drink the river water.

    Errors I’m getting when running MicaH’s script:

    .Net Framework: Unhandled exception has occurred in a component in your application. “Cannot index into a null array.”

  27. Jacob Turowski says:

    When I was running this, every object was showing as a generic container (and I wasn’t able to use the “New OU” functionality). I was able to locate the issue – the switch statement that looks at the type of each object/container passed to it was looking at the objectclass – but this was always null.

    Was able to locate the section that grabs the objects from AD. I added objectclass to the list of properties that the adsearcher is looking for on line 775 and things seem to be working correctly now. Wanted to pass along the slight change.

  28. Tom says:

    Hi MicaH,
    first of all i have to say it’s a really useful script you build here. But I have some problems with change domain. First of all it takes about 30-60 seconds with “Not Responding” to build the change domain window. Than sometimes it is stuck complete ( have to kill process) and some times I get the following error on domain change.

    Exception calling “GetDirectoryEntry” with “0” argument(s): “Unknown error (0x80005000)”
    $node = Add-Node -RootNode $RootNode -dname $_.GetDirecto …
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ActiveDirectoryOperationException

    The change seems to work.
    I am sorry but I don’t get it why it takes so long to build the domain list and why that error occures. Perhaps you have an idea here or you can have a look on it.
    Thanks in advance.

  29. Jordan says:

    You are the MAN!!! Thank you so much for this!

  30. Random says:

    is there an easy way to perform a foreach loop in the multi select results?
    when testing what i have poerformed with other PS for gui interactions with AD, usually you could embed the GUI within a variable – embed the variable within the foreach – and then foreach result run the desired commands – for instance.

    $OU = Choose-ADOrganizationalUnit -MultiSelect
    $distinguishedName = $OU.distinguishedName

    foreach ($SelectedOU in $OU){
    write-host $SelectedOU -forgroundcolour green #user notification
    – perform some more tasks based on the OU currently in the foreach baseline

    however, whenever tested – even the write-host result bundles all multiselected OU’s together in a single line repeated multi times based on total count of selections… so its writing the correct number of “lines” but each line has all ous in a single string.
    so the overall goal isnt going to work – just wondering if you had any advice, or recording thoughts?


    • MicaH says:

      That’s strange. It works as expected for me. If you use Write-Host like that (which you really shouldn’t) it will return a visual representation of a hastable:

      @{Name=Test; DistinguishedName=OU=OU=Test,DC=DOMAIN,DC=LOCAL}

      This is because Write-Host is converting the output (which is an array of PSCustomObjects) to strings.

      Try it like this:

      foreach ($SelectedOU in $OU){
      write-host $SelectedOU.distinguishedName -forgroundcolour green #user notification
      – perform some more tasks based on the OU currently in the foreach baseline
      • Random says:

        I must say, given its been about 3-4 months since ive been coding in powershell, i had a couple of things in the incorrect order haha.
        You resolved my issue 🙂
        I most certainly appreciate your assistance MicaH. I really do.


      • MicaH says:

        You’re welcome 🙂

  31. Random says:

    Hi MicaH, I was actually hoping to ask a new question, is there any way to alter the AD interactive GUI to perform recursive actions?
    As at the moment, for multiple GUI actions in sub OU’s the subs must be selected also, but if I wanted to perform the code on all sub OU’s, is there an alteration or minor line etc that would allow the auto recursive selection to be applied at all?

    Your advice is always appreciated!

    With kind regards,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s