4

I'm trying to find the simplest way to query Active Directory, in one of two ways:

  1. Given an AD username, find all the groups (INCLUDING nested groups) that the user is a member of.

  2. Given an AD group name, find all the users (including those users in nested groups) that are part of the group.

My app is in VB.NET on the v4.0 framework. I've reviewed suggestions from many different Google search results, some of which utilize LDAP and System.DirectoryServices.DirectorySearcher (which I'm thinking might be the best route).

But I'm spinning my wheels and am looking for code samples.

Thank you.

UPDATE:

I've got these pieces in place:

<add assembly="System.DirectoryServices, Version=3.5.0.0, etc."/>

<add namespace="System.DirectoryServices.AccountManagement" /> or Imports System.DirectoryServices.AccountManagement

and on this line of code:

Dim ctx As New PrincipalContext(ContextType.Domain)

I still get this error: Type 'PrincipalContext' is not defined

When you mention the "using statement," I assume you meant that I need to reference this namespace. Or did you mean I should do something like this?

Using ctx As New PrincipalContext(ContextType.Domain)

Octavient
  • 603
  • 3
  • 11
  • 21

3 Answers3

4

A Complete example

This code will list all users in a group AND sub groups by enumerating a given group name. also, if a users account is enabled / disabled.

To use, just call ListADGroupMembers("Some_Group_Name"). This will populate the users full name and mobile number into an array, which you can then loop through.

Its pretty simple to follow, just read it through.

Public ADUSers(,) As String
 Public n As Integer = 0

 Public Sub ListADGroupMembers(ByVal GN As String)

    Dim DirectoryRoot As New DirectoryEntry("LDAP://RootDSE")
    Dim DNC = DirectoryRoot.Properties("DefaultNamingContext")(0).ToString()
    Dim GroupName As String = GN '"G_All_IT_Users"
    Dim GroupMembers As System.Collections.Specialized.StringCollection = GetGroupMembers(DNC, GroupName)
    'Dim GroupMembersMobile As System.Collections.Specialized.StringCollection = GetGroupMembers(DNC, GroupName)
    '  For Each Member As String In GroupMembers
    '    ListBox1.Items.Add(Member)
    'Next Member

End Sub

Public Function GetGroupMembers(ByVal strDomain As String, ByVal strGroup As String) As System.Collections.Specialized.StringCollection

    Dim GroupMembers As New System.Collections.Specialized.StringCollection()

    Try
        Dim DirectoryRoot As New DirectoryEntry("LDAP://" & strDomain)
        Dim DirectorySearch As New DirectorySearcher(DirectoryRoot, "(CN=" & strGroup & ")")
        Dim DirectorySearchCollection As SearchResultCollection = DirectorySearch.FindAll()
        For Each DirectorySearchResult As SearchResult In DirectorySearchCollection
            Dim ResultPropertyCollection As ResultPropertyCollection = DirectorySearchResult.Properties
            Dim GroupMemberDN As String
            For Each GroupMemberDN In ResultPropertyCollection("member")
                Dim DirectoryMember As New DirectoryEntry("LDAP://" & GroupMemberDN)
                Dim DirectoryMemberProperties As System.DirectoryServices.PropertyCollection = DirectoryMember.Properties
                Dim DirectoryItem As Object = DirectoryMemberProperties("sAMAccountName").Value
                Dim DirectoryPhone As Object = DirectoryMemberProperties("mobile").Value
                Dim uac As Object = DirectoryMemberProperties("userAccountControl").Value

                If DirectoryMember.SchemaClassName = "group" Then
                    ' this is a group.                        
                    ListADGroupMembers(DirectoryItem)
                End If

                If DirectoryMember.SchemaClassName = "user" Then
                    ' this is a user.
                    If Nothing IsNot DirectoryItem Then
                        If AccEnabled(uac) = 1 Then ' check the ad account is enabled
                            GroupMembers.Add(DirectoryItem.ToString())
                            ListBox1.Items.Add(DirectoryItem.ToString() & " " & DirectoryPhone)

                            ADUSers(0, n) = DirectoryItem.ToString()
                            ADUSers(1, n) = DirectoryPhone
                            n += 1
                            ReDim Preserve ADUSers(1, n)
                        End If
                    End If
                End If

            Next GroupMemberDN

        Next DirectorySearchResult
    Catch ex As Exception
        MsgBox(ex.Message)
    End Try

    Return GroupMembers



End Function




  ' check account is active or not.
Function AccEnabled(ByVal uac As String) As String

    Dim aret As Integer = 0
    Select Case uac
        Case 512 'Enabled 
            aret = 1
        Case 514 ': ACCOUNTDISABLE()
            aret = 0
        Case 528 ': Enabled(-LOCKOUT)
            aret = 1
        Case 530 ': ACCOUNTDISABLE(-LOCKOUT)
            aret = 0
        Case 544 ': Enabled(-PASSWD_NOTREQD)
            aret = 1
        Case 546 ': ACCOUNTDISABLE(-PASSWD_NOTREQD)
            aret = 0
        Case 560 ': Enabled(-PASSWD_NOTREQD - LOCKOUT)
            aret = 1
        Case 640 ': Enabled(-ENCRYPTED_TEXT_PWD_ALLOWED)
            aret = 1
        Case 2048 ' : INTERDOMAIN_TRUST_ACCOUNT()
            aret = 1
        Case 2080 ': INTERDOMAIN_TRUST_ACCOUNT(-PASSWD_NOTREQD)
            aret = 1
        Case 4096 ': WORKSTATION_TRUST_ACCOUNT()
            aret = 1
        Case 8192 ': SERVER_TRUST_ACCOUNT()
            aret = 1
        Case 66048 ': Enabled(-DONT_EXPIRE_PASSWORD)
            aret = 1
        Case 66050 ': ACCOUNTDISABLE(-DONT_EXPIRE_PASSWORD)
            aret = 0
        Case 66064 ': Enabled(-DONT_EXPIRE_PASSWORD - LOCKOUT)
            aret = 1
        Case 66066 ': ACCOUNTDISABLE(-DONT_EXPIRE_PASSWORD - LOCKOUT)
            aret = 0
        Case 66080 ': Enabled(-DONT_EXPIRE_PASSWORD - PASSWD_NOTREQD)
            aret = 1
        Case 66082 ': ACCOUNTDISABLE(-DONT_EXPIRE_PASSWORD - PASSWD_NOTREQD)
            aret = 0
        Case 66176 ': Enabled(-DONT_EXPIRE_PASSWORD - ENCRYPTED_TEXT_PWD_ALLOWED)
            aret = 1
        Case 131584 ': Enabled(-MNS_LOGON_ACCOUNT)
            aret = 1
        Case 131586 ': ACCOUNTDISABLE(-MNS_LOGON_ACCOUNT)
            aret = 0
        Case 131600 ': Enabled(-MNS_LOGON_ACCOUNT - LOCKOUT)
            aret = 1
        Case 197120 ': Enabled(-MNS_LOGON_ACCOUNT - DONT_EXPIRE_PASSWORD)
            aret = 1
        Case 532480 'SERVER_TRUST_ACCOUNT - TRUSTED_FOR_DELEGATION (Domain Controller) 
            aret = 1
        Case 1049088 ': Enabled(-NOT_DELEGATED)
            aret = 1
        Case 1049090 ': ACCOUNTDISABLE(-NOT_DELEGATED)
            aret = 0
        Case 2097664 ': Enabled(-USE_DES_KEY_ONLY)
            aret = 1
        Case 2687488 ': Enabled(-DONT_EXPIRE_PASSWORD - TRUSTED_FOR_DELEGATION - USE_DES_KEY_ONLY)
            aret = 1
        Case 4194816 ': Enabled(-DONT_REQ_PREAUTH)
            aret = 1
        Case Else
            aret = 0
    End Select

    AccEnabled = aret

End Function
Himanshu
  • 31,810
  • 31
  • 111
  • 133
3

Try this:

' set up domain context
Dim ctx As New PrincipalContext(ContextType.Domain)

' find a user
Dim user As UserPrincipal = UserPrincipal.FindByIdentity(ctx, "SomeUserName")

If user IsNot Nothing Then
    Dim groupMemberships = user.GetAuthorizationGroups()

            ' do something with group....
    For Each gp As GroupPrincipal In groupMemberships
    Next
End If

You should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:

The new S.DS.AM makes it really easy to play around with users and groups in AD!

The method .GetAuthorizationGroups() will do a recursive search - so you should get all groups a user is member of - directly or indirectly.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Cool--I'll try this out. But don't I have to define the LDAP path? Or does this assume that I'm using ``...? – Octavient Oct 25 '12 at 12:37
  • I'm getting this error: BC30002: Type 'PrincipalContext' is not defined. I've included this in web.config: `` According to MSDN, the PrincipalContext Class is part of this AccountManagement class. Are there other namespaces/classes I need to include? – Octavient Oct 25 '12 at 12:47
  • Did you see my recent comment re: PrincipalContext error? Any ideas? – Octavient Oct 29 '12 at 23:52
  • @Octavient: have you included the using statement in your code-behind file where you're using `PrincipalContext`, too? Just adding the assembly isn't enough ... – marc_s Oct 30 '12 at 05:42
  • I've added an update to my original post, with more specific detail. Hoping you're willing to help me see this through...? – Octavient Nov 02 '12 at 15:29
  • @Octavient: yes, but I don't know what's going on - from all I see, I don't understand why it would complain about `PrincipalContext` being not defined ... I'm at a loss (and also not very fluent in VB.NET) – marc_s Nov 05 '12 at 16:30
0
Imports System.Security.Principal

Private Function GetGroups(userName As String) As List(Of String)
    Dim result As New List(Of String)
    Dim wi As WindowsIdentity = New WindowsIdentity(userName)

    For Each group As IdentityReference In wi.Groups
        Try
            result.Add(group.Translate(GetType(NTAccount)).ToString())
        Catch ex As Exception
        End Try
    Next

    result.Sort()
    Return result
End Function

So just use GetGroups("userID"). Because this approach uses the SID of the user, no explicit LDAP call is done. If you use your own user name it will use the cached credentials and so this function is very fast.

The Try Catch is necessary because in large companyies the AD is so big that some SIDs are getting lost in space.

see https://stackoverflow.com/a/19403297/2886367

Community
  • 1
  • 1
Mickey Mouse
  • 723
  • 6
  • 4