Stan Di
Stan Di

Reputation: 83

Get CN value from ADUser DistinguishedName

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

Upvotes: 4

Views: 20475

Answers (4)

mklement0
mklement0

Reputation: 437833

To complement the helpful answers here with PowerShell-idiomatic regex solutions, which are not fully robust, however (see below):

$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:

  • 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 implementation needs 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 for a solution.


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, convert 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
}

Upvotes: 3

marsze
marsze

Reputation: 17055

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

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

Upvotes: 6

Abraham Zinala
Abraham Zinala

Reputation: 4694

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(:

Upvotes: 2

Frenchy
Frenchy

Reputation: 17007

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

Upvotes: 1

Related Questions