How to get all the AD groups for a particular user? - c#

I checked this post already. But it doesn't answer my question. I want to get all the active directory groups in which a particular user is a member.
I've written the following code. But I'm not able to proceed further as I don't know how to give the filter and how to access the properties.
class Program
{
static void Main(string[] args)
{
DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com");
DirectorySearcher searcher = new DirectorySearcher(de);
searcher.Filter = "(&(ObjectClass=group))";
searcher.PropertiesToLoad.Add("distinguishedName");
searcher.PropertiesToLoad.Add("sAMAccountName");
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("objectSid");
SearchResultCollection results = searcher.FindAll();
int i = 1;
foreach (SearchResult res in results)
{
Console.WriteLine("Result" + Convert.ToString(i++));
DisplayProperties("distinguishedName", res);
DisplayProperties("sAMAccouontName", res);
DisplayProperties("name", res);
DisplayProperties("objectSid", res);
Console.WriteLine();
}
Console.ReadKey();
}
private static void DisplayProperties(string property, SearchResult res)
{
Console.WriteLine("\t" + property);
ResultPropertyValueCollection col = res.Properties[property];
foreach (object o in col)
{
Console.WriteLine("\t\t" + o.ToString());
}
}
}
Any ideas?

You should use System.DirectoryServices.AccountManagement. It's much easier. Here is a nice code project article giving you an overview on all the classes in this DLL.
As you pointed out, your current approach doesn't find out the primary group. Actually, it's much worse than you thought. There are some more cases that it doesn't work, like the domain local group from another domain. You can check here for details. Here is how the code looks like if you switch to use System.DirectoryServices.AccountManagement. The following code can find the immediate groups this user assigned to, which includes the primary group.
UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
Console.Out.WriteLine(group);
}

Use tokenGroups:
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
SearchResult sr = ds.FindOne();
DirectoryEntry user = sr.GetDirectoryEntry();
user.RefreshCache(new string[] { "tokenGroups" });
for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
//do something with the SID or name (nt.Value)
}
Note: this only gets security groups

Just query the "memberOf" property and iterate though the return, example:
search.PropertiesToLoad.Add("memberOf");
StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
String dn;
int equalsIndex, commaIndex;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
dn = (String)result.Properties["memberOf"][propertyCounter];
equalsIndex = dn.IndexOf("=", 1);
commaIndex = dn.IndexOf(",", 1);
if (-1 == equalsIndex)
{
return null;
}
groupNames.Append(dn.Substring((equalsIndex + 1),
(commaIndex - equalsIndex) - 1));
groupNames.Append("|");
}
return groupNames.ToString();
This just stuffs the group names into the groupNames string, pipe delimited, but when you spin through you can do whatever you want with them

This code works even faster (two 1.5 faster than my previous version):
public List<String> GetUserGroups(WindowsIdentity identity)
{
List<String> groups = new List<String>();
String userName = identity.Name;
int pos = userName.IndexOf(#"\");
if (pos > 0) userName = userName.Substring(pos + 1);
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov
DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
SearchResultCollection results = search.FindAll();
foreach (SearchResult sr in results)
{
GetUserGroupsRecursive(groups, sr, de);
}
return groups;
}
public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
{
if (sr == null) return;
String group = (String)sr.Properties["cn"][0];
if (String.IsNullOrEmpty(group))
{
group = (String)sr.Properties["samaccountname"][0];
}
if (!groups.Contains(group))
{
groups.Add(group);
}
DirectorySearcher search;
SearchResult sr1;
String name;
int equalsIndex, commaIndex;
foreach (String dn in sr.Properties["memberof"])
{
equalsIndex = dn.IndexOf("=", 1);
if (equalsIndex > 0)
{
commaIndex = dn.IndexOf(",", equalsIndex + 1);
name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
sr1 = search.FindOne();
GetUserGroupsRecursive(groups, sr1, de);
}
}
}

This is how I list all the groups (direct and indirect) for a specific Distinguished Name:
The string 1.2.840.113556.1.4.1941 specifies LDAP_MATCHING_RULE_IN_CHAIN.
This rule is limited to filters that apply to the DN. This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.
This method is 25 times faster than the UserPrincipal.GetGroups() method in my testing.
Note: The primary group (typically Domain Users) is not returned by this or GetGroups() method. To get the primary group name too, I've confirmed this method works.
Additionally, I found this list of LDAP filters extremely useful.
private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
var groups = new List<string>();
if (!string.IsNullOrEmpty(distinguishedName))
{
var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName}))";
using (DirectorySearcher dirSearch = new DirectorySearcher(domainDirectoryEntry))
{
dirSearch.Filter = getGroupsFilterForDn;
dirSearch.PropertiesToLoad.Add("name");
using (var results = dirSearch.FindAll())
{
foreach (SearchResult result in results)
{
if (result.Properties.Contains("name"))
groups.Add((string)result.Properties["name"][0]);
}
}
}
}
return groups;
}

The following example is from the Code Project article, (Almost) Everything In Active Directory via C#:
// userDn is a Distinguished Name such as:
// "LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com"
public ArrayList Groups(string userDn, bool recursive)
{
ArrayList groupMemberships = new ArrayList();
return AttributeValuesMultiString("memberOf", userDn,
groupMemberships, recursive);
}
public ArrayList AttributeValuesMultiString(string attributeName,
string objectDn, ArrayList valuesCollection, bool recursive)
{
DirectoryEntry ent = new DirectoryEntry(objectDn);
PropertyValueCollection ValueCollection = ent.Properties[attributeName];
IEnumerator en = ValueCollection.GetEnumerator();
while (en.MoveNext())
{
if (en.Current != null)
{
if (!valuesCollection.Contains(en.Current.ToString()))
{
valuesCollection.Add(en.Current.ToString());
if (recursive)
{
AttributeValuesMultiString(attributeName, "LDAP://" +
en.Current.ToString(), valuesCollection, true);
}
}
}
}
ent.Close();
ent.Dispose();
return valuesCollection;
}
Just call the Groups method with the Distinguished Name for the user, and pass in the bool flag to indicate if you want to include nested / child groups memberships in your resulting ArrayList:
ArrayList groups = Groups("LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com", true);
foreach (string groupName in groups)
{
Console.WriteLine(groupName);
}
If you need to do any serious level of Active Directory programming in .NET I highly recommend bookmarking & reviewing the Code Project article I mentioned above.

Here is the code that worked for me:
public ArrayList GetBBGroups(WindowsIdentity identity)
{
ArrayList groups = new ArrayList();
try
{
String userName = identity.Name;
int pos = userName.IndexOf(#"\");
if (pos > 0) userName = userName.Substring(pos + 1);
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName);
DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("cn");
String name;
SearchResultCollection results = search.FindAll();
foreach (SearchResult result in results)
{
name = (String)result.Properties["samaccountname"][0];
if (String.IsNullOrEmpty(name))
{
name = (String)result.Properties["cn"][0];
}
GetGroupsRecursive(groups, de, name);
}
}
catch
{
// return an empty list...
}
return groups;
}
public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn)
{
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))";
search.PropertiesToLoad.Add("memberof");
String group, name;
SearchResult result = search.FindOne();
if (result == null) return;
group = #"RIOMC\" + dn;
if (!groups.Contains(group))
{
groups.Add(group);
}
if (result.Properties["memberof"].Count == 0) return;
int equalsIndex, commaIndex;
foreach (String dn1 in result.Properties["memberof"])
{
equalsIndex = dn1.IndexOf("=", 1);
if (equalsIndex > 0)
{
commaIndex = dn1.IndexOf(",", equalsIndex + 1);
name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
GetGroupsRecursive(groups, de, name);
}
}
}
I measured it's performance in a loop of 200 runs against the code that uses the AttributeValuesMultiString recursive method; and it worked 1.3 times faster.
It might be so because of our AD settings. Both snippets gave the same result though.

I would like to say that Microsoft LDAP has some special ways to search recursively for all of memberships of a user.
The Matching Rule you can specify for the "member" attribute. In particular, using the Microsoft Exclusive LDAP_MATCHING_RULE_IN_CHAIN rule for "member" attribute allows recursive/nested membership searching. The rule is used when you add it after the member attribute. Ex. (member:1.2.840.113556.1.4.1941:= XXXXX )
For the same Domain as the Account, The filter can use <SID=S-1-5-21-XXXXXXXXXXXXXXXXXXXXXXX> instead of an Accounts DistinguishedName attribute which is very handy to use cross domain if needed. HOWEVER it appears you need to use the ForeignSecurityPrincipal <GUID=YYYY> as it will not resolve your SID as it appears the <SID=> tag does not consider ForeignSecurityPrincipal object type. You can use the ForeignSecurityPrincipal DistinguishedName as well.
Using this knowledge, you can LDAP query those hard to get memberships, such as the "Domain Local" groups an Account is a member of but unless you looked at the members of the group, you wouldn't know if user was a member.
//Get Direct+Indirect Memberships of User (where SID is XXXXXX)
string str = "(& (objectCategory=group)(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
//Get Direct+Indirect **Domain Local** Memberships of User (where SID is XXXXXX)
string str2 = "(& (objectCategory=group)(|(groupType=-2147483644)(groupType=4))(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
//TAA DAA
Feel free to try these LDAP queries after substituting the SID of a user you want to retrieve all group memberships of. I figure this is similiar if not the same query as what the PowerShell Command Get-ADPrincipalGroupMembership uses behind the scenes. The command states "If you want to search for local groups in another domain, use the ResourceContextServer parameter to specify the alternate server in the other domain."
If you are familiar enough with C# and Active Directory, you should know how to perform an LDAP search using the LDAP queries provided.
Additional Documentation:
<SID> Binding String
<GUID> Binding String

If you have a LDAP connection with a username and password to connect to Active Directory, here is the code I used to connect properly:
using System.DirectoryServices.AccountManagement;
// ...
// Connection information
var connectionString = "LDAP://domain.com/DC=domain,DC=com";
var connectionUsername = "your_ad_username";
var connectionPassword = "your_ad_password";
// Get groups for this user
var username = "myusername";
// Split the LDAP Uri
var uri = new Uri(connectionString);
var host = uri.Host;
var container = uri.Segments.Count() >=1 ? uri.Segments[1] : "";
// Create context to connect to AD
var princContext = new PrincipalContext(ContextType.Domain, host, container, connectionUsername, connectionPassword);
// Get User
UserPrincipal user = UserPrincipal.FindByIdentity(princContext, IdentityType.SamAccountName, username);
// Browse user's groups
foreach (GroupPrincipal group in user.GetGroups())
{
Console.Out.WriteLine(group.Name);
}

there is a helpers class based in curtisk response:
public static class ActiveDirectoryHelpers
{
private static readonly Regex keyValuePair = new Regex($"(?<key>[^=]+)=(?<value>[^,]+),?");
public enum X500DirectorySpecification
{
/// <summary>Common Name</summary>
CN,
/// <summary>Organizational Unit</summary>
OU,
/// <summary>Domain Component</summary>
DC
}
public static IEnumerable<string> GetUserMemberOfNodeValue(this PrincipalContext principalContext, string userName, X500DirectorySpecification node)
{
return principalContext.GetUserMemberOf(userName)
.SelectMany(memberOf =>
GetUserMemberOfKeyValues(memberOf).Where(item => item.Key == node.ToString()).Select(item => item.Value));
}
private static IEnumerable<string> GetUserMemberOf(this PrincipalContext principalContext, string userName)
{
using var user = UserPrincipal.FindByIdentity(principalContext, userName);
IEnumerable<string> result = null;
if (user != null)
{
var directoryEntry = (DirectoryEntry)user.GetUnderlyingObject();
var directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.PropertiesToLoad.Add("memberOf");
result = directorySearcher.FindOne().Properties["memberOf"].Cast<string>();
}
return result ?? Enumerable.Empty<string>();
}
private static IEnumerable<KeyValuePair<string, string>> GetUserMemberOfKeyValues(string memberOfValue)
{
return keyValuePair.Matches(memberOfValue).OfType<Match>()
.Select(item => new KeyValuePair<string, string>(item.Groups["key"].Value.Trim(), item.Groups["value"].Value));
}
}

PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, "DomainName", UserAccountOU, UserName, Password);
UserPrincipal UserPrincipalID = UserPrincipal.FindByIdentity(pc1, IdentityType.SamAccountName, UserID);
searcher.Filter = "(&(ObjectClass=group)(member = " + UserPrincipalID.DistinguishedName + "));

Related

Get email addresses from distribution list using c#

Update:
For me, LDAP way only worked for finding email addresses inside AD groups, for eg: named ITSolutionDeliveryDevelopers group. NOT inside Exchange Distribution Lists, for eg: named abc#domainname.com.
// I was able to figure out entry as suggested by #Gabriel Luci and
// all of the following possible formats worked for me:
// ngroupnet.com is my company domain name.
var entry = new DirectoryEntry();
var entry = new DirectoryEntry("LDAP://ngroupnet.com");
var entry = new DirectoryEntry("LDAP://ngroupnet.com", "MyAccountUsername", "MyAccountPassword");
var entry = new DirectoryEntry("LDAP://ngroupnet.com", "MyName#mycompany.com", "MyEmailAccountPassword");
For my complete answer, take a look below: https://stackoverflow.com/a/71518937/8644294
Original Question:
What is the best way to get all the individual email addresses comprising an exchange distribution list?
For eg: I have this distribution list called abc#domainname.com that has email addresses:
a#domainname.com
b#domainname.com
c#domainname.com
Now I need to get these addresses using C# code.
I found solution using LDAP but I felt it'd be a hassle to figure out LDAP path to my Active Directory.
// How do I get this LDAP Path, username and password?
// Is the username and password service account credentials of the app?
// And do they need to be registered in AD?
var entry = new DirectoryEntry("LDAP Path");//, username, password);
LDAP Way:
public static List<string> GetDistributionListMembers(string dlName = "abc#domainname.com")
{
var result = new List<string>();
try
{
// How do I get this LDAP Path?
var entry = new DirectoryEntry("LDAP Path");//, username, password);
var search = new DirectorySearcher(entry);
search.Filter = $"CN={dlName}";
int i = search.Filter.Length;
string str = "", str1 = "";
foreach (SearchResult AdObj in search.FindAll())
{
foreach (String objName in AdObj.GetDirectoryEntry().Properties["member"])
{
str += Convert.ToString(objName) + "<Br>";
int selIndex = objName.IndexOf("CN=") + 3;
int selEnd = objName.IndexOf(",OU") - 3;
str1 += objName.Substring(selIndex, selEnd).Replace("\\", "");
DirectorySearcher dsSearch = new DirectorySearcher(entry);
dsSearch.Filter = "CN=" + objName.Substring(selIndex, selEnd).Replace("\\", "");
foreach (SearchResult rs in dsSearch.FindAll())
{
//str1 += "<p align='right'><font face='calibri' color='#2266aa' size=2>" + Convert.ToString(rs.GetDirectoryEntry().Properties["mail"].Value) + "|" + Convert.ToString(rs.GetDirectoryEntry().Properties["displayName"].Value) + "|" + Convert.ToString(rs.GetDirectoryEntry().Properties["sAMAccountName"].Value) + "|" + Convert.ToString(rs.GetDirectoryEntry().Properties["department"].Value) + "|" + Convert.ToString(rs.GetDirectoryEntry().Properties["memberOf"].Value) + "</font></p>";
str1 = Convert.ToString(rs.GetDirectoryEntry().Properties["mail"].Value);
result.Add(str1);
}
}
}
return result;
}
catch (Exception ex)
{
//Do some logging or what have you.
throw;
}
}
So I just went with the EWS route.
EWS Way:
public static static List<string> GetDistributionListMembers(string dlName = "abc#domainname.com")
{
try
{
var service = new ExchangeService();
var cred = new WebCredentials("sharedmailbox#domain.com", "some_password");
service.Credentials = cred;
service.Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
var expandedEmailAddresses = new List<string>();
ExpandGroupResults myGroupMembers = service.ExpandGroup(dlName);
foreach (EmailAddress address in myGroupMembers.Members)
{
expandedEmailAddresses.Add(address.Address);
}
return expandedEmailAddresses;
}
catch (Exception ex)
{
// The DL doesn't have any members. Handle it how you want.
// Handle/ Log other errors.
}
}
Is EWS approach a good way?
If Yes, then I'm good. If not, I'll have to figure out that LDAP path.
Or if there's even a better way, please let me know.
If the computer you run this from is joined to the same domain as the group you're looking for, then you don't need to figure out the LDAP path. You can just do:
var search = new DirectorySearcher();
If your computer is not joined to the same domain, then you just use the domain name:
var entry = new DirectoryEntry("LDAP://domainname.com");
This requires that there is no firewall blocking port 389 between your computer and the domain controller(s). If you need to pass credentials, then do that:
var entry = new DirectoryEntry("LDAP://domainname.com", username, password);
The credentials can be any user on the domain.
That said, there are a lot of inefficiencies in your code that will make it run much slower than needed. I wrote an article about this that can help you update your code: Active Directory: Better Performance
Is EWS approach a good way?
If it works, it works. I'm not an expert on EWS (although I have used it), but I'm fairly certain that's using Basic Authentication, which is going to be disabled in October.
If all the Mailboxes are on Office365 then i would suggest you use the Graph API instead eg https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=http . There are several advantage in terms of security eg you could use Application permissions and all you need is access to the directory while if you did the same thing in EWS it would require full access to at least one mailbox.
LDAP will be best performing of the 3 if ultimate speed in the only thing that is important.
My complete solution for future reference. :)
EWS Way - For expanding Exchange Distribution Lists
public class SomeHelper
{
private static ExchangeService _exchangeService = null;
public static async Task<HashSet<string>> GetExchangeDistributionListMembersAsync(IEnumerable<string> dlNames)
{
var allEmailAddresses = new HashSet<string>();
foreach (var dlName in dlNames)
{
if (!SomeCache.TryGetCachedItem(dlName, out var dlMembers))
{
var groupEmailAddresses = new List<string>();
var exchangeService = await GetExchangeServiceAsync();
try
{
var myGroupMembers = exchangeService.ExpandGroup(dlName);
// Add the group members.
foreach (var address in myGroupMembers.Members)
{
groupEmailAddresses.Add(address.Address);
}
}
catch (Exception ex)
{
//If it can't expand the dlName, just return it.
groupEmailAddresses.Add(dlName);
//groupEmailAddresses.Add($"Attempting to expand '{dlName}' resulted in error message: '{ex.Message}'.");
}
// Cache the groupEmailAddresses for 7 days.
// Because Distribution Lists rarely change and expanding DL is an expensive operation.- AshishK Notes
SomeCache.AddItemToCache(dlName, groupEmailAddresses, 10080);
allEmailAddresses.UnionWith(groupEmailAddresses);
}
else
{
allEmailAddresses.UnionWith((List<string>)dlMembers);
}
}
return allEmailAddresses;
}
private static async Task<ExchangeService> GetExchangeServiceAsync()
{
if (_exchangeService == null)
{
_exchangeService = new ExchangeService();
var exchangeUrl = "https://outlook.office365.com/ews/exchange.asmx";
var cred = new WebCredentials("sharedmailbox#domain.com", "some_password");
_exchangeService.Credentials = cred;
//_exchangeService.AutodiscoverUrl("sharedmailbox#domain.com");
_exchangeService.Url = new Uri(exchangeUrl);
_exchangeService.TraceEnabled = true;
_exchangeService.TraceFlags = TraceFlags.All;
return _exchangeService;
}
else
{
return _exchangeService;
}
}
}
public class SomeCache
{
private static readonly ObjectCache _cache = MemoryCache.Default;
public static void AddItemToCache(string key, object itemToAdd, int cacheDurationMinutes)
{
var _policy = new CacheItemPolicy
{
Priority = CacheItemPriority.Default,
AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(cacheDurationMinutes)
};
_cache.Set(key, itemToAdd, _policy);
}
public static bool TryGetCachedItem(string key, out object cachedObject)
{
try
{
cachedObject = _cache[key] as object;
}
catch (Exception ex)
{
cachedObject = null;
}
return !(cachedObject == null);
}
}
LDAP Way - For expanding Active Directory Groups
public static List<string> GetADGroupDistributionListMembers(string adGroupName)
{
var returnResult = new List<string>();
var entry = GetDirectoryEntry();
DirectorySearcher groupSearch = new DirectorySearcher(entry)
{
Filter = "(SAMAccountName=" + adGroupName + ")"
};
groupSearch.PropertiesToLoad.Add("member");
SearchResult groupResult = groupSearch.FindOne(); // getting members who belong to the adGroupName
if (groupResult != null)
{
for (int iSearchLoop = 0; iSearchLoop < groupResult.Properties["member"].Count; iSearchLoop++)
{
string userName = groupResult.Properties["member"][iSearchLoop].ToString();
int index = userName.IndexOf(',');
userName = userName.Substring(0, index).Replace("CN=", "").ToString(); // the name of the user will be fetched.
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(name=" + userName + ")"
};
search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne(); //finding the mail id
if (result != null)
{
returnResult.Add(result.Properties["mail"][0].ToString());
}
}
}
return returnResult;
}
public static DirectoryEntry GetDirectoryEntry()
{
DirectoryEntry entryRoot = new DirectoryEntry("LDAP://RootDSE");
string Domain = (string)entryRoot.Properties["defaultNamingContext"][0];
DirectoryEntry de = new DirectoryEntry
{
Path = "LDAP://" + Domain,
AuthenticationType = AuthenticationTypes.Secure
};
return de;
}

Active Directory reading user attributes

I am trying to let my winform system to authonticate using username of the person in active directory. i am using now the following code. But the result is null !!
private static string LDAP_Connection = "corp.mycompany.global";
private static string LDAP_Path = "LDAP://OU=USERS,OU=BT,OU=EC,OU=tres,DC=corp,DC=company,DC=global";
static DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
DirectoryEntry ldapConnection = new DirectoryEntry(LDAP_Connection);
ldapConnection.Path = LDAP_Path;
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
public static void RetreiveUserInfoAdvanced()
{
try
{
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
// create search object which operates on LDAP connection object
// and set search object to only find the user specified
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
//search.Filter = "(mail =" + _userlogin + ")";
search.Filter = "mail = a.ghew#mycompany.com";
// create results objects from search object
//SearchResult result = search.FindOne();
string[] requiredProperties = new string[] { "cn", "mail" };
foreach (String property in requiredProperties)
search.PropertiesToLoad.Add(property);
SearchResult result = search.FindOne();
if (result != null)
{
foreach (String property in requiredProperties)
foreach (Object myCollection in result.Properties[property])
Console.WriteLine(String.Format("{0,-20} : {1}", property, myCollection.ToString()));
}
}
}
i used Ad Explorer with the same data, everything is find an working fine and i can reach the required data. But from my system can't.
I don't have your AD environment, but I did the following in a similar configuration:
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(mail=a.ghew#mycompany.com)";
search.SearchScope = SearchScope.Subtree;
Give that a go? Basically remove your whitespace in the filter expression and ensure you have traversal enabled.

Search for a Local user in a local group that does not have Foreign Security policy

Basically I found a post that has a solution for a problem we are having in our application and the solution was:
private static void listGroupMembers(string groupDistinguishedName, PrincipalContext ctx, List<UserPrincipal> users)
{
DirectoryEntry group = new DirectoryEntry("LDAP://" + groupDistinguishedName);
foreach (string dn in group.Properties["member"])
{
DirectoryEntry gpMemberEntry = new DirectoryEntry("LDAP://" + dn);
System.DirectoryServices.PropertyCollection userProps = gpMemberEntry.Properties;
object[] objCls = (userProps["objectClass"].Value) as object[];
if (objCls.Contains("group"))
listGroupMembers(userProps["distinguishedName"].Value as string, ctx, users);
if (!objCls.Contains("foreignSecurityPrincipal"))
{
UserPrincipal u = UserPrincipal.FindByIdentity(ctx, IdentityType.DistinguishedName, dn);
if(u!=null) // u==null for any other types except users
users.Add(u);
}
}
}
However I am trying to search a Local group so if I change the directory entry to say:
DirectoryEntry groupEntry =
new DirectoryEntry(string.Format("WinNT://{0}/{1},group", Environment.MachineName, groupName));
Then it doesn't work and it says that the property doesn't exist. How can I do the above but for a local group and user?
Basically to fix this I ended up doing:
protected bool IsUserInLocalGroup(string userName, string group)
{
using (DirectoryEntry computerEntry = new DirectoryEntry("WinNT://{0},computer".FormatWith(Environment.MachineName)))
using(DirectoryEntry groupEntry = computerEntry.Children.Find(group, "Group"))
{
foreach (object o in (IEnumerable)groupEntry.Invoke("Members"))
{
using (DirectoryEntry entry = new DirectoryEntry(o))
{
if (entry.SchemaClassName.Equals("User", StringComparison.OrdinalIgnoreCase) && entry.Name.Equals(userName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
}

get all users from a group in Active Directory

I'm trying to get all users of a particular group in AD, then return a list of Employees as mapped to properties in my Employee class. I have:
My Filter is producing no results - what should it be?
Also, I tried the first solution here:
List of users in specific Active Directory Distribution Group, but I need details such as mobile, extension, etc. which I couldn't get with that method.
public static List<Employee> CreateEmployeeList(string department)
{
List<Employee> employees = new List<Employee>();
string filter = string.Format("(&(ObjectClass=person)(memberOf=CN={0},OU=Users & Groups,OU=Blah,DC=Blah,DC=Blah,DC=Blah))", department);
DirectoryEntry adRoot = new DirectoryEntry("LDAP://" + domain, null, null, AuthenticationTypes.Secure);
DirectorySearcher searcher = new DirectorySearcher(adRoot);
searcher.SearchScope = SearchScope.Subtree;
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.Filter = filter;
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult user in results)
{
// do whatever you need to do with the entry
if (user != null)
{
UserDirectoryEntry = user.GetDirectoryEntry();
string displayName = GetUserProperty("displayName");
string firstName = GetUserProperty("givenName");
string lastName = GetUserProperty("sn");
string email = GetUserProperty("mail");
string tel = GetUserProperty("telephonenumber");
string extension = GetUserProperty("ipphone");
string mobile = GetUserProperty("mobile");
string title = GetUserProperty("description");
employees.Add(new Employee{ FullName = displayName, FirstName = firstName, Surname = lastName, Email = email.ToLower(), Telephone = tel, Extension = extension, Mobile = mobile, JobTitle = title });
}
}
return employees;
}
using (var context = new PrincipalContext(ContextType.Domain, "domainName"))
{
using (var group = GroupPrincipal.FindByIdentity(context, "groupName"))
{
if (group == null)
{
MessageBox.Show("Group does not exist");
}
else
{
var users = group.GetMembers(true);
foreach (UserPrincipal user in users)
{
//user variable has the details about the user
}
}
}
}
This should return all Active Directory Users in a group.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
namespace ADQuery
{
class Program
{
static void Main(string[] args)
{
GetListOfAdUsersByGroup("domain", "group");
Console.ReadLine();
}
public static void GetListOfAdUsersByGroup(string domainName, string groupName)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://DC=" + domainName + ",DC=com");
DirectorySearcher search = new DirectorySearcher(entry);
string query = "(&(objectCategory=person)(objectClass=user)(memberOf=*))";
search.Filter = query;
search.PropertiesToLoad.Add("memberOf");
search.PropertiesToLoad.Add("name");
System.DirectoryServices.SearchResultCollection mySearchResultColl = search.FindAll();
Console.WriteLine("Members of the {0} Group in the {1} Domain", groupName, domainName);
foreach (SearchResult result in mySearchResultColl)
{
foreach (string prop in result.Properties["memberOf"])
{
if (prop.Contains(groupName))
{
Console.WriteLine(" " + result.Properties["name"][0].ToString());
}
}
}
}
}
}
Good Luck!
The following code will search through nested domain local groups and/or global groups recursively to find users. You can modify this to look through any order of groups to suit what you need or to return any kind of group that you want.
// Set the list to return and get the group we are looking through.
List<UserPrincipal> list = new List<UserPrincipal>();
GroupPrincipal group = GroupPrincipal.FindByIdentity(new PrincipalContext(/* connection info here */), ((groupName.Length > 0) ? groupName : this.Properties.Name));
// For each member of the group add all Users.
foreach (Principal princ in group.Members)
{
/*
To change what you are looking for or how you are looking for it,
simply change some of the following conditions to match what you want.
*/
// If this member is a User then add them.
if (princ.StructuralObjectClass == "user")
{
list.Add(UserPrincipal.FindByIdentity(new PrincipalContext(/* connection info here */), princ.Name);
}
// If we are looking recursively and this member is a GL_Group then get the Users in it and add them.
if (recursive && (princ.StructuralObjectClass == "group") && (((GroupPrincipal)princ).GroupScope == GroupScope.Global))
{
list.AddRange(this.GetUsers(true, princ.Name));
}
}
return list;
Building on the example by Dalton, here's concise code to get group's usernames:
static SortedSet<string> GetUsernames(string domainName, string groupName) {
using (var pc = new PrincipalContext(ContextType.Domain, domainName))
using (var gp = GroupPrincipal.FindByIdentity(pc, groupName))
return gp == null ? null : new SortedSet<string>(
gp.GetMembers(true).Select(u => u.SamAccountName));
}
In this post I wrote something working in an ActiveDirectory 2003 ans 2008 R2. I use Microsoft LDAP_MATCHING_RULE_IN_CHAIN. This service use DirectoryServices. Be careful in this code as there is a double search.
But you can also do it using Managing Directory Security Principals in the .NET Framework 3.5. You can read this other post. You have to get a GroupPrincipal and you are looking for Members property. It also exists other entries in StackOverflow.

Getting manager email id from active directory

How to get user manager email id from active directory? I have written code with which I can get user's firstname, lastname, email id and his manager name based on userid, but I want to get manager email id along with his manager name.
Can somebody please help me how to get this? Here is my code:
protected void ddlAdsuser_SelectedIndexChanged(object sender, EventArgs e)
{
DirectoryEntry root = new DirectoryEntry("LDAP://RootDSE");
string myDomain = root.Properties["defaultNamingContext"].Value.ToString();
DirectoryEntry domain = new DirectoryEntry("LDAP://" + myDomain);
DirectorySearcher dsUsers = new DirectorySearcher(domain);
dsUsers.Filter = "(userPrincipalName=" + ddlAdsuser.Text + ")";
foreach (SearchResult sResultSet in dsUsers.FindAll())
{
lblfname.Text = GetProperty(sResultSet, "givenName");
lbllname.Text = GetProperty(sResultSet, "sn");
lblemail.Text = GetProperty(sResultSet, "mail");
string Manager = string.Empty;
Manager = GetProperty(sResultSet, "manager");
if (Manager != "")
{
if (Manager.Contains("CN="))
{
int Length = Manager.IndexOf(',');
Manager = Manager.Substring(3, Length - 3);
}
else
{
Manager = string.Empty;
}
}
lblManagerID.Text = Manager; //Here displaying the manager name.
}
}
public static string GetProperty(SearchResult searchResult, string PropertyName)
{
if (searchResult.Properties.Contains(PropertyName))
{
return searchResult.Properties[PropertyName][0].ToString();
}
else
{
return string.Empty;
}
}
DirectorySearcher objDirSearch = new DirectorySearcher(SearchRoot);
DirectoryEntry dentUser = null;
string pstrFieldName, pstrValue;
pstrFieldName = "company";
pstrValue = "12345"; //Employee number
/*setting the filter as per the employee number*/
objDirSearch.Filter = "(&(objectClass=user)(" + pstrFieldName + "=" + pstrValue + "))";
SearchResult objResults = objDirectorySearch.FindOne();
dentUser = new DirectoryEntry(objResults.Path);}
string strManager = dentUser.Properties["manager"].Value.ToString();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.DistinguishedName, strManager);
string strManagerMailID = user.EmailAddress;
Simple code and working great:
public static string GetEmail(string userId)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, userId);
return user.EmailAddress;
}
You must add assembly System.DirectoryServices.AccountManagement.dll.
If you have any troubles with connection to AD, you can try to add AD server name in PrincipalContext constructor.
Just do a second search for the manager.
Note that you way of building the query filter is buggy, you need to escape some characters (Especially the " quote) in order to avoid broken queries depending on user input.

Categories

Resources