How best to recursively query AD Group membership in ASP.NET (using vb)
Solution 1
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
Solution 2
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:
- Managing Directory Security Principals in the .NET Framework 3.5
- MSDN docs on System.DirectoryServices.AccountManagement
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.
Related videos on Youtube
Octavient
Updated on September 15, 2022Comments
-
Octavient over 1 year
I'm trying to find the simplest way to query Active Directory, in one of two ways:
Given an AD username, find all the groups (INCLUDING nested groups) that the user is a member of.
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" />
orImports 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 over 11 yearsCool--I'll try this out. But don't I have to define the LDAP path? Or does this assume that I'm using
<authentication mode="Windows" />
...? -
Octavient over 11 yearsI'm getting this error: BC30002: Type 'PrincipalContext' is not defined. I've included this in web.config:
<add namespace="System.DirectoryServices.AccountManagement" />
According to MSDN, the PrincipalContext Class is part of this AccountManagement class. Are there other namespaces/classes I need to include? -
Octavient over 11 yearsDid you see my recent comment re: PrincipalContext error? Any ideas?
-
marc_s over 11 years@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 ... -
Octavient over 11 yearsI've added an update to my original post, with more specific detail. Hoping you're willing to help me see this through...?
-
marc_s over 11 years@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) -
Octavient over 11 yearsROCK ON. Thank you so much--your code helped me out tremendously. Well done.