Get CN value from ADUser DistinguishedName

16.1k Views Asked by At

I have a PS script that checks some custom user's properties in Active Directory. One of the properties is "Manager".

 $data = Get-ADUser $user -Properties * |  Select-Object DisplayName, LockedOut, Enabled, LastLogonDate, PasswordExpired, EmailAddress, Company, Title, Manager, Office

 Write-Host "9." $user "manager is" $data.manager -ForegroundColor Green

When I run the script I've got:

User's manager is CN=cool.boss,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC=com

The problem is that text "OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC=com" will be different for some users

How can I modify output and remove everything except "cool.boss"? Thank you in advance

4

There are 4 best solutions below

3
On BEST ANSWER

This should be a more or less safe and still easy way to parse it:

($data.manager -split "," | ConvertFrom-StringData).CN
0
On

To complement the helpful answers here with PowerShell-idiomatic regex solutions:

$dn = 'CN=cool.boss,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC=com'

($dn -split '(?:^|,)CN=|,')[1] # -> 'cool.boss'
$dn = 'CN=cool.boss,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC=com'

$dn -replace '(?:^|,)CN=([^,]+).*', '$1' # -> 'cool.boss'

Note:

  • The above solutions do not rely on a specific order of the name-value pairs (RDNs) in the input (that is, a CN entry needn't be the first one), but they do extract only the first CN entry's value, should multiple ones be present, and they do assume that (at least) one is present.

  • In principle, DNs (Distinguished Names), of which the input string is an example, can have , characters embedded in the values of the name-value pairs that make up a DN, escaped as \, (or, in hex notation, \2C); e.g., "CN=boss\, cool,OU=Users,..."

  • A truly robust solution would have to take that into account, and would ideally also unescape the resulting value; none of the existing answers do that as of this writing; see below.


Robustly parsing an LDAP/AD DN (Distinguished Name):

The following Split-DN function:

  • handles escaped, embedded , chars., as well as other escape sequences, correctly
  • unescapes the values, which includes not just removing syntactic \, but also converting escape sequences in the form \<hh>, where hh is a two-digit hex. number representing a character's code point, to the actual character they represent (e.g, \3C, is converted to a < character).
  • outputs an ordered hashtable whose keys are the name components (e.g., CN, OU), with the values for names that occur multiple times - such as OU - represented as an array.

Example call:

PS> Split-DN 'CN=I \3C3 Huckabees\, I do,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC=com'

Name                           Value
----                           -----
CN                             I <3 Huckabees, I do
OU                             {Users, SO, PL, RET…}
DC                             {domain, com}

Note how escape sequence \3C was converted to <, the character it represents, and how \, was recognized as an , embedded in the CN value.

Since the input string contained multiple OU and DC name-value pairs (so-called RDNs, relative distinguished names), their corresponding hashtable entries became arrays of values (signified in the truncated-for-display-only output with { ... }, with , separating the elements).


Function Split-DN's source code:

Note: For brevity, error handling and validation are omitted.

function Split-DN {

  param(
    [Parameter(Mandatory)]
    [string] $DN
  )

  # Initialize the (ordered) output hashtable.
  $oht = [ordered] @{}

  # Split into name-value pairs, while correctly recognizing escaped, embedded
  # commas.
  $nameValuePairs = $DN -split '(?<=(?:^|[^\\])(?:\\\\)*),'

  $nameValuePairs.ForEach({

    # Split into name and value.
    # Note: Names aren't permitted to contain escaped chars.
    $name, $value = ($_ -split '=', 2).Trim()

    # Unescape the value, if necessary.
    if ($value -and $value.Contains('\')) {
      $value = [regex]::Replace($value, '(?i)\\(?:[0-9a-f]){2}|\\.', {
        $char = $args[0].ToString().Substring(1)
        if ($char.Length -eq 1) { # A \<literal-char> sequence.
          $char # Output the character itself, without the preceding "\"
        }
        else { # A \<hh> escape sequence, conver the hex. code point to a char.
          [char] [uint16]::Parse($char, 'AllowHexSpecifier') 
        }
      })
    }
    
    # Add an entry to the output hashtable. If one already exists for the name,
    # convert the existing value to an array, if necessary, and append the new value.
    if ($existingEntry = $oht[$name]) {
      $oht[$name] = ([array] $existingEntry) + $value
    }
    else {
      $oht[$name] = $value
    }

  })

  # Output the hashtable.
  $oht
}
0
On

you could use regex for that:

$s = "CN=cool.boss,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC =com"

$pattern = [regex]"CN=.*?OU"
$r = $pattern.Replace($s, "CN=OU")
$r
0
On

You can use the .split() method to get what you want.

$DN = "CN=cool.boss,OU=Users,OU=SO,OU=PL,OU=RET,OU=HBG,DC=domain,DC =com"
$DN.Split(',').Split('=')[1]

What i'd recommend, is throwing it into another Get-ADUser to get the displayname for neater output(: