5

I'm writing a Powershell script to populate all the company users to a Active Directory from a CSV file.

The script uses the Powershell command New-ADUser and it should know, for every user, where is the path to add them, for example:

"OU=IT Dept,OU=Users,DC=local,DC=contoso"

The problem is: this Active Directory doesn't have any users yet, so there's no Organizational Units created, and whenever I run the script, if the path doesn't exists, the user is not created.

I've looked up for a command to create Organizational Units, like New-ADOrganizationalUnit or dsadd, however they do not support recursive creations, like

"OU=Helpdesk,OU=IT Dept,OU=Users,DC=local,DC=contoso"    

if

"OU=IT Dept,OU=Users,DC=local,DC=contoso"    

does not exist yet.

Is there any way of doing that using New-ADOrganizationalUnit?

Thanks

thiagowfx
  • 182
  • 1
  • 4
Maurício Mota
  • 199
  • 2
  • 9
  • 1
    You could just have it check at each level and create it if it doesn't exist right? Instead of trying to create the whole OU path in one command. – Mike Aug 27 '14 at 01:20
  • 1
    Sure, I could do that, in fact that was my plan B, but if there's a "native" way to do that, my script will be simpler. – Maurício Mota Aug 27 '14 at 01:44

3 Answers3

8

As mentioned in the comments by Mike, you'll need to step through each level in the tree and test for the existence of the OUs ancestors, and then create the ones that doesn't exist, from the top-most level and down.

As long as New-ADOrganisationalUnit does not have a -Recurse parameter, this is the most elegant way I could think of:

function New-OrganizationalUnitFromDN
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [string]$DN
    )

    # A regex to split the DN, taking escaped commas into account
    $DNRegex = '(?<![\\]),'

    # Array to hold each component
    [String[]]$MissingOUs = @()

    # We'll need to traverse the path, level by level, let's figure out the number of possible levels 
    $Depth = ($DN -split $DNRegex).Count

    # Step through each possible parent OU
    for($i = 1;$i -le $Depth;$i++)
    {
        $NextOU = ($DN -split $DNRegex,$i)[-1]
        if($NextOU.IndexOf("OU=") -ne 0 -or [ADSI]::Exists("LDAP://$NextOU"))
        {
            break
        }
        else
        {
            # OU does not exist, remember this for later
            $MissingOUs += $NextOU
        }
    }

    # Reverse the order of missing OUs, we want to create the top-most needed level first
    [array]::Reverse($MissingOUs)

    # Prepare common parameters to be passed to New-ADOrganizationalUnit
    $PSBoundParameters.Remove('DN')

    # Now create the missing part of the tree, including the desired OU
    foreach($OU in $MissingOUs)
    {
        $newOUName = (($OU -split $DNRegex,2)[0] -split "=")[1]
        $newOUPath = ($OU -split $DNRegex,2)[1]

        New-ADOrganizationalUnit -Name $newOUName -Path $newOUPath @PSBoundParameters
    }
}

Usage using WHATIF

# The desired resulting OU DN  
$DN = "OU=Helpdesk,OU=IT Dept,OU=Users,DC=local,DC=contoso"
New-OrganizationalUnitFromDN $DN -WhatIf

Without WHATIF

# The desired resulting OU DN  
$DN = "OU=Helpdesk,OU=IT Dept,OU=Users,DC=local,DC=contoso"
New-OrganizationalUnitFromDN $DN

It will break if there are non-existing non-OU containers in the path.

Mathias R. Jessen
  • 25,161
  • 4
  • 63
  • 95
  • I'm trying to implement your solution to my script and I'm having some issues. Does your solution should be working for itself? I can't see any attribution for the $OU variable, am I missing something? Thanks in Advance – Maurício Mota Aug 29 '14 at 19:18
  • Attribution? I'm not sure I understand. The `$OU` variable in the last block is assigned from `foreach($OU in $MissingOUs)`. Since we reversed the order of the `$MissingOUs` array already, It will have these values assigned (in order) 1) "OU=Users,DC=local,DC=contoso", 2) "OU=IT Dept,OU=Users,DC=local,DC=contoso" and finally 3) "OU=HelpDesk,OU=IT Dept,OU=Users,DC=local,DC=contoso". Try to substitute `Write-Host` for New-ADOrganizationalUnit to see it – Mathias R. Jessen Aug 29 '14 at 19:36
  • I'm not sure if i'm doing terribly wrong, but copy and pasting your code and changing only the `New-ADOrganizationalUnit` to `Write-Host` not works for me. I'm new to powershell so i'm having some trouble to find the problem. it seems the problem is in the `if($NextOU.IndexOf("OU=") -ne 0 -or [ADSI]::Exists("LDAP://$NextOU/"))` line. – Maurício Mota Aug 29 '14 at 20:09
  • @MaurícioMota Join me in [this chat room](http://chat.stackexchange.com/rooms/16813/discussion-between-mathias-r-jessen-and-mauricio-mota) and I'll try to help you out – Mathias R. Jessen Aug 30 '14 at 12:48
1

The output of $NextOU.IndexOf("OU=") is case sensitive and will return -1 for all lowercase ou= try: $NextOU.IndexOf("OU=",[StringComparison]"CurrentCultureIgnoreCase")

kasperd
  • 30,455
  • 17
  • 76
  • 124
0

Try this approach; it is much simpler: https://dimitri.janczak.net/2016/04/20/recursive-ou-creation-with-powershell/

  • 1
    your answer is unclear and lack of the description, please add some description about link provided in answer, also please refer [how-to-answer](https://stackoverflow.com/help/how-to-answer) to write a good answer. – Vaibhav Panmand Sep 19 '21 at 09:13