We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you a podcast all about Citrix Workspace, moving to the cloud, and analytics & intelligence. Episode 2 coming soon!Listen Now

x

How to Get a List of Active Directory Members in a Distribution List with C#?

atomicstorm
atomicstorm asked
on
Medium Priority
6,196 Views
Last Modified: 2013-12-24
I have been searching through EE and the internet looking for a way to get a list of active directory members based on user input of the specific distribution list.  I've tried many different  combinations; however, all I get is a -532xxxxxxx COM error or 1000+ results (the test distribution list has 1 member in it).  I have attached the code I have so far.

NOTE: I have blacked out the AD location and I understand I am not filtering on anything but I apparently cannot get the LDAP query right.  I am new to LDAP.
Program.cs
--------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
 
namespace ExactTarget_ADFTP
{
    class Program
    {
        static string GetProperty(SearchResult searchResult, string PropertyName)
        {
            if(searchResult.Properties.Contains(PropertyName))
            {
                return searchResult.Properties[PropertyName][0].ToString() ;
            }
            else
            {
                return string.Empty;
            }
        }
 
        static void Main(string[] args)
        {
            XXXXX.BusinessLogic.ExactTarget_ADFTP.FetchAD oLDAP = new XXXXX.BusinessLogic.ExactTarget_ADFTP.FetchAD();
 
            int iCount = 0;
            Console.Write("What is the distribution list name? ");
            string input = Console.ReadLine();
 
            Console.WriteLine("");
            Console.WriteLine(String.Concat("Fetching Information for ", input, "..."));
 
            SearchResultCollection LDAPResultCollection = oLDAP.GetUserInfo(input);
 
            try
            {
                foreach (SearchResult LDAPResult in LDAPResultCollection)
                {
                    iCount += 1;
                    Console.WriteLine(GetProperty(LDAPResult, "cn")); // 
                    Console.WriteLine(GetProperty(LDAPResult, "givenName")); // First
                    Console.WriteLine(GetProperty(LDAPResult, "sn")); // Last
                    Console.WriteLine(GetProperty(LDAPResult, "mail")); // Email Address
                    Console.WriteLine(GetProperty(LDAPResult, "DistinguishedName")); // Group?
                }
            }
            catch (System.Exception)
            {
            }
 
            Console.Write("Program Ended, Count: " + iCount);
            input = Console.ReadLine();
        }
}
 
FetchAD.cs
-----------------------
#region "Imports"
using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
using System.Data;
#endregion
 
namespace XXXXX.BusinessLogic.ExactTarget_ADFTP
{
    public class FetchAD
    {
        protected String SPLDAPADDRESS = "xxxxxx.xxxxxx.net";
 
        public SearchResultCollection GetUserInfo(string Distribution)
        {
            try
            {
                string LDAPAddress = String.Format("LDAP://{0}", SPLAPADDRESS);
                DirectoryEntry deLDAP = new DirectoryEntry(LDAPAddress);
                deLDAP.AuthenticationType = AuthenticationTypes.Delegation;
                
                DirectorySearcher mySearcher = new DirectorySearcher(deLDAP, "(objectClass=person)");
 
                return mySearcher.FindAll();
            }
            catch (System.Exception)
            {
                return null;
            }
        }
    }
}

Open in new window

Comment
Watch Question

Anurag ThakurTechnical Manager

Commented:
the following ee link shows how to export it to an excel
may be you can find how to export to List
http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_21311589.html
PowerShell Developer
CERTIFIED EXPERT
Top Expert 2010
Commented:

You have two parts to this...

1. Convert the Group Name provided by the user to a Distinguished Name. When you consider this, you must also consider whether or not the group name you're given is accurate and unique. This is required because you cannot construct a filter based on just the group name, and returning all users then searching membership afterwards is excessively wasteful.

2. Return the members of the group. Either by enumerating the "member" attribute or by searching the directory for users that are a "memberOf" the distinguishedName returned above.

I'm not a programmer, so I'll stay clear of modifying the code you have above. However, I do have some samples of what I'm suggesting, those are below. There are two separate subroutines dropped here, it won't just copy and paste then work but do let me know if any of what I've done doesn't make sense.

Chris
// LDAP filter to find objects where sAMAccountName or name matches the value specified.
// txtObject is a field on the form, read as input.
String ldapFilter = "(&(|(sAMAccountName=" + txtObject.Text + ")(name=" + txtObject.Text + "))" +
  "(|(&(objectClass=user)(objectCategory=person))(objectClass=group)))";
 
// Connection to the current AD domain. Only works if we're running this on a domain member
DirectoryEntry adDomain = new DirectoryEntry();
DirectorySearcher adSearch = new DirectorySearcher(adDomain, ldapFilter);
adSearch.PageSize = 1000;
adSearch.PropertiesToLoad.Add("distinguishedName");
 
// Find a single result, hopefully this is unique. Demonstrative only.
SearchResult adSearchResult = adSearch.FindOne();
 
lblError.Text = "";
try {
  txtObject.Text = adSearchResult.Properties["distinguishedname"][0].ToString();
} catch {
  lblError.Text = "Failed to find account in directory";
}
 
// Enumeration of members by searching memberOf attribute
 
// LDAP filter to return all objects where memberOf contains objectDN (a specific group)
String ldapFilter = "(memberOf=" + objectDN + ")";
 
DirectoryEntry adDomain = new DirectoryEntry();
DirectorySearcher adSearch = new DirectorySearcher(adDomain, ldapFilter);
adSearch.PageSize = 1000;
adSearch.PropertiesToLoad.AddRange(new String[] {"name", "distinguishedName"});
SearchResultCollection adSearchResults = adSearch.FindAll();
 
// ADObjectData is a struct (not defined here). 
// ADSearchData is a List (result is bound to a DataGridView)
foreach (SearchResult adSearchResult in adSearchResults) {
  ADObjectData ADObject = new ADObjectData();
  ADObject.name = adSearchResult.Properties["name"][0].ToString();
  ADObject.dn = adSearchResult.Properties["distinguishedname"][0].ToString();
  ADSearchData.Add(ADObject);
}

Open in new window

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
Was just cruising by and saw this question.  I realise this isn't a direct answer to your question but it does work and does list all users in a requested Active Directory - it might give you a good place to start.

If it helps please post a comment on the article - it's my site.  :)

http://digitalformula.net/net-geekery/follow-up-list-active-directory-users-this-time-in-c/
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:

Hey Number5ix :)

I have a few comments if I may, hopefully you don't mind :)

> DirectoryEntry de = resEnt.GetDirectoryEntry();

If you're just listing members you do not need to create a directory entry for each user. It is more efficient (costs less) to retrieve the value from the SearchResult, especially important if we're looking at large result sets.

> "(objectClass=user)"

Because of how Classes are defined this filter will return Computer objects as well (if they appear within the specified sub tree). "(&(objectClass=user)(objectCategory=person))" will return just users, where just "(objectCategory=person)" will return users and contact objects.

Paging: You'll need it enabled if you expect to return more than 1000 results in the query.

Returned attributes, again only applies for large(r) result sets, but it is generally sensible to restrict the Properties loaded into the SearchResult to only those that are actually necessary.

Chris
Nope I don't mind at all - they're good tips.  Thanks!  :)
@Chris-Dent:

The objectCategory=person thing works well.  Your comment about not needing to create a DirectoryEntry is interesting though.  On looking at my code again, how would you do it?  Once you've got the SearchResult objects from mySearcher.FindAll() how do you iterate through each of them and get the properties without creating a DirectoryEntry

I've tried the code snippet below but it's WAY slower than my original version ...
// Repeating this line for each property:
 
string emailAddress = resEnt.GetDirectoryEntry().Properties["Mail"].Value.ToString();
 
// is way slower than doing this for each object in mySearcher.FindAll():
 
DirectoryEntry de = resent.GetDirectoryEntry();
string emailAddress = de.Properties["Mail"].Value.ToString();
string givenName = de.Properties["givenName"].Value.ToString();

Open in new window

Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:

You're still getting the Directory Entry though, that's the bit that's slow :)

Instead, we can pull the values directly from resEnt (as below).

The properties loaded into the result can be explicitly set with:

mySearcher.PropertiesToLoad.Add("attributename");

Or:

mySearcher.PropertiesToLoad.AddRange(new String[] {"mail", "givenname"});

The idea being to reduce the cost of the search by returning only attributes we're interested in seeing.

Chris
string emailAddress = resEnt.Properties["mail"][0].ToString();
givenName = resEnt.Properties["givenname"][0].ToString();

Open in new window

Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:

Just noticed I missed the variable type for the second command, but I'm sure you'll catch that anyway :)

Chris

Author

Commented:
I did something completely different but your solution guided me in the right direction.
Very handy, thanks Chris.  It's nothing to do with what you wrote but it's interesting how the same code returns a different number of users from AD if converted to VB.NET ...
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:

Really? It shouldn't make any difference if both are just using .NET classes, I normally test in both languages.

Chris
Yeah, it surprised me too.  C# returns 439 users in the AD I'm testing on but VB only returns 200 or so.
Chris DentPowerShell Developer
CERTIFIED EXPERT
Top Expert 2010

Commented:

Hmmm I can't provide an answer for that, I've never noticed a difference when I've been testing things. At least not unless I go and make a mistake :)

Almost tempted to get you to try it in PowerShell as well, that can use the .NET framework as well :)

Chris
Hehe yep, it's probably something I've done but I haven't bothered trying to fix it yet.  PowerShell rocks!  I do a ton of it here at work although to be honest I'm battling with loading an external DLL into PowerShell through reflection at the moment lol
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.