Reading Information from Active Directory

Retrieving information from Active Directory is perhaps one of the most important and frequently used operations. (Writing data into Active Directory or modifying attributes of Active Directory objects is not a challenge if you know the type of these data, i.e., attribute syntax rules, and are familiar with methods of access to data of that type.) Certainly, you can use any standard tools and administrative snap-ins discussed earlier in this book to get information; however, custom scripts allow you to save a lot of time on routine operations or combine results in a way that is most convenient for you. Here are topics that the following scripts aim to demonstrate:

  • Which data and operations can be valuable for administrators

  • Where these data are located

  • How to retrieve data of frequently used types

Saving Results to a Disk File and Reading the Abstract Schema

Before we begin discussing various ADSI scripts, let us consider how to save results for subsequent analysis. As an example, we will use a script that produces more than two thousand lines in total. The following script reads all information about attributes and classes represented in the Active Directory schema (in a particular installation), simultaneously displays results on the screen and writes them to a file. You can insert statements (only four in all!) similar to the bold ones into any script, and save the obtained results if necessary.

All attributes of the abstract schema used — attribute Types, extendedAttributeInfo, objectClasses, and extendedClassInfo — are multi-valued. Thus, this script is also an example of reading properties that may have more than one value. Note that in Windows. NET these attributes are only accessible with the GetInfo Ex method, whereas on Windows 2000 servers you can use the usual GetInfo method.

The following table shows the total number of attributes and classes in Windows 2000 (schema version 13) and Windows. NET (build 3663, schema version 29):


Windows 2000 (schema version 13)

Windows .NET (build 3663, schema version 29)







Listing 17.1. AbstrSchema.vbs — Writing the Definitions of Active Directory Attributes and Classes to a File

start example
    Dim objSchema 'As IADsContainer    Dim attr    Dim x 'As Variant    Dim i 'As Integer    Dim myFile 'As Variant    Dim objFSO 'As Variant    Set objSchema = _      Getobject ("LDAP: //CN=Aggregate, CN=Schema, CN=Configuration," + -                  "DC=net, DC=dom")    objSchema.GetInfo    Set ObjFSO = Createobject ("Scripting. FileSystemObject")    ' Specify your own file name. An existing file will be re-written!    Set myFile = ObjFSO. CreateTextFile ("C: \Abstract Schema. txt", True)    i = 1    ' ***** Read attributes represented in the schema. *****    ' Here is a sample output string (parentheses are included):    ' ( NAME 'cn' SYNTAX ''    '  SINGLE-VALUE )    ' All Microsoft attribute IDs begin with the prefix ''1.2.840.113556.1''.    objSchema. GetInfoEx Array (''attributeTypes''), 0    attr = objSchema.Get (''attributeTypes'')    myFile.WriteLine ''Attributes'' + vbCrLf    For Each x In attr      'WScript.Echo CStr(i) + ''. '' + x      myFile.WriteLine CStr(i) + ''. ''.'' + x      i = i + 1    Next    WScript.Echo '' Attributes (TOTAL) '' + CStr(i - 1)    i = 1    ' ***** Read additional information about attributes. *****    ' Here is a sample output string:    ' ( NAME 'cn' RANGE-LOWER '1' RANGE-UPPER '64'    '  PROPERTY-GUID '3F7996BFE60DD011A28500AA003049E2'        '  PROPERTY-SET-GUID '54018<SP>DE4F8BCD111870200C04FB96050' INDEXED )   objSchema.GetInfoEx Array ("extendedAttributeInfo"), 0    attr = objSchema.Get ("extendedAttributeInfo")    myFile.WriteLine vbCrLf + "Attributes — Extended information" + vbCrLf    For Each x In attr      'WScript. Echo CStr(i) + " . " + x      i = i + 1    Next    i = 1    ' ***** Read classes represented in the schema. ******    ' Output strings are similar to the following:    ' ( NAME 'subSchema' SUP top STRUCTURAL MAY    '   (extendedClassInfo $ extendedAttributeInfo $ dITContentRules    '   $ attributeTypes $ objectClasses $ modifyTimeStamp ) )    ' Keep in mind that each class also inherits the attribute list of its    ' parent class!    objSchema.GetInfoEx Array ("objectClasses"), 0    attr = objSchema.Get ("objectClasses")    myFile.WriteLine vbCrLf + "Classes" + vbCrLf    For Each x In attr      'WScript.Echo CStr(i) + "." + x      myFile.WriteLine CStr(i) + "." + x      i = i + 1    Next    WScript.Echo " Classes (TOTAL) " + CStr(i - 1)    i = 1    '***** Read additional information about classes, for example:    ' ( NAME 'subSchema'    '   CLASS-GUID '61328B5A8DC3D111BBC90080C76670C0' )    objSchema.GetInfoEx Array("extendedClassInfo"), 0    attr = objSchema.Get ("extendedClassInfo")    myFile.WriteLine vbCrLf + "Classes - Extended information" + vbCrLf    For Each x In attr      'WScript.Echo CStr(i) + "." + x      myFile.WriteLine CStr(i) + "." + x      i = i + 1    Next    WScript .Echo "End."    myFile.Close    Set objSchema = Nothing    Set attr = Nothing    Set x = Nothing    Set objFSO = Nothing    Set myFile = Nothing 
end example

Retrieving Information from a RootDSE

From the following script, you can learn how to access the RootDSE object and use two popular interfaces, namely, IADsPropertyList and IADsPropertyEntry. RootDSE is the main source of information about names of Active Directory partitions and Directory Service Agents. (See Chapter 2, "Active Directory Terminology and Concepts," for detailed information on RootDSE.)

This script can also serve as an example of handling ADSI errors.

Listing 17.2. getRootDSE.vbs — Reading the Attributes of a RootDSE Object

start example
    Dim objRootDSE 'As IADsPropertyList    Dim objProperty 'As IADsPropertyEntry    Dim i 'As Integer    Dim intCount 'As Integer    On Error Resume Next    ' Serverless binding is preferable.    Set objRootDSE = GetObject ("LDAP://RootDSE")    'Yet in some cases, you may need to bind to a specific DC:    '   Set objRootDSE =_    '      GetObject ("LDAP://")    ' Catch possible errors:    If Hex (Err.Number) = "8007203A" Then    WScript.Echo "ERROR_DS_ SERVER _ DOWN (" & Hex(Err.Number) & ")"    WScript.Quit    ElseIf Hex(Err.Number) <> 0 Then       WScript .Echo "Error", Hex (Err .Number)       WScript . Quit    End If    objRootDSE.GetInfo    WScript .Echo "*** RootDSE object on " + objRootDSE.Get ("dnsHostName") -                + " ***" + vbCrLf    intCount = objRootDSE.PropertyCount    For i = 0 To intCount - 1       Set objProperty = objRootDSE. Item(i)       WScript.Echo CStr(i + 1) + ") " + objProperty.Name    Next    WScript.Echo "------------------------------------"    WScript .Echo " DSA name: " + objRootDSE. dsServiceName    ' Names of Active Directory partitions must be always obtained    ' from the RootDSE object only:    WScript.Echo " Domain patition:     " + _                 objRootDSE. Get ("defaultNamingContext")    WScript.Echo " Schema partition:     " + _                 objRootDSE. Get ("schemaNamingContext")    WScript. Echo " Configuration partition: " + _                 objRootDSE. Get ("configurationNamingContext")    WScript.Echo " Highest USN: " + objRootDSE. Get ("highestCommittedUSN")    WScript.Echo " Is synchronized? "+ _                 objRootDSE. Get ("isSynchronized")    WScript. Echo " Is a Global Catalong? " + _                 objRootDSE. Get ("isGlobalCatalogReady")    Set objRootDSE = Nothing    Set objProperty = Nothing 
end example

The highest USN committed by the server is one of the basic parameters used for troubleshooting Active Directory replication. Besides, if you write down the USN at a specific moment, you'll be able to determine which objects have been modified since then, for instance, by using a simple command similar to the following one:

    search "LDAP: //DC=net, DC=dom" /C: "uSNChanged>=123456" /S: subtree 

The isSynchronized attribute indicates whether the DC has replicated all directory partitions after its promotion. Do not confuse this initial replication with a normally scheduled one.

Remember that the isGlobalCatalogReady attribute is TRUE only when the DC has successfully completed its promotion to a GC server. Do not expect that it will be immediately set once you have designated (manually or programmatically) that DC as a GC server.

Reading the Property List

Here is another example of using the IADsPropertyList and IADsPropertyEntry interfaces with various providers as well as with Global Catalog. By using this code, you can compare a directory object's attribute lists received from different providers or see the attributes replicated to Global Catalog. Only defined attributes (i.e., those that have values) are included in the list. (To see all possible attributes, you must refer to the schema; see Listing 17.4.) You can also view the type of each attribute as it defined in the ADSTYPE enumeration.

The program also illustrates how to use one more important (core) interface — IADsOpenDSObject. This interface is used when you want to explicitly specify the credentials, which the program will use for binding to the directory object. Authentication flags used to define the binding options are listed in the ADS_AUTHENTICATION_ENUM enumeration.

Listing 17.3. Prop-of-obj.bas — Retrieving the Property List of a Directory Object

start example
    Option Explicit    Sub Main ( )    Dim objcontext As IADsOpenDSObject    Dim objAD As IADsPropertyList    Dim objProp As IADsPropertyEntry    Dim iCount As Integer    Dim i As Integer    Set objcontext = GetObject ("LDAP:")    ' Set objAD = objContext.OpenDSObject _    '     ("LDAP: //CN=John Smith,OU=Staff, DC=net, DC=dom", _    '      vbNullString, vbNullString, _    '      ADS_SECURE_AUTHENTICATION)    ' The preceding statement is equal to    '  Set objAD = GetObject("LDAP://...")    ' i.e. you bind to the object using the credentials    ' of the caller (currently logged on user)    On Error Resume Next    ' Bind using the specified credentials:    Set objAD = objContext.OpenDSObject _               ("LDAP://CN=John Smith,OU=Staff,DC=net,DC=dom", -               "administrator", "psw",_               ADS_SECURE_AUTHENTICATION)    If Hex (Err.Number) = "80072030" Then       Debug.Print "LDAP_NO_SUCH_OBJECT (" + Hex(Err.Number) + ")"       Exit Sub    ElseIf Hex (Err.Number) <> 0 Then       ' You may or may not be authenticated:       Debug.Print "Error (" + Str(Err.Number) + ") Hex: " + Hex(Err.Number)       Exit Sub    End If    ' As an alternative, you can use the security context of the currently    ' logged on user. Instead of all preceding statements choose one    ' of the following applicable binding strings:    ' * Binding to a user object:    '   Set objAD = Getobject ("LDAP: //CN=John Smith,OU=Staff,DC=net,DC=dom")    ' * Binding through Global Catalog:    '   Set objAD = GetObject ("GC: //CN=John Smith,OU=Staff,DC=net,DC=dom")    ' * Binding to a user or group object with use of the WinNT provider:    '   Set objAD = GetObject ("WinNT: //NET/jsmith,user")    '   Set objAD = GetObject ("WinNT: //NET/Domain Users, group")    ' * Binding to the domain object:    '   Set objAD = GetObject ("WinNT: //NET, domain")    objAD.GetInfo    Debug. Print "Attributes of", objAD.ADsPath    iCount = objAD.PropertyCount    Debug.Print " Total # " + CStr(iCount)    For i = 0 To iCount - 1       Set objProp = objAD.Item(i)       Debug.Print CStr(i + 1) + ") " + objProp.Name + _         " (type " + CStr(objProp.ADsType) + ")"    Next    Set objAD = Nothing    Set objProp = Nothing    End Sub 
end example

Retrieving Characteristics of an Object Class from the Schema

You might want to retrieve the complete information about a directory object derived from an object class. These data are stored in the Schema partition, and there is a special interface named IADsClass that allows you to access these data. A few methods of this interface as well as the Schema and Class methods of the IADs interface are represented in the following program.

Listing 17.4. Attrs-of-Class.bas — Obtaining Common Information and a List of Possible Attributes for a Directory Class

start example
    Option Explicitsub Main()    Dim strPath As String    Dim objAD As IADs    Dim objClass As IADsClass    Dim varAttr As Variant    Dim arr As Variant    Dim n As Integer    ' A user account has been specified here;    ' You can specify any directory object:    strPath = "LDAP: //CN=John Smith,OU=Staff, DC=net, DC=dom"    ' Connect to the directory object specified in the path    Set objAD = GetObject (strPath)    Debug.Print "Schema path: '" + objAD.Schema & "'for " + strPath    Debug.Print "Object class: '" + objAD.Class + "'" + vbCrLf    Debug.Print "=== The properties of the class ==="    ' Retrieve the information about the class — bind to the schema.    ' You could omit the previous statements and directly specify the name    ' of the necessary object class, for example ''LDAP: //schema/user''.    Set objClass = GetObject (objAD.Schema)    Debug.Print "Is abstract class? " + CStr (objClass.Abstract)    Debug.Print "ADsPath: " + objClass.ADsPath    Debug.Print "Is container? " + CStr (objClass.Container)    If objClass.Container Then       Debug.Print " May contain: "       ' The list of object classes that the selected class can contain:       arr = objClass.Containment       On Error Resume Next       If Len(arr) = 0 Then         For Each varAttr In arr         Debug.Print " - " + varAttr       Next     Else      Debug.Print " - " + objClass.Containment     End If    End If    Debug.Print "Derived from: " + objClass.DerivedFrom    Debug.Print "Naming: " + objClass.NamingProperties    Debug.Print "Class OID: " + objClass.OID    Debug.Print "Possible superiors:"    arr = objClass.PossibleSuperiors    On Error Resume Next    If Len(arr) = 0 Then      For Each varAttr In arr       Debug.Print " - " + varAttr      Next    Else      Debug.Print " - " + objClass.PossibleSuperiors    End If    Debug.Print vbCrLf + "=== Attributes ==="    Debug.Print "MUST have:" ' The list of mandatory attributes n=0    For Each varAttr In objClass.MandatoryProperties      Debug.Print " :: " + varAttr      n = n + 1    Next    Debug.Print " (TOTAL) : " + CStr (n)    Debug.Print    Debug.Print "MAY have: " ' The list of optional attributes    n=0    For      Each varAttr In objClass.OptionalProperties      Debug.Print " :: " + varAttr      n = n + 1    Next    Debug.Print " (TOTAL): " + CStr (n)    Set objAD = Nothing    Set objClass = Nothing    End Sub 
end example

Reading Property Values of Different Types

Some problems may arise when you attempt to display the values of certain properties. This is often due to selection of an inappropriate format. The following example program displays some property types. Some types, such as Large Integer, NT Security Descriptor, or Octet String (for example, the objectSid property), require special conversion procedures. Take note of the obj.Guid method inherited from the IADs interface. It produces a string that can be used for binding to the object (in the format "LDAP: //<GUID=XXXX>"); however, this string cannot be used with the Search.vbs script or Guid2obj.exe tool.

Data types are defined in the ADSTYPE enumeration.

Listing 17.5. getProps.vbs - Retrieving the Property Values for a User Object

start example
    Option Explicit    Sub Main ()    Dim objPropList As IADsPropertyList    Dim objPropEntry As IADsPropertyEntry    Dim objVar As IADsPropertyValue    Dim v As Variant    Dim i As Integer    Dim iCount As Integer    ' You can select any directory object; a user account, for example:    Set objPropList = _            GetObject ("LDAP: //CN=John Smith,OU=Staff, DC=net, DC=dom")    objPropList.GetInfo    iCount = objPropList.PropertyCount    Debug.Print "***** Total " + CStr(iCount) + _                "attributes have values for " + _                objPropList.ADsPath; " * * * * * "    Debug.Print "GUID: " + objPropList.Guid    For i = 0 To iCount - 1      Set objPropEntry = objPropList.Item(i) 'or = objPropList.Next      Debug.Print objPropEntry.Name + " (" + _                  CStr (objPropEntry.ADsType) + ") "    Select Case objPropEntry.ADsType      Case 1 'ADSTYPE_DN_STRING    For Each v In objPropEntry.Values      Set objVar = v      Debug.Print " Value: " + objVar.DNString    Next       Case 3 'ADSTYPE_CASE_IGNORE_STRING       For Each v In objPropEntry.Values         Set objVar = v         Debug.Print " Value: " + objVar.CaseIgnoreString       Next       Case 7 'ADSTYPE_INTEGER       For Each v In objPropEntry.Values         Set objVar = v         Debug.Print " Value: " + CStr (objVar.Integer)       Next       Case 8 'ADSTYPE_OCTET_STRING         Debug.Print " .OctetString"       Case 9 'ADSTYPE_UTC_TIME         For Each v In objPropEntry.Values         Set objVar = v         Debug.Print " Value: " + CStr (objVar.UTCTime)       Next       Case 10 'ADSTYPE_LARGE_INTEGER         Debug.Print " .LargeInteger" 'Object doesn't support this                                      ' property or method    Case 25 'ADSTYPE_NT_SECURITY_DESCRIPTOR         Debug.Print " .SecurityDescriptor" ' Object doesn't                              'support this property or method       Case Else         Debug.Print " The value hasn't been converted."       End Select    Next    Set objPropList = Nothing    Set objPropEntry = Nothing    Set objVar = Nothing    End Sub 
end example

Windows  .NET Domains & Active Directory
Windows .NET Server 2003 Domains & Active Directory
ISBN: 1931769001
EAN: 2147483647
Year: 2002
Pages: 154 © 2008-2017.
If you may any questions please contact us: