ScriptMethods

I’ve always liked extensible languages like Perl so I’m enjoying PowerShell’s type extensibility. I’ve added several script methods or properties written by other people to my standard setup. After writing about how I had added a function to my profile.ps1 file which would convert a Guid string to “native” format, it occurred to me that I could just add that as a method to the system.guid type definition. So I added this to my My.Types.Ps1xml file:

<Type>
  <Name>System.Guid</Name>
  <Members>
    <ScriptMethod>
      <Name>ToNativeString</Name>
      <Script>
        $local:NativeGuid = “”
        $this.ToByteArray() | foreach { $NativeGuid +=  $_.tostring(“x2”) }
        $NativeGuid
      </Script>
    </ScriptMethod>
  </Members>
</Type>

 Now I can use that method like this:

PS>$Guid = new-object system.guid (“4c5efbc3-a504-4cda-8cc7-1211ad502c60”)
PS>$Guid|gm

   TypeName: System.Guid

Name           MemberType   Definition
—-           ———-   ———-
CompareTo      Method       System.Int32 CompareTo(Object value), System.Int32 CompareTo(Guid value)
Equals         Method       System.Boolean Equals(Object o), System.Boolean Equals(Guid g)
GetHashCode    Method       System.Int32 GetHashCode()
GetType        Method       System.Type GetType()
ToByteArray    Method       System.Byte[] ToByteArray()
ToString       Method       System.String ToString(), System.String ToString(String format), System.String ToString(…
MSDN           ScriptMethod System.Object MSDN();
ToNativeString ScriptMethod System.Object ToNativeString();
PS>$Guid.ToNativeString()
c3fb5e4c04a5da4c8cc71211ad502c60

And my function becomes  this:

function get-NativeGuid {
  param ($GuidString)
  (new-object system.guid ($GuidString)).ToNativeString()
}

Advertisements

Wrappers

I’ve been using PowerShell to explore Active Directory. Since I do most of my exploring from the command line and I’m inherently lazy, I’ve been creating “wrappers” for some .Net 2.0 AD stuff to add to my profile.ps1 file. (“The fewer keystrokes, the better” is my motto.) I started with the DirectorySearcher by creating a function “new-ADSearcher”.

I was always using the same “preamble” of setting the CacheResults, SearchScope and PageSize so at least I could get rid of typing those all the time. (Yeah, I know that those values for CacheResults and SearchScope are the PS defaults, but I don’t like to have to depend on defaults. So call me OCD.)

The Filter and SearchRoot attributes of the DirectorySearcher were the main things I seemed to be using so they became the parameters for the function. But since the SearchRoot attribute actually required a DirectoryEntry object, that lead to a separate function named (predictably enough) “get-ADEntry”.

get-ADEntry originally only had a single optional parameter of LdapPath, which met the need for the new-ADSearcher function. But I was also frequently needing to access user objects based on their sAMAccountName attribute, as well as other AD objects based on their objectGuid attribute. So get-ADEntry grew a little with the addition of the samAccount and Guid parameters. And now I needed two additional functions, get-UserDN and get-NativeGuid.

get-UserDN was pretty straightforward. All I needed to do was use new-ADSearcher with the appropriate filter. (Did I mention that I was lazy?) Of course, after I wrote get-UserDN, I found myself searching for users by their SMTP address so I just added this to get-USerDN also. (And now I can go back and add that as an option to get-ADEntry also since it’ll just need to call get-UserDN.)

get-NativeGuid was needed because GUIDs are IMHO weird things. By definition a GUID is a 128-bit integer (16 bytes). The most common representation that you seem to see is in the form “e1c9f329-a5c2-46c0-b900-ee8e4e3ebbf6”. But to search for a GUID in an LDAP filter in PowerShell with DirectorySearcher object, you seem to need to use the form “29f3c9e1c2a5c046b900ee8e4e3ebbf6” which is the form that you’ll get from the get_NativeGuid() method of a DirectoryEntry object in PowerShell. (I say “seem to need” because I can’t find this documented anywhere.) Unfortunately, the system.guid class.tostring formatting options don’t seem to provide this conversion so this function was needed.

Disclaimer: Since I primarily work in a single domain, single forest environment, these functions might have issues in a multi-domain forest. And I haven’t spent any time adding error-checking or usability features. (Did I mention that I was lazy?)

function new-ADSearcher {
  param ($Filter=””,$Root=””)
  $local:Searcher = New-Object DirectoryServices.DirectorySearcher
  $Searcher.CacheResults = $true
  $Searcher.SearchScope = “Subtree”
  $Searcher.PageSize = 1000
  if ($Filter -ne “”) { $Searcher.Filter = $Filter }
  if ($Root -ne “”) { $Searcher.SearchRoot = get-ADEntry -ldappath $Root }
  $Searcher
}

function get-ADEntry {
  param ($LdapPath=””, $samAccount=””, $Guid=””, $SmtpAddress=””)
  if ($LdapPath -ne “”) {
    New-Object DirectoryServices.DirectoryEntry (“LDAP://” + $LdapPath)
  }
  elseif ($samAccount -ne “”) {
    New-Object DirectoryServices.DirectoryEntry (“LDAP://” + (get-userDN -samAccount $samAccount))
  }
  elseif ($SmtpAddress -ne “”) {
    New-Object DirectoryServices.DirectoryEntry (“LDAP://” + (get-userDN -SmtpAddress $SmtpAddress))
  }
  elseif ($Guid -ne “”) {
    New-Object DirectoryServices.DirectoryEntry (“LDAP://<GUID=” + (get-NativeGuid $Guid) + “>”)
  }
  else {
    New-Object DirectoryServices.DirectoryEntry
  }
}

function get-UserDN {
  param ($samAccount = “”, $SmtpAddress = “”)
  $local:Filter = “”
  if ($samAccount -ne “”) {
    $Filter = “(&(objectCategory=person)(objectClass=user)(samAccountName=” + $samAccount + “))”
  }
  elseif ($SmtpAddress -ne “”) {
    $Filter = “(&(objectCategory=person)(objectClass=user)(mailnickname=*)(proxyaddresses=smtp:” + $SmtpAddress + “))”
  }
  $local:Searcher = new-ADSearcher -filter $Filter
  $local:SearchResults = $Searcher.FindOne()
  $SearchResults.properties.distinguishedname
}

function get-NativeGuid {
  param ($GuidString)
  $local:Guid = new-object system.guid ($GuidString)
  $local:NativeGuid = “”
  $Guid.ToByteArray() | foreach { $NativeGuid +=  $_.tostring(“x2”) }
  $NativeGuid
}

Hello world!

Welcome to the Pathological Scripter.