private/CreateOrSetOU.ps1

function CreateOrSetOU {
    [CmdletBinding(DefaultParameterSetName="DistinguishedNameOnly",SupportsShouldProcess=$true)]
    Param
    (
        [Parameter(ParameterSetName="DistinguishedNameOnly", ValueFromPipelineByPropertyName)]
        [String]$DistinguishedName,

        [Parameter(ParameterSetName="NameAndDescription",Mandatory, ValueFromPipelineByPropertyName)]
        [String]$Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [String]$Description = "---Automatically created by createOrSetOU $((get-date).ToString("yyyy.MM.dd--HH.mm"))---",

        [Parameter(ParameterSetName="NameAndDescription", Mandatory, ValueFromPipelineByPropertyName)]
        [String]$Path,

        [Microsoft.ActiveDirectory.Management.ADDirectoryServer]$Server = (get-addomainController -Writable -Discover)
    )
    BEGIN {
        $shouldProcess = @{
            Confirm = [bool]($ConfirmPreference -eq "low")
            Whatif = [bool]($WhatIfPreference.IsPresent)
            verbose = [bool]($VerbosePreference -ne "SilentlyContinue")
        }
        $CreatedOUs = [System.Collections.Generic.List[String]]::new()
        $RootDSE = [adsi]"LDAP://RootDSE"
        $DefaultOU_SDDL = (get-adobject -server $server -filter { (ldapDisplayName -eq "organizationalUnit") } -searchBase ($rootDSE.SchemaNamingContext[0]) -properties defaultSecurityDescriptor).defaultSecurityDescriptor
        $defaultOU_ACL = [System.DirectoryServices.ActiveDirectorySecurity]::new()
        $defaultOU_ACL.SetSecurityDescriptorSddlForm($defaultOU_SDDL)
        write-loghandler -level "Verbose" -message "Initializing OU/Container Processing (DC: $server)"
    }
    PROCESS {
        $thisDN = if ($distinguishedName) {
            $DistinguishedName
        } else {
            "OU={0},{1}" -f $Name,$Path
        }
        $LocationTable = split-LDAPPath -distinguishedName $thisDN -ashashtable
        write-loghandler -level "Debug" -message "Started Processing '$thisDN''" -target $LocationTable.DistinguishedName
        $OUParams = @{
            ProtectedFromAccidentalDeletion = $false
            Description = $Description
        }

        # Check if parent path exists. If not, lets recursively create it.
        # We use `get-adobject` to account for non-ou containers (like ProgramData)
        # We use "Distinguishedname = $LocationTable.Parent" to avoid infinite recursion.
        trap {
            $output['Status'] ="Error"
            write-loghandler -level "warning" -message "Here there be dragons: $($_.exception.getType().fullname)"
            write-loghandler -level "error" -message ("Error updating OU {0} at {1} with Description {2}" -f $LocationTable.LeafName, $LocationTable.Parent, $Description) -target $_
            throw $_
        }

        $Output = [ordered]@{
            Status = "Pending"
            Name = $LocationTable.LeafName
            Path = $LocationTable.Parent
            Description = $Description
        }

        try {
            $foundOU = @(Get-ADOrganizationalUnit -identity $LocationTable.distinguishedName)
            if ($foundOU.count -ne 1) {
                throw "Didn't find exactly 1 OU"
            }
            $PSShouldProcessMsg = $(write-logHandler -passthru -target $LocationTable.DistinguishedName -message "Set Description, Protection, and ACLs")
            if ($PSCmdlet.ShouldProcess.Invoke($PSShouldProcessMsg)){
                Set-ADOrganizationalUnit -Identity $LocationTable.DistinguishedName @OUParams -replace @{ntSecurityDescriptor = $defaultOU_ACL} -confirm:$False | out-null
                $output['Status'] = "Updated"
            } else {
                $output['Status'] ="Set"
            }

        } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
            try {
                if ($PSCmdlet.ShouldProcess.Invoke($(write-logHandler -passthru -level "Debug" -target $LocationTable.DistinguishedName -message "Attempt to create New OU"))){
                    New-ADOrganizationalUnit -server $server -name $LocationTable.LeafName -path $LocationTable.Parent @OUParams -confirm:$false
                }
            } Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
                # This means a parent OU doesn't exist
                if ($LocationTable.Parent -ne $runtime.domain.dn -and $LocationTable.Parent -like "OU=*") {
                    try {
                        write-loghandler -level "warning" -message ("Parent '{0}' doesn't seem to exist; trying to create it...." -f $LocationTable.Parent) -target $LocationTable.Parent
                        CreateOrSetOU -DistinguishedName $LocationTable.Parent -server $server
                        New-ADOrganizationalUnit -server $server -name $LocationTable.LeafName -path $LocationTable.Parent @OUParams -confirm:$false
                    } catch {
                        throw $_
                    }
                } else {
                    write-loghandler -level "Warning" -message  ("Parent {0} doesn't exist and isn't an OU" -f $LocationTable.Parent)
                    throw $_
                }
            }
            $output['Status'] ="New"
            $CreatedOUs.add($LocationTable.DistinguishedName)
        } catch [Microsoft.ActiveDirectory.Management.ADException] {
            if ($_.exception.innerException -like "*already exists*") {
                # the OU already exists
            } else {
                write-loghandler -level "Debug" -message "Not sure what this error is, throw it." -target $LocationTable.DistinguishedName -indentLevel 3
                $output['Status'] ="Error"
                throw $_
            }
        } catch {
            write-loghandler -level "Debug" -message "Not sure what this is-- throw it to trap" -target $LocationTable.DistinguishedName -indentLevel 2
            throw $_
        }
        if ($WhatIfPreference.isPresent) {
            $output['Status'] ="{0}(Whatif)" -f $output['Status']
        }
        [pscustomobject]$Output


    } END {
        write-progress -Activity "CreateOrSetOU" -Completed
        for ($i = 0; $i -lt $CreatedOUs.count; $i++) {
        #foreach ($OU in $CreatedOUs) {
            $ProgressActivity = "Waiting for creation of new OUs (total: {0} " -f $CreatedOUs.count
            $OU = $CreatedOUs[$i]
            Write-Progress -Activity $ProgressActivity -PercentComplete (($i/$createdOUs.count) * 100)
            try {
                Get-ADOrganizationalUnit -server $server -identity $OU | out-null
            } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
                for ($j = 0; $j -lt $Settings.AppSettings.SleepTimeout; $j+=$Settings.AppSettings.SleepLength) {
                    $OUExists = [bool](Get-ADOrganizationalUnit -server $server -filter "distinguishedName -eq '$OU'")
                    if ($OUExists) {
                        break
                    }
                    $status = "Checking $OU (waiting for $j seconds)"
                    Write-Progress -Activity $ProgressActivity -Status $status -CurrentOperation $status -SecondsRemaining $($Settings.AppSettings.SleepLength - $j)
                    start-sleep -seconds $Settings.AppSettings.SleepLength
                }
                Write-Progress -Activity $ProgressActivity -completed
            } catch {
                write-warning $_.exception.getType().fullname
                throw $_
            }
            Write-Progress -Activity $ProgressActivity -completed
        }
    }
}