Home > Articles > Operating Systems, Server > Microsoft Servers

  • Print
  • + Share This
From the author of

Getting the Most Out of the Global Catalog

The GC can be thought of as a small database within AD that is replicated around the whole forest. The GC holds partial information about the objects within the AD environment, and it is what is used when a user from one region travels to another and logs onto their home domain from the region they have travelled to.

The GC is a very useful tool when trying to get information about the objects in AD when they are disbursed around the world.

Listing 1 shows you how to connect to AD via ADO, and how to query the GC for a user's details.

Listing 1—Querying Our GC for User Information

Private Sub QueryGC()
  'General strings and integers
  Dim strConn As String
  Dim strGCQuery As String
  Dim strADRoot As String
  Dim strUserAccount As String
  Dim i As Integer  
  'ADO objects
  Dim objCmd As ADODB.Command
  Dim objRs As ADODB.Recordset
  Set objCmd = New ADODB.Command
  Set objRs = New ADODB.Recordset
  'A user name and password can be specified in the connection string
  'But when reading from the AD any domain user can read
  strConn = "Provider=ADsDSOObject;" & _
       "App=HGI-AMPAM;"       
  'Just subsititue these variable with your domain and username you want to find...
  strADRoot = "root.mydomain.com"
  strUserAccount = "rhawthorne"
  'Build the Global Catalog query string (notice the GC prefix)...
  strGCQuery = "<GC://" & strADRoot & ">;(&(objectClass=user)" & _
        "(sAMAccountName=" & strUserAccount & "));" & _
        "AdsPath,objectGUID;subtree"  
  With objCmd
    .CommandTimeout = 60
    .ActiveConnection = strConn
    'Notice that we cannot specify the type of query like we would if it was
    'a query against a SQL DB
    .CommandType = adCmdUnknown
    .CommandText = strGCQuery
  End With
  With objRs
    .CursorLocation = adUseClient
    .CursorType = adOpenStatic
    .LockType = adLockReadOnly
    .Open objCmd
  End With
  If Not objRs.EOF Then
    While Not objRs.EOF
      For i = 0 To objRs.Fields.Count - 1
        Debug.Print objRs.Fields(i).Name & " :: " & objRs.Fields(i).Value
      Next i
      objRs.MoveNext
    Wend
  End If
  'Be good and clean up your objects now!!!
  objRs.Close
  Set objCmd = Nothing
  Set objRs = Nothing
End Sub

For any of you who have done LDAP queries before, this will not look too dissimilar. However, there are some special attributes that we request back: objectGUID and ADsPath. With these two attributes, we can retrieve the relative path of the user (ADsPath); that is, where the user exists within AD and the GUID of the user.

Nine times out of 10, this code will do exactly what you need. However, what you need to be careful about is where you specify the username. Notice we have not specified the domain that the user resides in. This means that if we have two (or more) rhawthornes in different domains, we will return more than one record in our recordset.

The million dollar question is, "Why don't you just specify the domain that the user resides in?" The answer, my reader? You can't! Yes, I was as shocked as you are, but after numerous calls to Microsoft PSS, it was discovered that you can't specify the domain that you want a user from in the query string in GC queries to the AD. Instead, you have to get back the whole recordset and then iterate through the records to get the user that you want from a specific domain. The following code snippet is one I use to get the correct user's domain information from the ADsPath.

Public Function GetDomain(ByVal strDomainName As String) As String
  Dim strDomain As String
  Dim strSearch As String
  Dim strEndString As String
  Dim intStart As Integer
  Dim intEnd As Integer
'Look for the occurrence of the string DC separated by a comma
  strSearch = "DC="
  strEndString = ","
  If InStr(strDomainName, strSearch) <> 0 Then
    intStart = InStr(strDomainName, strSearch)
    intEnd = InStr(intStart, strDomainName, strEndString)
    strDomain = Mid(strDomainName, intStart + Len(strSearch), intEnd - (intStart + Len(strSearch)))
  End If
  GetDomain = strDomain
End Function

NOTE

The ADsPath of a user comes in the format DC= root,DC=mydomain,DC=com. This code assumes that the first occurrence of the DC= is the user's domain (which, by the way, it always is!). AD always build a path from the lowest attribute to the highest: Username, Organizational Unit, User Domain, Parent Domain(s), and Parent Domain suffix.

One last thing: The objectGUID is not a string, or in fact even a number. It is a byte-array, and as such needs to be converted into a string so that you can read it. The following code snippet will convert the byte-array into a string so that you can read it.

Public Function ParseGUID(ByVal varObjectGUID As Variant) As String
  Dim i As Integer
  Dim strGUID As String
  'Just reset the variable strGUID to ensure that we do not get invalid GUIDs
  strGUID = ""
  For i = 0 To UBound(varObjectGUID)
    If Len(Hex(Trim(varObjectGUID(i)))) < 2 Then
      strGUID = strGUID & "0" & Hex(Trim$(varObjectGUID(i)))
    Else
      strGUID = strGUID & Hex(Trim$(varObjectGUID(i)))
    End If
  Next i
  ParseGUID = LCase$(Trim$(strGUID))
End Function

You might have noticed the little "hack" that needed to be implemented to ensure that the GUID is not invalid. When you use the HEX function to convert "01" from a byte array, it returns "1". This means that when you look for a user in the AD with a GUID, it won't find them! You need to "pad" the converted value (if its length is less than 2) with a "0" to ensure that you do not lose any values when doing the conversion.

  • + Share This
  • 🔖 Save To Your Account