2013/10/16

PowerShell - Get-DomainUser

Today one of my IT coworkers, in another department, sent a couple of emails to the Ops to get the username (SamAccount) from a couple of Active Directory users accounts. This guy, which is not familiar with AD, had only the DisplayName properties information.

I wrote him back that he could just request RSAT(Remote Server Administration Tools) to be installed on his workstation or just use this small PowerShell that I just wrote in minutes. Since Active Directory does not require any specific permission to access this kind of information. Here is the code, nothing advanced, but it does the work ;-)

function Get-DomainUser {
    PARAM($DisplayName)
    $Search = [adsisearcher]"(&(objectCategory=person)(objectClass=User)(displayname=$DisplayName))"
    foreach ($user in $($Search.FindAll())){
        New-Object -TypeName PSObject -Property @{
            "DisplayName" = $user.properties.displayname
            "UserName"    = $user.properties.samaccountname
            "Description" = $user.properties.description}
    }
}

Result


PS C:\> Get-DomainUser -DisplayName "jonathan*" | Format-List
UserName    : {JonathanD}
Description : {Account of Jonathan Delpiero}
DisplayName : {Jonathan Delpiero}

UserName    : {DumoulinJ}
Description : {Account of Jonathan Dumoulin}
DisplayName : {Jonathan Dumoulin}




[AdsiSearcher] Accelerator

I'm using the Windows PowerShell [AdsiSearcher] type accelerator to search Active Directory Domain Services (AD DS).

Using ADSI/LDAP Query is kind of confusing and I had to make some research to really understand how it actually works.

Let's take a look at this object and its members.
I start by creating an instance of the System.DirectoryServices.DirectorySearcher class by supplying empty strings.

PS C:\> [adsisearcher]""
CacheResults             : True
ClientTimeout            : -00:00:01
PropertyNamesOnly        : False
Filter                   :
PageSize                 : 0
PropertiesToLoad         : {}
ReferralChasing          : External
SearchScope              : Subtree
ServerPageTimeLimit      : -00:00:01
ServerTimeLimit          : -00:00:01
SizeLimit                : 0
SearchRoot               : System.DirectoryServices.DirectoryEntry
Sort                     : System.DirectoryServices.SortOption
Asynchronous             : False
Tombstone                : False
AttributeScopeQuery      :
DerefAlias               : Never
SecurityMasks            : None
ExtendedDN               : None
DirectorySynchronization :
VirtualListView          :
Site                     :
Container                :




PS C:\> [adsisearcher]"" | get-member
   TypeName: System.DirectoryServices.DirectorySearcher

Name                      MemberType Definition
----                      ---------- ----------
Disposed                  Event      System.EventHandler Disposed(System.Object, System.EventArgs)
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Dispose                   Method     void Dispose(), void IDisposable.Dispose()
Equals                    Method     bool Equals(System.Object obj)
FindAll                   Method     System.DirectoryServices.SearchResultCollection FindAll()
FindOne                   Method     System.DirectoryServices.SearchResult FindOne()
GetHashCode               Method     int GetHashCode()
GetLifetimeService        Method     System.Object GetLifetimeService()
GetType                   Method     type GetType()
InitializeLifetimeService Method     System.Object InitializeLifetimeService()
ToString                  Method     string ToString()
Asynchronous              Property   bool Asynchronous {get;set;}
AttributeScopeQuery       Property   string AttributeScopeQuery {get;set;}
CacheResults              Property   bool CacheResults {get;set;}
ClientTimeout             Property   timespan ClientTimeout {get;set;}
Container                 Property   System.ComponentModel.IContainer Container {get;}
DerefAlias                Property   System.DirectoryServices.DereferenceAlias DerefAlias {get;set;}
DirectorySynchronization  Property   System.DirectoryServices.DirectorySynchronization DirectorySynchronization {get;set;}
ExtendedDN                Property   System.DirectoryServices.ExtendedDN ExtendedDN {get;set;}
Filter                    Property   string Filter {get;set;}
PageSize                  Property   int PageSize {get;set;}
PropertiesToLoad          Property   System.Collections.Specialized.StringCollection PropertiesToLoad {get;}
PropertyNamesOnly         Property   bool PropertyNamesOnly {get;set;}
ReferralChasing           Property   System.DirectoryServices.ReferralChasingOption ReferralChasing {get;set;}
SearchRoot                Property   adsi SearchRoot {get;set;}
SearchScope               Property   System.DirectoryServices.SearchScope SearchScope {get;set;}
SecurityMasks             Property   System.DirectoryServices.SecurityMasks SecurityMasks {get;set;}
ServerPageTimeLimit       Property   timespan ServerPageTimeLimit {get;set;}
ServerTimeLimit           Property   timespan ServerTimeLimit {get;set;}
Site                      Property   System.ComponentModel.ISite Site {get;set;}
SizeLimit                 Property   int SizeLimit {get;set;}
Sort                      Property   System.DirectoryServices.SortOption Sort {get;set;}
Tombstone                 Property   bool Tombstone {get;set;}
VirtualListView           Property   System.DirectoryServices.DirectoryVirtualListView VirtualListView {get;set;}


Here we will be interested by the FindOne() and FindAll() methods to perform our searches, the problem is... how to filter the result, we just have thousands of objects with different object types.

After some search I found a list of the different Logical Operators and main Filters available for LDAP Search Filtering.

Logical Operators

Logical operator
Description
=
Equal to
~=
Approximately equal to
<=
Lexicographically less than or equal to
>=
Lexicographically greater than or equal to
&
AND
|
OR
!
NOT

Filters on objectCategory and objectClass

objectCategory
objectClass
Result
person
user
user objects
person
user and contact objects
person
contact
contact objects
user
user and computer objects
computer
computer objects
user
user and contact objects
contact
contact objects
computer
computer objects
person
user, computer, and contact objects
contact
user and contact objects
group
group objects
group
group objects
person
organizationalPerson
user and contact objects
organizationalPerson
user, computer, and contact objects
organizationalPerson
user and contact objects

More information on filters and examples. 


Now we can start to work with some basic filtering.

PS C:\> ([adsisearcher]"name=*jonathan*").FindAll()
Path                                              Properties
----                                              ----------
LDAP://CN=JonathanD,CN=Users,DC=FX,DC=LAB         {givenname, codepage, objectcategory, departme...
LDAP://CN=Jonathan Dumoulin,CN=Users,DC=FX,DC=LAB {givenname, codepage, objectcategory, descript...
LDAP://CN=Test Jonathan Group,CN=Users,DC=FX,D... {usnchanged, distinguishedname, grouptype, whe...
LDAP://OU=Test Jonathan OU,OU=Administration,D... {usnchanged, distinguishedname, whencreated, i...
LDAP://CN=Jonathan Contact,CN=Users,DC=FX,DC=LAB  {usnchanged, distinguishedname, displayname, w...


We get all kind of different type of objects: User, Organization Unit, Contact and Group objects.
Remember my coworker has the DisplayName property.
He just wants to know what are their AccountName (SamAccount)

So let's try with DisplayName property

PS C:\> ([adsisearcher]"displayname=*jonathan*").FindAll()
Path                                              Properties
----                                              ----------
LDAP://CN=JonathanD,CN=Users,DC=FX,DC=LAB         {givenname, codepage, objectcategory, departme...
LDAP://CN=Jonathan Dumoulin,CN=Users,DC=FX,DC=LAB {givenname, codepage, objectcategory, descript...
LDAP://CN=Jonathan Contact,CN=Users,DC=FX,DC=LAB  {usnchanged, distinguishedname, displayname, w...

Better, but we still get 2 type of objects (Users and Contacts). We'll need to add an extra filter to get the user object type only.

Using the two tables above I will build my filter.
Conditions

  • ObjectCategory = Person
  • ObjectClass = User
  • DisplayName = <input from user>

Finally, I use the ampersand symbol '&' symbol at the start. The whole query can be translated to:
Search for objectCategory=Person AND ObjectClass=user AND DisplayName = <input user>


PS C:\> ([adsisearcher]"(&(objectCategory=person)(objectClass=User)(displayname=*jonatha
n*))").FindAll()
Path                                              Properties
----                                              ----------
LDAP://CN=JonathanD,CN=Users,DC=FX,DC=LAB         {givenname, codepage, objectcategory, departme...
LDAP://CN=Jonathan Dumoulin,CN=Users,DC=FX,DC=LAB {givenname, codepage, objectcategory, descript...


Awesome! :-)
Now we just have to select the properties we want:

PS C:\> ([adsisearcher]"(&(objectClass=person)(objectClass=user)(displayname=*jonathan*)
)").FindAll().properties | Select-Object -first 1
Name                           Value
----                           -----
givenname                      {Jonathan}
codepage                       {0}
objectcategory                 {CN=Person,CN=Schema,CN=Configuration,DC=FX,DC=LAB}
department                     {FINANCE}
description                    {Account of Jonathan Delpiero}
usnchanged                     {196705}
instancetype                   {4}
logoncount                     {0}
name                           {JonathanD}
badpasswordtime                {0}
pwdlastset                     {0}
objectclass                    {top, person, organizationalPerson, user}
badpwdcount                    {0}
samaccounttype                 {805306368}
usncreated                     {61787}
sn                             {Delpiero}
objectguid                     {130 136 175 148 8 9 110 70 158 21 180 63 255 91 134 170}
whencreated                    {5/20/2013 2:26:31 AM}
adspath                        {LDAP://CN=JonathanD,CN=Users,DC=FX,DC=LAB}
useraccountcontrol             {512}
cn                             {JonathanD}
countrycode                    {0}
l                              {Montreal}
primarygroupid                 {513}
whenchanged                    {10/12/2013 4:04:01 AM}
title                          {Assistant}
lastlogon                      {0}
dscorepropagationdata          {1/1/1601 12:00:00 AM}
distinguishedname              {CN=JonathanD,CN=Users,DC=FX,DC=LAB}
samaccountname                 {JonathanD}
objectsid                      {1 5 0 0 0 0 0 5 21 0 0 0 180 190 60 92 74 161 105 103 20 195 233...
lastlogoff                     {0}
displayname                    {Jonathan Delpiero}
accountexpires                 {9223372036854775807}


Thanks for Reading! If you have any questions, leave a comment or send me an email at fxcat@lazywinadmin.com. I invite you to follow me on Twitter: @lazywinadm

2 comments:

  1. I come across this very often as well. There are many situations where people don't have the ActiveDirectory module installed, but still need to query Active Directory.

    Some things to be aware of:
    1) When you want to get other information by example the samaccountname, you can simply add $user.properties.samaccountname <--- This is case sensitive though, so $user.properties.SamAccountName will not return the value.
    2)To return a string, you can use : [String]$user.properties.displayname

    ReplyDelete
    Replies
    1. Hi Bjorn,

      I was not aware of your #1, will check it out!

      Thank you Bjorn!

      Delete