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! ;^)

Conclusion

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!

Edit:

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.

Advertisements

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.

47 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 (http://brechtgijbels.blogspot.be/2015/01/powershell-net-ad-ou-picker.html). 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:

        $formChooseOU.BringToFront()

        The entire scriptblock should look like this:

        $formChooseOU_Shown={
        	#Build treeview when form is shown
        	$formChooseOU.BringToFront()
        	$formChooseOU.Cursor = 'WaitCursor'
        	try
        	{
        		Build-TreeView
        	}
        	catch
        	{
        		Show-Error ($_ | Out-String)
        	}
        	finally
        	{
        		$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:

        $formChooseOU.BringToFront()

        The entire scriptblock should look like this:

        $formChooseOU_Shown={
        	#Build treeview when form is shown
        	$formChooseOU.BringToFront()
        	$formChooseOU.Cursor = 'WaitCursor'
        	try
        	{
        		Build-TreeView
        	}
        	catch
        	{
        		Show-Error ($_ | Out-String)
        	}
        	finally
        	{
        		$formChooseOU.Cursor = 'Default'
        	}
        }

        Please let me know if this does the trick.

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

  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
        [switch]
        $MultiSelect,

        [string]
        $OURoot
        ………….
        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
        }
        else
        {
        $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

        $treeNodes.Expand()
        $RootDomainNode.Expand()
        }

  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.

    https://www.facebook.com/groups/5901799452/

  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?

    Example:
    $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; $objItem.name }
        	}
        

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s