OnError_Fix
asked on
Using ActiveDirectory to return a list of all users in a given group
Hi,
I've seen many solutions to this but none actually work for me. All I want is a nice simple function as trying to get my head around the namespace is beginning to wear me down!
Using VB.NET (not C#), I want a function that will accept a string for the group name, and a string for the server. I then want it to connect, and output the list of users in that group as a string (or whatever).
Can anyone help?
Thanks.
I've seen many solutions to this but none actually work for me. All I want is a nice simple function as trying to get my head around the namespace is beginning to wear me down!
Using VB.NET (not C#), I want a function that will accept a string for the group name, and a string for the server. I then want it to connect, and output the list of users in that group as a string (or whatever).
Can anyone help?
Thanks.
Use Directory Services API under System.DirectoryServices namespace is the proper way to access information stored in AD. The basic idea is that each group object in active directory has "member" attribute which stores the list of users that belong to the group.
Given a group name, you can query to AD using DirectorySearcher class to get the group object as follows
Private m_LdapPath As String = "LDAP://SERVER_NAME/..."
Private m_AdminAccountName As String = "Domain\UserName"
Private m_AdminPassword As String = "password"
Public Function GetUserRoles(ByVal groupName As String) As String()
Dim filter As String = String.Format("(&(objectCa tegory=gro up)(cn={0} ))", groupName)
Dim entry As New DirectoryEntry(m_LdapPath, m_AdminAccountName, m_AdminPassword, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher(entry, filter)
Dim users As New ArrayList
Try
Dim result As SearchResult = searcher.FindOne()
Dim member As Object = result.Properties("member" )
If IsNothing(member) Then
For Each dn As String In CType(member, IEnumerable)
users.Add(dn)
Next
' returns string array of users
Return CType(users.ToArray(GetTyp e(String)) , String())
End If
' returns empty string array
Return New String() {}
Catch
Throw
Finally
If Not IsNothing(entry) Then
entry.Dispose()
End If
If Not IsNothing(searcher) Then
searcher.Dispose()
End If
End Try
End Function
For you to read:
connection string format for ldap provider
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/adsi/adsi/ldap_adspath.asp
Notes: the code listed above uses AdminAccountName and AdminPassword password. You can however ignore the two parameters and pass as null (or empty string) value instead and the current logon user credentials will be used by .NET Framework to do the job. For any user account used make sure it has proper permission rights to query and view group information in Active Directory server.
Given a group name, you can query to AD using DirectorySearcher class to get the group object as follows
Private m_LdapPath As String = "LDAP://SERVER_NAME/..."
Private m_AdminAccountName As String = "Domain\UserName"
Private m_AdminPassword As String = "password"
Public Function GetUserRoles(ByVal groupName As String) As String()
Dim filter As String = String.Format("(&(objectCa
Dim entry As New DirectoryEntry(m_LdapPath,
Dim searcher As New DirectorySearcher(entry, filter)
Dim users As New ArrayList
Try
Dim result As SearchResult = searcher.FindOne()
Dim member As Object = result.Properties("member"
If IsNothing(member) Then
For Each dn As String In CType(member, IEnumerable)
users.Add(dn)
Next
' returns string array of users
Return CType(users.ToArray(GetTyp
End If
' returns empty string array
Return New String() {}
Catch
Throw
Finally
If Not IsNothing(entry) Then
entry.Dispose()
End If
If Not IsNothing(searcher) Then
searcher.Dispose()
End If
End Try
End Function
For you to read:
connection string format for ldap provider
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/adsi/adsi/ldap_adspath.asp
Notes: the code listed above uses AdminAccountName and AdminPassword password. You can however ignore the two parameters and pass as null (or empty string) value instead and the current logon user credentials will be used by .NET Framework to do the job. For any user account used make sure it has proper permission rights to query and view group information in Active Directory server.
ASKER
Hi iHenry,
Thanks for your post. This appears to be the closest I have come to getting what I need. That said however, when I run the code, I get an error message:
LabelSystem.Runtime.Intero pServices. COMExcepti on (0x80072020): An operations error occurred at System.DirectoryServices.D irectoryEn try.Bind(B oolean throwIfFail) at System.DirectoryServices.D irectoryEn try.Bind() at System.DirectoryServices.D irectoryEn try.get_Ad sObject() at System.DirectoryServices.D irectorySe archer.Fin dAll(Boole an findMoreThanOne) at System.DirectoryServices.D irectorySe archer.Fin dOne() at Incorpex.Olympia2004V1.Act iveDirecto ry.GetUser Roles(Stri ng groupName) in C:\Documents and Settings\r.parker\VSWebCac he\intrane t.domain.n et\adminis tration\Ac tiveDirect ory.vb:lin e 23,
Now --- ActiveDirectory.vb line 23 reads:
Dim result As SearchResult = searcher.FindOne()
And here are the values of the following strings:
Dim m_LdapPath As String = "LDAP://DEV"
Dim m_AdminAccountName As String = ""
Dim m_AdminPassword As String = ""
I have also tried "LDAP://DEV.LOCAL/CN=Users " as well, to produce the same error.
Any ideas?
Thanks.
P.S. Points value increased to 200.
Thanks for your post. This appears to be the closest I have come to getting what I need. That said however, when I run the code, I get an error message:
LabelSystem.Runtime.Intero
Now --- ActiveDirectory.vb line 23 reads:
Dim result As SearchResult = searcher.FindOne()
And here are the values of the following strings:
Dim m_LdapPath As String = "LDAP://DEV"
Dim m_AdminAccountName As String = ""
Dim m_AdminPassword As String = ""
I have also tried "LDAP://DEV.LOCAL/CN=Users
Any ideas?
Thanks.
P.S. Points value increased to 200.
ASKER
Hi,
I found this code on the web, but alas - it's all in C#. Would this code work if it were converted to VB.NET?
using System;
using System.DirectoryServices;
using System.Runtime.InteropServ ices;
using System.Reflection;
using activeds; // Import activeds.tlb (%windir%\system32\actived s.tlb)
class Test {
static string bindUser = "administrator"; // binding user with sufficient privileges
static string bindPwd = "hispwd"; // password of the binding user
static void ListUserAndGroups(string machineName)
{
DirectoryEntry _compContainer = new DirectoryEntry("WinNT://" + machineName + ",computer", bindUser, bindPwd);
try
{
foreach(DirectoryEntry de in _compContainer.Children)
{
switch (de.SchemaClassName.ToLowe r())
{
case "group":
Console.WriteLine("------- --- group - {0} ---------", de.Name);
ListMembersInGroup(de.Path );
break;
case "user":
Console.WriteLine("------- --- user - {0} ---------", de.Name);
ListUserProp(de.Path);
break;
default:
break;
}
}
}
finally {
_compContainer.Dispose();
}
}
private static void ListMembersInGroup(string dirPath) {
IADsMembers MembersCollection = null;
DirectoryEntry _groupEntry = new DirectoryEntry(dirPath ,bindUser, bindPwd);
try {
// call native method "members" on the IADsGroup COM interface exposed by activeds.dll
IADsGroup gr = _groupEntry.NativeObject as IADsGroup;
MembersCollection = gr.Members();
// or call Invoke on the DirectoryEntry object passing the Method to call as arg.
// cast the retruned object to IADsMembers
// MembersCollection = _groupEntry.Invoke("Member s") as IADsMembers;
object[] filter = {"user"};
MembersCollection.Filter = filter;
// enumerate members of collection object that supports the IADsMembers interface
// ADSI provider doesn't support count property!!
try {
foreach (IADsUser member in MembersCollection) {
Console.WriteLine("[{0}]", member.Name);
ListUserProp(member.ADsPat h);
}
}
catch (COMException e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
catch (COMException e) {
Console.WriteLine(e.Messag e);
}
finally {
_groupEntry.Dispose();
}
}
private static void ListUserProp(string dirPath) {
DirectoryEntry userEntry = null;
try {
userEntry = new DirectoryEntry(dirPath,bin dUser, bindPwd);
PropertyCollection pcoll = userEntry.Properties;
foreach(string sc in pcoll.PropertyNames)
Console.WriteLine("\t" + sc + "\t" + pcoll[sc].Value);
}
catch (COMException e) {
Console.WriteLine(e.Messag e);
}
finally
{
userEntry.Dispose();
}
}
public static void Main() {
ListUserAndGroups("scenic" );
}
}
I found this code on the web, but alas - it's all in C#. Would this code work if it were converted to VB.NET?
using System;
using System.DirectoryServices;
using System.Runtime.InteropServ
using System.Reflection;
using activeds; // Import activeds.tlb (%windir%\system32\actived
class Test {
static string bindUser = "administrator"; // binding user with sufficient privileges
static string bindPwd = "hispwd"; // password of the binding user
static void ListUserAndGroups(string machineName)
{
DirectoryEntry _compContainer = new DirectoryEntry("WinNT://" + machineName + ",computer", bindUser, bindPwd);
try
{
foreach(DirectoryEntry de in _compContainer.Children)
{
switch (de.SchemaClassName.ToLowe
{
case "group":
Console.WriteLine("-------
ListMembersInGroup(de.Path
break;
case "user":
Console.WriteLine("-------
ListUserProp(de.Path);
break;
default:
break;
}
}
}
finally {
_compContainer.Dispose();
}
}
private static void ListMembersInGroup(string dirPath) {
IADsMembers MembersCollection = null;
DirectoryEntry _groupEntry = new DirectoryEntry(dirPath ,bindUser, bindPwd);
try {
// call native method "members" on the IADsGroup COM interface exposed by activeds.dll
IADsGroup gr = _groupEntry.NativeObject as IADsGroup;
MembersCollection = gr.Members();
// or call Invoke on the DirectoryEntry object passing the Method to call as arg.
// cast the retruned object to IADsMembers
// MembersCollection = _groupEntry.Invoke("Member
object[] filter = {"user"};
MembersCollection.Filter = filter;
// enumerate members of collection object that supports the IADsMembers interface
// ADSI provider doesn't support count property!!
try {
foreach (IADsUser member in MembersCollection) {
Console.WriteLine("[{0}]",
ListUserProp(member.ADsPat
}
}
catch (COMException e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
catch (COMException e) {
Console.WriteLine(e.Messag
}
finally {
_groupEntry.Dispose();
}
}
private static void ListUserProp(string dirPath) {
DirectoryEntry userEntry = null;
try {
userEntry = new DirectoryEntry(dirPath,bin
PropertyCollection pcoll = userEntry.Properties;
foreach(string sc in pcoll.PropertyNames)
Console.WriteLine("\t" + sc + "\t" + pcoll[sc].Value);
}
catch (COMException e) {
Console.WriteLine(e.Messag
}
finally
{
userEntry.Dispose();
}
}
public static void Main() {
ListUserAndGroups("scenic"
}
}
>> I have also tried "LDAP://DEV.LOCAL/CN=Users " as well, to produce the same error.
Hm..are you sure the path is correct? do you really have an OU directly under you rootDSE?
Hm..are you sure the path is correct? do you really have an OU directly under you rootDSE?
ASKER
I may have this bit wrong then.... I've tried several combinations. It's basically the standard setup straight "out-of-the-box". I seem to be getting confused constructing the LDAP:// string, if that is where the error is?
What would you suggest I try to retrieve (for example) just the users in the Domain Admins group?
Server name: DEV.DOMAIN.LOCAL (dns)
Thanks
What would you suggest I try to retrieve (for example) just the users in the Domain Admins group?
Server name: DEV.DOMAIN.LOCAL (dns)
Thanks
Default AD instalation will have this kind of ldap path,
LDAP://<SERVER_NAME>:389/C N=Users,DC =<DOMAIN_N AME>,DC=lo cal
Or you can ignore the SERVER_NAME if you want to do server-less binding. That a common case when web server is part of the AD domain. And port number defaults to 389 (you can ignore this as well).
And user account on which the code is running also determine whether you need to supply user name and password. Are you using ASP.NET or just a console application?
LDAP://<SERVER_NAME>:389/C
Or you can ignore the SERVER_NAME if you want to do server-less binding. That a common case when web server is part of the AD domain. And port number defaults to 389 (you can ignore this as well).
And user account on which the code is running also determine whether you need to supply user name and password. Are you using ASP.NET or just a console application?
ASKER
ASP.NET using Windows Forms Authentication.
(Integrated Logon is not enabled).
(Integrated Logon is not enabled).
First, lets do some checking whether you can bind to AD in the first place. Run the code by commenting out line 3 and 4.
Then do the same thing with line 3 and 4.
Dim de As New DirectoryEntry() ' ln 1
de.Path = ldapPath ' ln 2
de.User = userName ' ln 3
de.Password = password ' ln 4
de.AuthenticationType = AuthenticationTypes.None ' ln 5
de.RefreshCache() ' ln 6
Do some combination of AuthenticationTypes enum, use ServerBind if SERVER_NAME is specified in ldapPath. Then, post your ldap path and any error message occurs.
Then do the same thing with line 3 and 4.
Dim de As New DirectoryEntry() ' ln 1
de.Path = ldapPath ' ln 2
de.User = userName ' ln 3
de.Password = password ' ln 4
de.AuthenticationType = AuthenticationTypes.None ' ln 5
de.RefreshCache() ' ln 6
Do some combination of AuthenticationTypes enum, use ServerBind if SERVER_NAME is specified in ldapPath. Then, post your ldap path and any error message occurs.
To make thing clearer, when using FormsAuthentication normally anonymous access in IIS is enabled and the app is running under IUSR_MACHINE security context. When enable anonymous access, the app is running under login user security context. So whatever user account used to bind to AD, it must be a domain user and has proper permission rights to view information in AD.
ASKER
Hi IHenry:
When using a blank username and password, with the LDAP string "" I get:
System.Runtime.InteropServ ices.COMEx ception (0x80072020): An operations error occurred at System.DirectoryServices.D irectoryEn try.Bind(B oolean throwIfFail) at System.DirectoryServices.D irectoryEn try.Bind() at System.DirectoryServices.D irectoryEn try.Refres hCache() at Incorpex.Olympia2004V1.Act iveDirecto ry.DebugAD () in C:\Documents and Settings\r.parker\VSWebCac he\intrane t.domain.n et\adminis tration\Ac tiveDirect ory.vb:lin e 25
When using the username and password of a local administrator account on the server (with the SAME LDAP string), I get:
System.Runtime.InteropServ ices.COMEx ception (0x8007202B): A referral was returned from the server at System.DirectoryServices.D irectoryEn try.Bind(B oolean throwIfFail) at System.DirectoryServices.D irectoryEn try.Bind() at System.DirectoryServices.D irectoryEn try.Refres hCache() at Incorpex.Olympia2004V1.Act iveDirecto ry.DebugAD () in C:\Documents and Settings\r.parker\VSWebCac he\intrane t.domain.n et\adminis tration\Ac tiveDirect ory.vb:lin e 25
NOTE: Line 25 is equal to Line 6 of the code you sent, that is: de.RefreshCache().
Back to the drawing board?!!!
Note: points increased to 300.
When using a blank username and password, with the LDAP string "" I get:
System.Runtime.InteropServ
When using the username and password of a local administrator account on the server (with the SAME LDAP string), I get:
System.Runtime.InteropServ
NOTE: Line 25 is equal to Line 6 of the code you sent, that is: de.RefreshCache().
Back to the drawing board?!!!
Note: points increased to 300.
ASKER
Of course it would help if I actually posted the LDAP String :)
It is:
"LDAP://DEV:389/CN=Users,D C=INCORPEX .LOCAL,DC= local"
;)
It is:
"LDAP://DEV:389/CN=Users,D
;)
ASKER
Hi iHenry:
Success?!
The following code DOES NOT produce any error:
Dim ldapPath As String = "LDAP://DEV/CN=Users,DC=xx xx,DC=loca l"
Dim username As String = "xxxxx"
Dim password As String = "xxxxx"
Dim de As New DirectoryEntry ' ln 1
de.Path = ldapPath ' ln 2
de.Username = username ' ln 3
de.Password = password ' ln 4
de.AuthenticationType = AuthenticationTypes.Server Bind ' ln 5
de.RefreshCache() ' ln 6
So I think I've found an LDAP String that works. Shall I plug this into the existing code you submitted before?
Success?!
The following code DOES NOT produce any error:
Dim ldapPath As String = "LDAP://DEV/CN=Users,DC=xx
Dim username As String = "xxxxx"
Dim password As String = "xxxxx"
Dim de As New DirectoryEntry ' ln 1
de.Path = ldapPath ' ln 2
de.Username = username ' ln 3
de.Password = password ' ln 4
de.AuthenticationType = AuthenticationTypes.Server
de.RefreshCache() ' ln 6
So I think I've found an LDAP String that works. Shall I plug this into the existing code you submitted before?
ASKER
Ok -- steaming along here.
Plugging the correct LDAP string into the code you gave me before now appears to NOT produce errors, but doesn't return any results.
Any ideas?
Plugging the correct LDAP string into the code you gave me before now appears to NOT produce errors, but doesn't return any results.
Any ideas?
Yea, it seems to be working..
Sorry, I was busy today.lots of problems.
One more things, include this line to make sure you can retrieve some attributes from the OU object
Dim dn As String = CType( de.Properties("distinguish edName").V alue, String )
Dim wc As DateTime = CType( de.Properties("whenCreated ").Value, DateTime )
If everything's fine, use the same ldapPath, userName, password and AuthenticationType to the original code.
Sorry, I was busy today.lots of problems.
One more things, include this line to make sure you can retrieve some attributes from the OU object
Dim dn As String = CType( de.Properties("distinguish
Dim wc As DateTime = CType( de.Properties("whenCreated
If everything's fine, use the same ldapPath, userName, password and AuthenticationType to the original code.
ASKER
Hi iHentry, not to worry about being busy!! We all are!
I've done what you have said, but it doesn't return any users - and doesn't give an error message. Any ideas?
I've done what you have said, but it doesn't return any users - and doesn't give an error message. Any ideas?
:o)
When do AD programming you might feel get intimidated with the error messages that do not give information about the nature of the failure. That's pretty much all you get with an LDAP bind.
When do AD programming you might feel get intimidated with the error messages that do not give information about the nature of the failure. That's pretty much all you get with an LDAP bind.
ASKER
True ---
The problem is no users are returned. Where do we go from here?
The problem is no users are returned. Where do we go from here?
Hm..can you do some debugging?
'-> check if the member var is not null (nothing)
Dim member As Object = result.Properties("member" )
If IsNothing(member) Then
'-> check if it gets into the loop
For Each dn As String In CType(member, IEnumerable)
users.Add(dn)
Next
'-> check number of element of the users array
' returns string array of users
Return CType(users.ToArray(GetTyp e(String)) , String())
End If
'-> check if the member var is not null (nothing)
Dim member As Object = result.Properties("member"
If IsNothing(member) Then
'-> check if it gets into the loop
For Each dn As String In CType(member, IEnumerable)
users.Add(dn)
Next
'-> check number of element of the users array
' returns string array of users
Return CType(users.ToArray(GetTyp
End If
One question,
are you sure you can find any group under the "Users" OU in “Active Directory Users and Computers” snap-ins?
-> LDAP://DEV/CN=Users,DC=xxx x,DC=local
are you sure you can find any group under the "Users" OU in “Active Directory Users and Computers” snap-ins?
-> LDAP://DEV/CN=Users,DC=xxx
ASKER
Hi,
If I change the groupname from "Users" to something else, it throws an error:
There is no such object on the server
at System.DirectoryServices.D irectoryEn try.Bind(B oolean throwIfFail) at System.DirectoryServices.D irectoryEn try.Bind() at System.DirectoryServices.D irectoryEn try.Refres hCache() at Incorpex.Olympia2004V1.Act iveDirecto ry.DebugAD () in C:\Documents and Settings\r.parker\VSWebCac he\intrane t.domain.n et\adminis tration\Ac tiveDirect ory.vb:lin e 25
Yet there is: I've tried changing the LDAP String to:
"LDAP://DEV/CN=Guests,DC=x xx,DC=loca l"
"LDAP://DEV/CN=Administrat ors,DC=xxx ,DC=local"
"LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local"
It only wants to execute if CN=Users. But if it does that, nothing is actually returned.
Let's say I wanted to view all the users who were a member of the "Administrators" group....
If I change the groupname from "Users" to something else, it throws an error:
There is no such object on the server
at System.DirectoryServices.D
Yet there is: I've tried changing the LDAP String to:
"LDAP://DEV/CN=Guests,DC=x
"LDAP://DEV/CN=Administrat
"LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local"
It only wants to execute if CN=Users. But if it does that, nothing is actually returned.
Let's say I wanted to view all the users who were a member of the "Administrators" group....
If I don't remember if there're ldap default path with names
LDAP://DEV/CN=Guests,DC=xx x,DC=local
LDAP://DEV/CN=Administrato rs,DC=xxx, DC=local or
LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local
I think that should be
LDAP://DEV/CN=USERS,CN=Gue sts,DC=xxx ,DC=local
LDAP://DEV/CN=USERS,CN=Adm inistrator s,DC=xxx,D C=local or
LDAP://DEV/CN=USERS,CN=Dom ain Admins,DC=xxx,DC=local
What's the Administrators group's parent container? sorry, I don't have access to AD now.
LDAP://DEV/CN=Guests,DC=xx
LDAP://DEV/CN=Administrato
LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local
I think that should be
LDAP://DEV/CN=USERS,CN=Gue
LDAP://DEV/CN=USERS,CN=Adm
LDAP://DEV/CN=USERS,CN=Dom
What's the Administrators group's parent container? sorry, I don't have access to AD now.
You can check using “Active Directory Users and Computers” console. Or do you have other utility to browse active directory object like ADSI viewer or ldap browser for windows (download for free). Such utilities can make your life much easier.
ASKER
The parent container for the Administrators group is "Builtin". I'm just trying to solve this now using the ldap browser program you suggested.
ASKER
Hi,
I am increasing the reward for this question to 750 points. I need an answer, and soon!
Points go to s/he who can give me a VB.NET function that has the server to bind to and the group name to find as parameters (strings), and search the Active Directory server for that group, and then return a list of all the group's members.
Thank you.
Note to iHenry: still no joy. I'm not having any luck here.
In ASP classic this was so simple. What happened in .NET?!
Thanks.
I am increasing the reward for this question to 750 points. I need an answer, and soon!
Points go to s/he who can give me a VB.NET function that has the server to bind to and the group name to find as parameters (strings), and search the Active Directory server for that group, and then return a list of all the group's members.
Thank you.
Note to iHenry: still no joy. I'm not having any luck here.
In ASP classic this was so simple. What happened in .NET?!
Thanks.
ASKER
Whoops - I cannot increase to 750 so I am setting to the max of 500.
I think you don't get the idea here, query groups to AD via DirectorySearcher should be the easiest way.
This is a user object, not a container object. A user object cannot contain any object and does not have "member" attribute.
LDAP://DEV/CN=Guests,DC=xx x,DC=local
These two are group objects, not container objects. A group object cannot contain any object and has "member" attribute.
LDAP://DEV/CN=Administrato rs,DC=xxx, DC=local
LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local
When you create a DirectorySearcher object the constructor needs two parameters. First, the DirectoryEntry which represents a node (a container) in the Active Directory hierarchy where the search starts.
Let say you are searching for Administrators group which is under a container with this dn format "CN=BuiltIn, DC=<domain>, DC=", so your ldapPath should looks something like this "LDAP://DEV/CN=BuiltIn,DC= xxx,DC=loc al"
And the query filter is the search criteria, so "(&(objectCategory=group)( cn=Adminis trator)" means you are searching in AD for any object with objectCategory as group and has name exactly match to "Administrator".
And finally, this is how to call the function
Dim groups As String = GetUserRoles("Administrato rs")
Furthermore, path of an AD object actually depends on your AD structure. So knowing your own structure is the first priority here.
Active Directory default groups naming is pretty well documented here..
http://www.microsoft.com/resources/documentation/WindowsServ/2003/all/techref/en-us/Default.asp?url=/Resources/Documentation/windowsserv/2003/all/techref/en-us/w2k3tr_princ_how.asp
This is a user object, not a container object. A user object cannot contain any object and does not have "member" attribute.
LDAP://DEV/CN=Guests,DC=xx
These two are group objects, not container objects. A group object cannot contain any object and has "member" attribute.
LDAP://DEV/CN=Administrato
LDAP://DEV/CN=Domain Admins,DC=xxx,DC=local
When you create a DirectorySearcher object the constructor needs two parameters. First, the DirectoryEntry which represents a node (a container) in the Active Directory hierarchy where the search starts.
Let say you are searching for Administrators group which is under a container with this dn format "CN=BuiltIn, DC=<domain>, DC=", so your ldapPath should looks something like this "LDAP://DEV/CN=BuiltIn,DC=
And the query filter is the search criteria, so "(&(objectCategory=group)(
And finally, this is how to call the function
Dim groups As String = GetUserRoles("Administrato
Furthermore, path of an AD object actually depends on your AD structure. So knowing your own structure is the first priority here.
Active Directory default groups naming is pretty well documented here..
http://www.microsoft.com/resources/documentation/WindowsServ/2003/all/techref/en-us/Default.asp?url=/Resources/Documentation/windowsserv/2003/all/techref/en-us/w2k3tr_princ_how.asp
ASKER
Right,
You're right: I don't quite understand the ins and outs of the DirectoryServices namespace.... hence my desire to find an answer here on EE.
I know how to CALL the function. It is the function itself which is returning an error for me. So my question is the same: who can write a simple function that will show me the users in the given group? I have tried the code you have sent me, and quite a few variations on it, and I either get an error message, or no users returned.
If you're able to continue trying I'd appreciate it.
Thank you.
You're right: I don't quite understand the ins and outs of the DirectoryServices namespace.... hence my desire to find an answer here on EE.
I know how to CALL the function. It is the function itself which is returning an error for me. So my question is the same: who can write a simple function that will show me the users in the given group? I have tried the code you have sent me, and quite a few variations on it, and I either get an error message, or no users returned.
If you're able to continue trying I'd appreciate it.
Thank you.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
iHenry,
No offence was taken so don't worry! I was getting quite frustrated; I thought the code you'd given me was OK but I was doing something wrong! Still, never mind, all that's history now because you've cracked it; it works brilliantly. I will award points to you. Just one mini question (still related) which you may be able to answer.
When the output comes back, it returns the users Full Name instead of their "username". For example:
CN=Test User,OU=HighRisk,DC=xxx,DC =local
"Test User" is actually the full name of the user (the concatenation of Firstname and Lastname), and the username should be "Test.User".
Any ideas?
If not no worries - I really appreciate your help so far.
No offence was taken so don't worry! I was getting quite frustrated; I thought the code you'd given me was OK but I was doing something wrong! Still, never mind, all that's history now because you've cracked it; it works brilliantly. I will award points to you. Just one mini question (still related) which you may be able to answer.
When the output comes back, it returns the users Full Name instead of their "username". For example:
CN=Test User,OU=HighRisk,DC=xxx,DC
"Test User" is actually the full name of the user (the concatenation of Firstname and Lastname), and the username should be "Test.User".
Any ideas?
If not no worries - I really appreciate your help so far.
The return value is actually "distinguishedName" attribute value of each user object.
CN=Test User,OU=HighRisk,DC=xxx,DC
The "Test User" is taken from cn attribute, it is not always as full name. That because it depends heavily on how the user object is created. For instance, you create one user object using the “Active Directory Users and Computers” console, the names default as follows. You specify the first name, initials, and last name of the user and it is displayed as <firstname> <initials>. <last name>. The source of confusion is when you create a user object programmatically, it is displayed as the cn attribute's value. You can overwrite this behaviour by set the cn attributes the same way as when you create a user via “Active Directory Users and Computers” console.
And also given a "distinguishedName" attribute you can get the associated object, example
Dim dn As String = "CN=white.trevor,CN=Users,
Dim de As New DirectoryEntry()
de.Path = "LDAP://THE_SERVER_NAME/" + dn
de.Username = userName
de.Password = password
de.RefreshCache()
Dim firstName = de.Properties("givenName")
Dim lastName = de.Properties("sn").Value
Just concatenate the two string whatever you like.
ASKER
At long last!
It's here. And it works. Thanks to the very kind member iHenry and all the support given to me from EE, I am posting here my final solution to the initial problem for the world to see. It's basically two functions. One which iterates through ActiveDirectory to return all the objects within a given DN. The second, for each DN given to it, binds to it, checks to see if it is a user, and then adds it to a list which can be used elsewhere.
Triumph!
Enjoy the code :)
Public Function GetUserGroups(ByVal groupName As String)
Dim ldapPath = "LDAP://" & ServerName & "/CN=" & CN & ",DC=" & DomainName & ",DC=" & DomainSuffix
Dim filter As String = String.Format("(&(objectCa tegory=gro up)(cn={0} ))", groupName)
Dim entry As New DirectoryEntry(ldapPath, adminUserName, adminPassword, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher(entry, filter)
Dim users As New ArrayList
Try
Dim result As SearchResult = searcher.FindOne()
Dim member As Object = result.Properties("member" )
If Not IsNothing(member) Then
For Each dn As String In CType(member, IEnumerable)
Dim sAMAccountName As String = GetAccountName(dn)
If Len(sAMAccountName) > 0 Then
users.Add(sAMAccountName)
End If
Next
' returns string array of users
Return CType(users.ToArray(GetTyp e(String)) , String())
End If
Catch e As Exception
Return e.ToString
End Try
entry.Dispose()
searcher.Dispose()
End Function
Public Function GetAccountName(ByVal DN As String)
Dim strOutput As String
Dim DE As New DirectoryEntry
DE.Path = "LDAP://" & ServerName & "/" & DN
DE.Username = AdminUsername
DE.Password = AdminPassword
DE.RefreshCache()
Try
For Each DV As String In CType(DE.Properties("objec tClass"), IEnumerable)
If DV = "user" Then
strOutput = DE.Properties("sAMAccountN ame").Valu e
End If
Next
If Not Len(strOutput) > 0 Then
Exit Function
End If
Catch ex As Exception
'TODO: Raise an error
strOutput = "There was an exception of some kind. It was: " & ex.ToString
End Try
Return strOutput
DE.Dispose()
End Function
It's here. And it works. Thanks to the very kind member iHenry and all the support given to me from EE, I am posting here my final solution to the initial problem for the world to see. It's basically two functions. One which iterates through ActiveDirectory to return all the objects within a given DN. The second, for each DN given to it, binds to it, checks to see if it is a user, and then adds it to a list which can be used elsewhere.
Triumph!
Enjoy the code :)
Public Function GetUserGroups(ByVal groupName As String)
Dim ldapPath = "LDAP://" & ServerName & "/CN=" & CN & ",DC=" & DomainName & ",DC=" & DomainSuffix
Dim filter As String = String.Format("(&(objectCa
Dim entry As New DirectoryEntry(ldapPath, adminUserName, adminPassword, AuthenticationTypes.None)
Dim searcher As New DirectorySearcher(entry, filter)
Dim users As New ArrayList
Try
Dim result As SearchResult = searcher.FindOne()
Dim member As Object = result.Properties("member"
If Not IsNothing(member) Then
For Each dn As String In CType(member, IEnumerable)
Dim sAMAccountName As String = GetAccountName(dn)
If Len(sAMAccountName) > 0 Then
users.Add(sAMAccountName)
End If
Next
' returns string array of users
Return CType(users.ToArray(GetTyp
End If
Catch e As Exception
Return e.ToString
End Try
entry.Dispose()
searcher.Dispose()
End Function
Public Function GetAccountName(ByVal DN As String)
Dim strOutput As String
Dim DE As New DirectoryEntry
DE.Path = "LDAP://" & ServerName & "/" & DN
DE.Username = AdminUsername
DE.Password = AdminPassword
DE.RefreshCache()
Try
For Each DV As String In CType(DE.Properties("objec
If DV = "user" Then
strOutput = DE.Properties("sAMAccountN
End If
Next
If Not Len(strOutput) > 0 Then
Exit Function
End If
Catch ex As Exception
'TODO: Raise an error
strOutput = "There was an exception of some kind. It was: " & ex.ToString
End Try
Return strOutput
DE.Dispose()
End Function
you are very welcome :0) and well done!
just two very trivial suggestions to the GetAccountName function,
put the DE.RefreshCache() method call in the try and catch block and use DE.SchemaClassName property to get the object class name.
just two very trivial suggestions to the GetAccountName function,
put the DE.RefreshCache() method call in the try and catch block and use DE.SchemaClassName property to get the object class name.
ASKER
iHenry, you deserve a medal mate :)
I've taken your suggestions and reorganised the function. It now reads:
Public Function GetAccountName(ByVal DN As String)
Dim strOutput As String
Dim DE As New DirectoryEntry
DE.Path = "LDAP://" & ServerName & "/" & DN
DE.Username = AdminUsername
DE.Password = AdminPassword
Try
DE.RefreshCache()
If DE.SchemaClassName = "user" Then
strOutput = DE.Properties("sAMAccountN ame").Valu e
End If
If Not Len(strOutput) > 0 Then
Exit Function
End If
Catch ex As Exception
'TODO: Raise an error
strOutput = "There was an exception and I died. It was: " & ex.ToString
End Try
Return strOutput
DE.Dispose()
End Function
And it even executes slightly faster, too.
NICE ONE!!
Thanks again.
I've taken your suggestions and reorganised the function. It now reads:
Public Function GetAccountName(ByVal DN As String)
Dim strOutput As String
Dim DE As New DirectoryEntry
DE.Path = "LDAP://" & ServerName & "/" & DN
DE.Username = AdminUsername
DE.Password = AdminPassword
Try
DE.RefreshCache()
If DE.SchemaClassName = "user" Then
strOutput = DE.Properties("sAMAccountN
End If
If Not Len(strOutput) > 0 Then
Exit Function
End If
Catch ex As Exception
'TODO: Raise an error
strOutput = "There was an exception and I died. It was: " & ex.ToString
End Try
Return strOutput
DE.Dispose()
End Function
And it even executes slightly faster, too.
NICE ONE!!
Thanks again.
This is a nice clean way to find them
Public Shared Function ADAllUsers(ByVal strGroupName As String) As ArrayList
' ** This will return Active Directory Info for a ALL USERS belonging to a GROUP
Dim adDS As DirectorySearcher = New DirectorySearcher("LDAP:// yourdomain .local") With {.Filter = "(&(objectClass=group)(cn= " & strGroupName & "))"}
Dim results As DirectoryServices.SearchRe sultCollec tion = adDS.FindAll()
Dim result As DirectoryServices.SearchRe sult
Dim WorkList As New ArrayList
If (results.Count > 0) Then
Dim strManipulateName As String
Dim intConstLocation As Int16
For Each result In results
For Each member As String In result.Properties("member" )
strManipulateName = member.Replace("CN=", "").Replace("\", "")
intConstLocation = InStr(strManipulateName, "OU=", CompareMethod.Text)
strManipulateName = Mid(strManipulateName, 1, intConstLocation - 2)
WorkList.Add(strManipulate Name)
Next
Next
Else
WorkList.Add("NOTHING FOUND!!")
End If
WorkList.Sort()
Return WorkList
End Function
Public Shared Function ADAllUsers(ByVal strGroupName As String) As ArrayList
' ** This will return Active Directory Info for a ALL USERS belonging to a GROUP
Dim adDS As DirectorySearcher = New DirectorySearcher("LDAP://
Dim results As DirectoryServices.SearchRe
Dim result As DirectoryServices.SearchRe
Dim WorkList As New ArrayList
If (results.Count > 0) Then
Dim strManipulateName As String
Dim intConstLocation As Int16
For Each result In results
For Each member As String In result.Properties("member"
strManipulateName = member.Replace("CN=", "").Replace("\", "")
intConstLocation = InStr(strManipulateName, "OU=", CompareMethod.Text)
strManipulateName = Mid(strManipulateName, 1, intConstLocation - 2)
WorkList.Add(strManipulate
Next
Next
Else
WorkList.Add("NOTHING FOUND!!")
End If
WorkList.Sort()
Return WorkList
End Function
https://www.experts-exchange.com/questions/21066352/How-can-you-loop-through-the-'Members-Of'-dynamically-down-multiple-'Tiers'-in-Active-Directory-if-you-dont-know-how-'deep'-the-Tiers-go.html