How can I create Organizational Units recursively on Powershell?

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


Solution 1:

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.

Solution 2:

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