2

I am asking this question mainly to ask if there is a better way to do what I have working. I would also like to know if anyone sees any issues with getting this information this way.

I am trying to get the top level OU that a user is in, and any lower level OUs. The main problem is that we have multiple sites, some of which have multiple layers of OUs for user accounts (ou=doctors,ou=Users,ou=Site,dc=example,dc=com), and some sites that just have a single OU (ou=Users,ou=Site,dc=example,dc=com). I used the script below to get the DN path, split it, and rebuild it backwards with the last three pieces. Can anyone see any issues with doing it this way. Something about it just feels wrong....

$user = Get-ADUser CKnutson
$user.DistinguishedName
# Returns: CN=Cory Knutson,OU=IT,OU=Users,OU=Site,DC=example,DC=com

$split = $user.DistinguishedName.Split(',')
$path = "$($split[-3]),$($split[-2]),$($split[-1])"

Write-Host $path
# Returns: OU=Site,DC=example,DC=com

Just to state, the end goal was for me to get the path to the "Disabled" OU that we have just inside of each of the "Site" OUs. So my scripting could move the object when disabling the account to the proper place, in that site's top level OU (OU=Disabled,OU=Site,DC=example,DC=com).

2
  • Don't think I'd necessarily call anything wrong, but is the final goal to have it in the DN format? If not you could also call the canonical name and split that. I think those two attributes are both going to require some splitting and/or joining but are probably the only way to achieve the stated goal with any kind of ease.
    – Kyp
    Oct 25, 2017 at 2:21
  • The other thought that occurs is simply pattern matching your domain name with regex or with a straight up string match and splitting off only the stuff after -- ahem before -- that to put in another cell in your CSV.
    – Kyp
    Oct 25, 2017 at 2:24

3 Answers 3

2

Can anyone see any issues with doing it this way.

Yes, I see two immediate problems that might arise from your current approach.


1. Escaped commas

Consider an OU with a comma in its name, like: OU=Users\, Admin,DC=corp,DC=example

Your use of string.Split() won't care about the escape sequence and you end up with:

 Admin,DC=corp,DC=example

Use the -split regex operator with a lookbehind to make sure you ignore escaped commas:

$parts = $user.DistinguishedName -split '(?<!\\),'

2. Portability

Your code assumes that the NC part of the DN (eg. DC=example,DC=com), will always be just 2 labels wide. This means your code will fail if you use it in scripts you might want to reuse in other domains/environments.

I would grab each part, from right-to-left until I find one without the DC RDN prefix:

$topParts = foreach($part in $parts[-1..-$parts.Length]){
    $part
    if($part -notlike 'DC=*'){
        break
    }
}
# Remember to reverse the RDNs again
$path = $topParts[-1..-$topParts.Length] -join ','
2
  • But if I want the top level OU included, the if statement would be if($part -notlike 'DC=*'){$part;break}, correct? Then it would include the first non-DN piece, which would be the top level folder/OU. Oct 25, 2017 at 21:33
  • 1
    Look at the line just before if($part -notlike ... - when we break we've already emitted $part :-) Oct 26, 2017 at 5:17
1

In my opinion it is simpler to use Pathname COM object and simply ask for the parent of the DN. You can put this in a while loop to get the hierarchy of the object. Example using my ADName module:

$dn = Get-ADUser user | Select-Object -ExpandProperty DistinguishedName
$parent = $dn | Get-ADName -Format Parent
while ( $parent -like "OU=*" ) {
  $parent
  $parent = $parent | Get-ADName -Format Parent
}

Example output:

OU=Level 3,OU=Level 2,OU=Level 1,DC=fabrikam,DC=com
OU=Level 2,OU=Level 1,DC=fabrikam,DC=com
OU=Level 1,DC=fabrikam,DC=com
0

So you're after the DN of the path to the account? You can do that with a regular expression that uses the (?) ungreedy qualifier. There is no split, so escaped commas are not an issue.

"CN=Cory Knutson,ou=doctors,ou=Users,ou=Site,dc=example,dc=com" -match ".+?,(OU=.+)"
$matches[1]
ou=doctors,ou=Users,ou=Site,dc=example,dc=com

"CN=Knutson\, Cory,ou=doctors,ou=Users,ou=Site,dc=example,dc=com" -match ".+?,(OU=.+)"
$matches[1]
ou=doctors,ou=Users,ou=Site,dc=example,dc=com

"CN=Cory Knutson,ou=Site,dc=example,dc=com" -match ".+?,(OU=.+)"
$matches[1]
ou=Site,dc=example,dc=com

Edit
I was unclear from the phrase the top level OU that a user is in, and any lower level OUs. You can also do this:

$dn = "CN=Cory Knutson,ou=doctors,ou=Users,ou=Site,dc=example,dc=com"
$last = ($dn.toUpper()).lastIndexOf("OU=")
$dn.substring($last)
ou=Site,dc=example,dc=com
1
  • I wanted only the top level OU, so all three should return: ou=Site,dc=example,dc=com Oct 28, 2017 at 2:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .