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 feature set.

14-06-2018: I’ve had some requests to add a parameter for picking a root OU. I’ve updated the script so if that’s useful to you re-download it and give it a try.

About MicaH

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

118 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 }
        	}
        
  16. Sergey says:

    Hi. I can’t download the script. URL “raw.githubusercontent.com” 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 🙂

    Thanks

    • 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

    $ADSearcher.PropertiesToLoad.AddRange(@(‘name’,’distinguishedname’))

    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!

    Thomas

  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

    Mick

  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 = $User.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?
    http://i.imgur.com/fW2y3uQ.png
    new-aduser -Path $OU -givenname $firstname -surname $lastname -name $fullname -Displayname $fullname -samaccountname $FLID -company COmpany -Userprincipalname $FLID@domain.com -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

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

    Cheers

    Mick

  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?
    Thanks,
    Craig

    • 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.
    Regards,
    Tom

  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?

    Regards,
    Random.

    • 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.

        Regards,
        Random.

      • 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,
    Random.

  32. thetetchytechie says:

    Hi MicaH,
    First off, thanks for posting this, it’s a great bit of work and will be hugely useful for us. I just wanted to flag a slight gotcha in case anyone else goes through the pain I just did. My use case for the OU Picker is to use it as part of an OS Deployment task in System Centre Configuration Manager (to force my techies to pick the OU for the computer account during deployment rather than hoping they’ll remember to move it afterwards). So this means I’ve been trying to run it during the WinPE phase of a deployment sequence. Once I’d sorted out a few little DNS quirks particular to our environment, the picker would always fail with a fatal error calling FindAll(). After several hours of testing, debugging and more or less typing the script in one command at a time, I found that WinPE doesn’t have the DLLs necessary for some ADSI functionality (although weirdly it could do all the ADSearcher / System.DirectoryServices bits).

    There’s no official MS-supported way to add the necessary ADSI functionality to a WinPE image, but I found this page – https://deploymentresearch.com/Research/Post/508/Adding-ADSI-Support-for-WinPE-10 – where a kindly soul has listed the necessary DLLs and written an inf file to allow them to be injected as a driver into WinPE either via DISM or as an SCCM driver package. I’ve added them to our WinPE image, and the picker now works beautifully. Hope that might save someone a bit of time and pain!

    • MicaH says:

      Thank you for the useful feedback..

    • TheITGuyOfOz says:

      Anyway you could provide the steps you took and how you configured the execution step withing your task sequence to achieve this? I just came across this tool and I am looking to do the exact same thing!

      Thanks!

      • thetetchytechie says:

        Will try – it was a bit of a ‘hack things till it worked’ kind of effort, but as far as I remember what worked was:
        1) Visit the link I posted and download the ADSI plugin – extract ADSIx64.inf or ADSIx86.inf from the depending on whether you’re using 32 or 64 bit PE in your SCCM boot image.
        2) The readme in the plugin lists the 6 DLLs you need – dig them out of a working Win10 installation (I made sure to use a Win 10 version which matched the PE version of my SCCM boot image, but that may not have been necessary)
        3) Put the inf and the DLLs into a folder on your SCCM server and add a new driver to your SCCM driver library using that folder as the source.
        4) Open the properties page of your SCCM boot image, go to the driver tab and add your newly created ADSI driver to the image. Once the image has updated and been re-distributed to your distribution points, it should be able to handle all the ADSI stuff in MicaH’s script.
        5) My execution step calling the OU Picker is a ‘Run Command Line’ type of step, with the command set to be:
        M:\MasterUI\ServiceUIx64.exe %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File M:\MasterUI\OUSelector_OSD.ps1

        – M:\MasterUI\ is a mapped drive to a shared folder on our SCCM server, which I map using the built in ‘map network drive’ TS step earlier in the sequence. You could do it using SCCM packages instead, I just find using a mapped drive is quicker for development – no faffing with updating/redistributing packages each time you make a change.
        – If you haven’t come across it, ServiceUI is part of the MDT. You need it if you want to interact with the user during a task sequence, otherwise any window you try and display won’t actually appear (technically I think it does appear, just not on the desktop you can actually see on the screen)
        – OUSelector_OSD.ps1 is my slightly modified version of MicaH’s script. You’ll want to modify the script – somewhere before the form is actually displayed add this:

        $TSProgressUI = New-Object -COMObject Microsoft.SMS.TSProgressUI
        $TSProgressUI.CloseProgressDialog()

        This gets rid of the task sequence progress box, otherwise it sits in front of the OU Picker being annoying and getting in the way
        .
        The rest of my changes were to hardcode our AD domain info into the script – we have a very weird semi-delegated DNS environment, and the lookup on our AD domain was failing, but I’m pretty sure that’s just our strange DNS – apart from the above change I’d expect MicaH’s script to work as-is in a task sequence.

        Hope that helps – good luck!

  33. Scott Harris says:

    Excellent work, It is exactly what i needed.

    I did have one issue though, One of our OU’s is has over 3000 directories in it, and it takes over 2 minutes to load. Is it possible to start the tree one level above it… AKA pick a start point thats not the normal root? A sub OU?

  34. Tyler Bench says:

    Thanks, works great!

  35. Jon says:

    I run this and nothing happens. I dont get it

  36. Pingback: PowerShell Tool – Backup Group Policy Objects (GPO) linked to Oranizational Units (OU) – benecke.cloud

  37. Jaybee says:

    Hi MicaH, first let me say thanks so much for the work you’ve done. This script is awesome.

    I am trying to leverage one set of credentials to both use your function on the local AD and do the domain join/PC rename and have used get-credentials to grab a user with rights details and make a $creds variable to use twice but am having trouble using this in your function. Your parameters for -credentials only seem to accept the username and not the whole get-credential object with password as well:
    eg
    Choose-ADOrganizationalUnit -Domain $MyDomainName -Credential $creds.Username

    So the above works but when I add the entire get-credentials output it fails. Any suggestions as I’m a beginner with powershell. Tried “-Credential Username,Password” in here but not having any luck. Not a biggee but having the password prompt twice annoys me and may confuse others thinking that its failed. Alternatively can I reuse the password variable within the function to use further down in my script to the domain join too.

  38. chris says:

    Hi, How can i change forest without using the GUI. i would like to use this script to help with migrating machines from one forest to another

  39. Nigel says:

    Hi, I have started using this in a large environment, as part of a domain migration. We have multiple forests with multiple domains, and I was hoping to be able to use it to pick OUs in different forests, as required by the user’s input. However, although I can change the Forest once the GUI has opened, if I tell it to start in a domain in a different forest to the one I am in, I get an error “No domain found with FQDN (the domain I specify).” Is it not possible to automatically get the Forest of the domain I use in the “-Domain” argument, or even have a separate argument to specify the Forest FQDN to use? Other than that, a really useful script and the speed boost from the first version is amazing. Thank you so much.

  40. Pingback: Changing AD UPN in bulk using Powershell GUI – 365 by Thijs

  41. Jason says:

    Happy New Year. Great work on the script and continually providing comments for people with questions. I’m attempting to run this script in a WinPE setting. After doing an import-module and running the function, then inputting the domain and proper credentials, I am getting the following error:

    “you cannot call a method on a null-valued expression (line 256, char 7)”

    currently trying to work through the script, but not advanced enough in powershell to know how your functions work and why this line is failing:

    $Domain = $CurrentDomain.GetDirectoryEntry()

    Your script works perfectly on a domain joined device.

    • Jason says:

      running the -Domain switch seems to have fixed the domain join problems, however I am left with a bunch of .net errors. You can ignore my previous post and I’ll provide an update for others if they are attempting a similar process.

      • jason says:

        I wasn’t able to get the script working in WinPE (sccm) even after the .net packages. if anyone else has a solution let me know.

      • Ben says:

        I’m a little late to the game, but I am also having this exact same problem in WinPE, but this script works perfectly otherwise. This would be a huge timesaver and prevent errors instead of typing the distinguished name of the OU in MDT.

  42. AJAL says:

    Hey MicaH. Thank you for this nice Script, very useful.
    The is a bug, regarding “AdvancedFeatures” View.
    You check the ‘showinadvancedviewonly’ property for some objects, to show or hide them.
    The bug: the will be shown always!

    Fix:
    Please add the property ‘showinadvancedviewonly’ in your ADSISearcher PropertiesToLoad range, like this:
    $ADSearcher.PropertiesToLoad.AddRange(@(‘name’,’distinguishedname’,’objectClass’,’showinadvancedviewonly’))

    …otherwise, you will not be able to check for this property here:
    $ADObjects = $ADsearcher.FindAll() | ?{$_.Properties[‘showinadvancedviewonly’][0] -eq $false -or
    $_.Properties[‘showinadvancedviewonly’][0] -eq $null}

    Cheers
    Ajdin

  43. Randy says:

    I love your script, and I just wanted to say that I found a really weird, obscure bug.

    Upon first displaying the UI, if I click the [-] to collapse “Active Directory Hierarchy” and then quickly press the [+] to re-expand it, the window closes and returns nothing. No result, no error, nothing. And these clicks have to be fairly quick, maybe double-click speed. If I collapse it, wait a few seconds and then expand it, it’s fine. If I click literally anywhere else first (that actually performs an action… highlight something, expand/collapse a different item) it’s fine – performing that same double-click works fine after that initial click. It seems to only happen if the very first click is a double-click of that top expand/collapse button

    I’m not sure if it’s even worth the time to track down what causes this and fix it, just figured I’d let you know in case you want to take that time (or maybe when you test in your environment it will be fine, who knows.)

  44. $user says:

    Hi MicaH

    is there any way to restrict the output so that you can’t view sub OUs?

    similar to the functionality of using something like.. -SearchScope OneLevel

    I’m using the -RootOU functionality to list all of the OUs withing it, and we want to restrict the choice to just those OUs .. is it possible?

  45. Jens Mertens says:

    Hi Micah,

    This is awesome and it works really well, and i can’t miss it anymore.
    There’s almost no more reason to open ADUC anymore 🙂
    I placed it in my $profile so i can use it all the time.

  46. Neil McIntyre says:

    This is amazing, i love it’s speed!

    however, when i attempt to use the OU selected in other new-remotemailbox command, i get the following:

    Cannot process argument transformation on parameter ‘OnPremisesOrganizationalUnit’. Cannot convert the “@{Name=Construction; DistinguishedName=OU=Construction,OU=Redink
    Homes,OU=Users,OU=Production,DC=xxxxxxxx,DC=local}” value of type “Deserialized.System.Management.Automation.PSCustomObject” to type
    “Microsoft.Exchange.Configuration.Tasks.OrganizationalUnitIdParameter”

    • MicaH says:

      Hi Neil. That’s because the output is not an OU object but a custom object. Try using the DistinguishedName property instead of the whole object. Like so:

      $OU = Choose-ADOrganizationalUnit
      New-RemoteMailbox -OnPremisesOrganizationalUnit $OU.DistinguishedName 
      
  47. Ben says:

    Hey MicaH, I am having the same .NET errors in Windows PE that Jason describes in his post from Jan 2, 2020. The error says “Exception calling “FindAll” with “0” argument(s): “Unknown error (0x80005000)”. There are lots of error details below that. I have added Powershell and .NET Framework features to the Windows PE boot image from within MDT, updated the deployment share, and imported that new boot image to our WDS server. Do you have any suggestions?

Leave a reply to Jacob Turowski Cancel reply