Summary: Learn how to use the Windows PowerShell [adsiSearcher] type accelerator to search Active Directory Domain Services (AD DS).

Hey, Scripting Guy! Question Hey, Scripting Guy! I am trying to get in touch with my inner programmer. The problem is that our company has cut head count in the past two years, and the remaining staff (myself included) is afraid to say no to anything, or to complain about too much work. The result has been many 12-hour days over the last two years. Training budget? Ha! That flew out the window months before the first pink slip arrived. So, I am struggling with attempting to learn a new skill, with no money for formal classes, or even to purchase a good book on the subject. While searching on the internet to learning about using Windows PowerShell to search Active Directory, I ran across your web site, but something confuses me. It seems that you use random methods to search AD, and I do not understand how you can use something in square brackets and it magically seems to work. How about starting from the beginning, I have been in IT for more than 30 years, so you can assume that I am mediocre, but trainable.

-- BB

 

Hey, Scripting Guy! Answer Hello BB,

Microsoft Scripting Guy Ed Wilson here. Craig sent me a link to a really good deal on tea this afternoon. It seems that my favorite e-commerce web site was running a special on tea today. Far out; those kind of things seem like a ray of sunshine, on an otherwise cloudy day. I was in the middle of a cup of rather generic tea with some lotus blossoms, cinnamon, lemon grass, and a single pine needle (I like coming up with my own combinations of tea) with a plate of Tim Tams and raw walnuts. The Blues Brothers were wailing from my Zune HD, and I was experimenting with Windows PowerShell when the Communicator on my laptop went off. It was Craig and the aforementioned good deal on tea –yes, I was interested! It is really cool to have good friends that look out for your best interests.

BB, I will be your friend. I can’t believe I never really explained the adsisearcher square brackets (as opposed to sponges who wear square pants). Well, let’s dive in.

When using the [adsiSearcher] type accelerator, you can create an instance of the class by supplying the constructor to it directly on the same line. You do not use the New-Object cmdlet. For example, if you attempt to use the New-Object cmdlet to create an instance of the class, the error shown in the following image is displayed. 

Image of error shown when attempting to use New-Object cmdlet

Typing [adsiSearcher] in the Windows PowerShell console, and pressing enter tells you the type accelerator is recognized, and that it will work, but it does not create an instance of the class – i.e., there is no constructor. Piping the type accelerator to the format-list cmdlet and asking for all the properties to be returned, supplies lots of cool information about the class, but does not return the information we saw when we used the new-object cmdlet to create the DirectorySearcher class. The details returned via the format-list cmdlet are seen here.

PS C:\> [adsisearcher]
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False DirectorySearcher System.ComponentModel....

PS C:\> [adsisearcher] | fl *
Module : System.DirectoryServices.dll
Assembly : System.DirectoryServices, Version=2.0.0.0, Culture=neut
ral, PublicKeyToken=b03f5f7f11d50a3a
TypeHandle : System.RuntimeTypeHandle
DeclaringMethod :
BaseType : System.ComponentModel.Component
UnderlyingSystemType : System.DirectoryServices.DirectorySearcher
FullName : System.DirectoryServices.DirectorySearcher
AssemblyQualifiedName : System.DirectoryServices.DirectorySearcher, System.Dire
ctoryServices, Version=2.0.0.0, Culture=neutral, Public
KeyToken=b03f5f7f11d50a3a
Namespace : System.DirectoryServices
GUID : 9ea63a6d-7480-31a3-ac60-1b358a0655a7
GenericParameterAttributes :
IsGenericTypeDefinition : False
IsGenericParameter : False
GenericParameterPosition :
IsGenericType : False
ContainsGenericParameters : False
StructLayoutAttribute : System.Runtime.InteropServices.StructLayoutAttribute
Name : DirectorySearcher
MemberType : TypeInfo
DeclaringType :
ReflectedType :
MetadataToken : 33554466
TypeInitializer : Void .cctor()
IsNested : False
Attributes : AutoLayout, AnsiClass, Class, Public, HasSecurity, Befo
reFieldInit
IsVisible : True
IsNotPublic : False
IsPublic : True
IsNestedPublic : False
IsNestedPrivate : False
IsNestedFamily : False
IsNestedAssembly : False
IsNestedFamANDAssem : False
IsNestedFamORAssem : False
IsAutoLayout : True
IsLayoutSequential : False
IsExplicitLayout : False
IsClass : True
IsInterface : False
IsValueType : False
IsAbstract : False
IsSealed : False
IsEnum : False
IsSpecialName : False
IsImport : False
IsSerializable : False
IsAnsiClass : True
IsUnicodeClass : False
IsAutoClass : False
IsArray : False
IsByRef : False
IsPointer : False
IsPrimitive : False
IsCOMObject : False
HasElementType : False
IsContextful : False
IsMarshalByRef : True

To create an instance of the DirectorySearcher class using the type accelerator, you can supply a set of empty strings as seen here.

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

There is only one problem, and that is that no search filter has been supplied. This is a major source of confusion for people attempting to use the [adsisearcher] type accelerator; and it is especially true when people are used to the behavior of the new-object cmdlet / no constructor syntax that was examined earlier. Remember, that when one string value is supplied to the DirectorySearcher class for a constructor the value is interpreted as the filter. Therefore, [adsisearcher]”” specifies a filter that has no characters in it. The good thing is that the searchroot is automatically set to the root of the current domain. This is seen here.

PS C:\> $a = [adsisearcher]""
PS C:\> $a.Filter
PS C:\> $a.SearchRoot
distinguishedName : {DC=NWTraders,DC=Com}
Path : LDAP://DC=NWTraders,DC=Com
PS C:\>

With the new-object cmdlt and no constructor the “objectcategory=*” filter is created. But when [adsisearcher’”” is used, we have specifically created an instance of the DirectorySearcher class with no filter. There is no way, (that I know of. If you find a way, email me at scripter@microsoft.com) to create an instance of the DirectorySearcher class, using the [adsisearcher] type accelerator by supplying no constructor. With no filter, an error is returned when attempting to call the findone() method. This is seen here.

PS C:\> $a.FindOne()
Exception calling "FindOne" with "0" argument(s): "The search filter is invalid."
At line:1 char:11
+ $a.FindOne <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
PS C:\>

It is not that different objects are being created; the same object is created. This is seen here.

PS C:\> ([adsisearcher]"").gettype()
IsPublic IsSerial Name BaseType
-------- -------- ----
True False DirectorySearcher System.ComponentModel
PS C:\> ([adsisearcher]"name=bob").gettype()
IsPublic IsSerial Name BaseType
-------- -------- ----
True False DirectorySearcher System.ComponentModel....
PS C:\>

It is, of course, possible to add a filter, and then to call the method. This is seen here.

PS C:\> $a = [adsisearcher]""
PS C:\> $a.Filter = "name=bob"
PS C:\> $a.FindOne()
Path Properties
---- ----------
LDAP://CN=bob,OU=HSG_TestOU,DC=NWTrader... {primarygroupid, msexchpoliciesinclude...
PS C:\>

The problem is this is simply extra work. Because of this confusion, some bloggers have advocated simply not using the [adsisearcher] type accelerator, and always using New-Object to create the DirectorySearcher class. However, the goal is to do less work, not more, and in this case, the answer is easy. Supply the search filter when creating the class instance. The reason for creating an instance of the DirectorySearcher is that you are interested in searching for something; therefore, you will always have some sort of filter in mind.

If you want to use the default search filter that is created by the New-Object technique, use the “objectcategory=*” filter. This is seen here.

PS C:\> $a = [adsisearcher]"objectcategory=*"
PS C:\> $a.Filter
objectcategory=*
PS C:\> $a.SearchRoot
distinguishedName : {DC=NWTraders,DC=Com}
Path : LDAP://DC=NWTraders,DC=Com
PS C:\>

Because the DirectorySearcher object is created by using the [adsisearcher] type accelerator, and the filter is specified in the constructor, there is no need to store the intervening object in a variable. The intervening variable methodology is seen here.

PS C:\> $a = [adsisearcher]"objectcategory=computer"
PS C:\> $a.FindOne()
Path Properties
---- ----------
LDAP://CN=HYPERV,OU=Domain Controllers,... {primarygroupid, iscriticalsystemobjec...
PS C:\>

The DirectorySearcher object is stored in the $a variable, and then the FindOne() method is called. To avoid the intervening variable, you can use a pair of parentheses to force evaluation of the first line of code. Once the DirectorySearcher object is created, the FindOne() method can be called. This is seen here.

PS C:\> ([adsisearcher]"objectcategory=computer").FindOne()
Path Properties
---- ----------
LDAP://CN=HYPERV,OU=Domain Controllers,... {primarygroupid, iscriticalsystemobjec...
PS C:\>

The FindAll() method can also be called directly. This is shown in the following image.

Image of FindAll() method being called directly

 

BB, that is all there is to using the [adsisearcher] type accelerator. Searching Active Directory Domain Services week will continue tomorrow when we talk about searching AD DS for computers, and pinging them to see if they are up. We would love for you to follow us on Twitter or Facebook. If you have any questions, send email to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys