2024-08-29

Excel Add-on for querying Active Directory

Below are some utility VBA functions that query user information from active directory. These can be added to an .xla file, and loaded as an Excel add-on. This will provide excel functions like ADUserProperty, managerOf, emailOf etc. The only down side is that each function call is a blocking network operation, so flash-filling them for large number of rows might cause excel to freeze for some time.

Option Explicit

Public Function getADDomain() As String
    Dim objLdap As Object
    Dim strLdapDomain As String

    On Error Resume Next
    Set objLdap = GetObject("LDAP://rootDSE")
    On Error GoTo 0

    If (objLdap Is Nothing) Then
        Exit Function
    End If

    strLdapDomain = objLdap.Get("defaultNamingContext")

    If (Trim(strLdapDomain) = "") Then
        Exit Function
    Else
    getADDomain = strLdapDomain
    End If
End Function


Public Function ADUserProperty(ByVal strUserId As String, ByVal adField As String, Optional ByVal domainStr As String = "DEFAULT_DOMAIN") As String

    Dim objLdapConnection As Object
    Dim objLdapCommand As Object
    Dim objLdapRecordSet As Object

    Dim objField As Variant

    ' Connect to ActiveDirectory using ADODB
    Set objLdapConnection = CreateObject("ADODB.Connection")
    Call objLdapConnection.Open("Provider=ADsDSOObject;")

    ' Create command to queryActiveDirectory using LDAP
    Set objLdapCommand = CreateObject("ADODB.Command")

    ' Set the query properties
    With objLdapCommand
        ' Set the connection
        .ActiveConnection = objLdapConnection

        ' Search the AD recursively, starting at the root of the domain
        .CommandText = "<LDAP://" & Trim(domainStr) & ">;" & _
          "(&(objectCategory=User)" & _
          "(|(sAMAccountName=" & Trim(strUserId) & ")" & _
          "(distinguishedName=" & Trim(strUserId) & ")" & _
          "(displayName=" & Trim(strUserId) & ")" & _
          "(cn=" & Trim(strUserId) & ")" & _
          "(mail=" & Trim(strUserId) & ")));" & _
          Trim(adField) & ";subtree"
    End With

    ' Execute LDAP query
    Set objLdapRecordSet = objLdapCommand.Execute

    If (objLdapRecordSet.BOF Or objLdapRecordSet.EOF) Then
        ADUserProperty = "0"
        Exit Function
    Else
        ' Walk through all users
        Do While (Not objLdapRecordSet.EOF)

            objField = objLdapRecordSet.Fields(Trim(adField))

            If (Trim(adField) = "description") Then
             ADUserProperty = Join(VariantArrayToStringArray(objField))
            ElseIf (objField <> vbNull) Then
               ADUserProperty = objField
            End If
             ' Next record
             Call objLdapRecordSet.MoveNext
         Loop

         If (Not objLdapRecordSet Is Nothing) Then
            Call objLdapRecordSet.Close
            Set objLdapRecordSet = Nothing
         End If
    End If
    Set objLdapCommand = Nothing
End Function

Public Function managerOf(ByVal strUserId As String, Optional ByVal domainStr As String = "DEFAULT_DOMAIN") As String
    Dim managerDN As String

    managerDN = ADUserProperty(strUserId, "manager", domainStr)
    If (managerDN = "0") Then
        managerOf = "0"
    Else
        managerOf = ADUserProperty(managerDN, "cn", domainStr)
    End If
End Function

Public Function managerEmailOf(ByVal strUserId As String, Optional ByVal domainStr As String = "DEFAULT_DOMAIN") As String
    Dim managerDN As String

    managerDN = ADUserProperty(strUserId, "manager", domainStr)
    If (managerDN = "0") Then
        managerEmailOf = "0"
    Else
        managerEmailOf = ADUserProperty(managerDN, "mail", domainStr)
    End If
End Function

Public Function emailOf(ByVal strUserId As String, Optional ByVal domainStr As String = "DEFAULT_DOMAIN") As String
        emailOf = ADUserProperty(strUserId, "mail", domainStr)
End Function

Public Function descriptionOf(ByVal strUserId As String, Optional ByVal domainStr As String = "DEFAULT_DOMAIN") As String
        descriptionOf = ADUserProperty(strUserId, "description", domainStr)
End Function


' Array Variant to String
Public Function VariantArrayToStringArray(ByVal arrVariants As Variant) As String()
    Dim arrStrings() As String
    ' Get the string array
    Call ParamArrayToStringArray(arrVariants, arrStrings)
    ' Get the string array
    VariantArrayToStringArray = arrStrings
End Function

' Array Variant to String
Public Sub ParamArrayToStringArray(ByVal arrVariants As Variant, ByRef arrStrings() As String)
    Dim intLength As Integer
    ' Handle the array
    Call ParamArrayToStringArrayInternal(arrVariants, arrStrings, intLength)
End Sub


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''


' Array Variant to String
Private Sub ParamArrayToStringArrayInternal(ByVal arrVariants As Variant, ByRef arrStrings() As String, ByRef intLength As Integer)
    ' Parameter is array?
    If (IsArray(arrVariants)) Then
        Dim i As Integer
        Dim objValue As Variant
        ' Walk through the specified partner objects
        For i = LBound(arrVariants) To UBound(arrVariants) Step 1
            ' Get the value
            objValue = arrVariants(i)
            ' Array to string
            Call ParamArrayToStringArrayInternal(objValue, arrStrings, intLength)
        Next
    Else
        ' Next item
        intLength = intLength + 1
        ' Expand array
        ReDim Preserve arrStrings(1 To intLength)

        ' Set the value
        arrStrings(intLength) = CStr(arrVariants)
    End If
End Sub

' String Array
' Convert ParamArray to String array
Public Function StringArray(ParamArray arrValues() As Variant) As String()
    ' Get the string array
    StringArray = VariantArrayToStringArray(arrValues)
End Function