I am trying to print users of only certain OUs in my company AD.
So far I've come up with this:
string groupName = "Domain Users";
string domainName = "domain";
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName);
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, groupName);
if (grp != null)
{
foreach (Principal p in grp.GetMembers(false))
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
grp.Dispose();
ctx.Dispose();
Console.ReadLine();
}
else
{
Console.WriteLine("\nWe did not find that group in that domain, perhaps the group resides in a different domain?");
Console.ReadLine();
}
The problem is that it prints every user and not a specific OU like "Employees" or "Students".
How do I add a parameter to specify 1 or 2 OU's that it should cycle through instead of a group?
If you want to limit your search to a single OU / container, you can just bind to that using another PrincipalContext constructor:
string groupName = "Domain Users";
string domainName = "domain";
string ouName = "CN=Users,DC=yourcompany,DC=com";
// bind to the specified container you want
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName, ouName);
Then, of course, you're only searching in that CN=Users container - no where else.
I don't know your code exactly so this is pseudocode.
When you have some property e.g. a string to specify the type then use Where:
var groupName = "Student";
foreach (Principal p in grp.GetMembers(false).Where(princ => princ.OUName.Equals(groupName))
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
Or is it some kind of inheritance then OfType can be used:
foreach (Principal p in grp.GetMembers(false).OfType<Student>())
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
You are searching the entire Domain, and "Domain Users" is probably not the OU you would wan´t, change the name of the variable and add:
string domainName = "Domain Users";
string groupName = "Students";
Then add the OU to your PrincipalContext:
var ctx = new PrincipalContext(ContextType.Domain, domainName, groupName);
I use the following code in my Application.
This might be a bit overkill for you, but i think it mostly fits your needs.
public static void DoStuff(UserPrincipal princ) {
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain => {
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
searcher.Filter = $"(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{princ.SamAccountName}*)))";
return searcher;
});
var directoryEntriesFound =
allSearcher.SelectMany(searcher =>
searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var memberOf = directoryEntriesFound.Select(entry => {
using (entry) {
return new {
Name = entry.Name,
GroupName = ((object[])entry.Properties["MemberOf"].Value)
.Select(obj => obj.ToString())
};
}
}
);
var result1 = new List<string>();
foreach (var member in memberOf) {
if(member.GroupName.Contains("Student") )
Console.WriteLine(princ.SamAccountName + " is Student");
if (member.GroupName.Contains("Employee"))
Console.WriteLine(princ.SamAccountName + " is Employee");
}
}
Just call this in your foreach (Principal p in grp.GetMembers(false))
Related
I'm attempting to create a cascading drop down in which the first populates a list of Director level employees (directors have multiple managers and the managers have multiple employees) which would then populate the second dropdown through ajax. I'm trying to put together the logic that given a displayname will grab all direct reports and the direct reports of the direct reports. With the logic I've put together it will print everyone im looking for but only returns the reports to the original Director. I'm struggling to put everything together. The code isnt at all pretty and if there's an easier way to do this I'm open for suggestions.
There are three methods in which I'm attempting to populate the directReports list in the DirectReports method with all employees
public List<string> DirectReports(string name)
{
List<string> directReports = new List<string>();
var domain = new PrincipalContext(ContextType.Domain, "somedomain");
UserPrincipal user = new UserPrincipal(domain);
user.DisplayName = name;
PrincipalSearcher ps = new PrincipalSearcher();
ps.QueryFilter = user;
DirectorySearcher ds = ps.GetUnderlyingSearcher() as DirectorySearcher;
ds.SearchScope = SearchScope.Subtree;
ds.Filter = "(&(objectClass=user)(objectCategory=person)(displayName=" + name + "))";
ds.PropertiesToLoad.Add("DirectReports");
SearchResultCollection results = ds.FindAll();
foreach(SearchResult result in results)
{
if (hasDirectReports(result))
{
//directReports.AddRange(GetReportsFinal(GetDirectReportsList(result)));
directReports.AddRange(GetDirectReportsList(result));
directReports.Add(result.Properties["displayname"][0].ToString() + " (" + result.Properties["samaccountname"][0].ToString()+")");
}
else
{
directReports.Add(result.Properties["displayname"][0].ToString() + " (" + result.Properties["samaccountname"][0].ToString() + ")");
}
}
foreach(var item in directReports)
{
Debug.WriteLine(item);
}
return directReports;
}
public bool hasDirectReports(SearchResult result)
{
if(result.Properties["directreports"].Count >= 1)
{
return true;
}
else
{
return false;
}
}
public List<string> GetDirectReportsList(SearchResult result)
{
List<string> formattedDirectReportsList = new List<string>();
foreach (var thing in result.Properties["directreports"])
{
string employeeString = thing.ToString();
//employeeString = employeeString.Split(setp, StringSplitOptions.None)[0];
employeeString = employeeString.Replace("CN=", "");
employeeString = employeeString.TrimEnd(',');
employeeString = employeeString.Replace("\\, ", ", ");
//Debug.WriteLine(employeeString);
if (employeeString.Split(',')[2] == "OU=Users")
{
string fn = employeeString.Split(',')[1];
string ln = employeeString.Split(',')[0];
var domain = new PrincipalContext(ContextType.Domain, "somedomain");
UserPrincipal directreportname = new UserPrincipal(domain);
directreportname.Name = ln + "," + fn;
PrincipalSearcher prinsearcher = new PrincipalSearcher();
prinsearcher.QueryFilter = directreportname;
DirectorySearcher dirsearcher = prinsearcher.GetUnderlyingSearcher() as DirectorySearcher;
Principal reportResults = prinsearcher.FindOne();
formattedDirectReportsList.Add(reportResults.DisplayName + " (" + reportResults.SamAccountName + ")");
DirectReports(reportResults.DisplayName);
}
}
return formattedDirectReportsList;
}
I am looking to get a list of users that belong to a specific group 'groupName' is passed into the private method.
DirectoryEntry de = new DirectoryEntry("LDAP://DC=xxxx,DC=net"); // Root Directory //
var ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("SAMAccountName");
ds.PropertiesToLoad.Add("member");
ds.Filter = "(&(objectClass=group)(SAMAccountName=" + groupName + "))";
SearchResultCollection AllGroupUsers;
AllGroupUsers = ds.FindAll();
The query returns 3 properties :- adspath, accountName and member.
Member is what I am really after.I access the member property and its values as the following piece of code demonstrates:-
if (AllGroupUsers.Count > 0)
{
ResultPropertyValueCollection values = AllGroupUsers[0].Properties["member"];
but something strange happens here. On the RHS of the equal sign, AllGroupUsers has a value for a specific member as "CN=Mike Schoomaker R,........"
While on the LHS of the equal sign, values has "CN=Mike Schoomaker (OR),....."
I am not quite sure how this is possible... It doesn't happen for each and every value under AllGroupUsers... only thing I know is it happens for external users on the active directory... Can anyone show me how I can fix this and get the actual firstName, LastName and MiddleInitial ?
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find a user
using (var group = GroupPrincipal.FindByIdentity(ctx, "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
}
}
}
}
To get a user, not a group you should set DirectoryEntry object and use corresponding properties (e.g. displayName, sn, givenName, initials)
Example:
...
AllGroupUsers = ds.FindAll();
if (AllGroupUsers.Count > 0) {
ResultPropertyValueCollection values = AllGroupUsers[0].Properties["member"];
foreach (string s in values)
{
DirectoryEntry u = new DirectoryEntry("LDAP://" + s);
Console.WriteLine(u.Properties["displayName"].Value);
}
}
I need to display a list of users (in autocomplete) with similar names from the AD. For example, if I search Arthur and there are 2 people with same name (first name or last name), I should display both the names in the autocomplete. I tried to get the names but it is taking around 2-4 minutes to get the names. I am using the following code:
string[] domainNames = new string[] { "domain1", "domain2" };
List<string> userNames = new List<string>();
string user = string.Empty;
foreach (string domain in domainNames)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, "Domain Users");
if (group != null)
{
foreach (Principal p in group.GetMembers(false))
{
if (p.Name.ToLower().Contains(key.ToLower()))
{
if (!userNames.Contains(p.Name))
userNames.Add(p.Name);
}
}
}
}
}
Any way I can speed up the process? I am already using ajax call.
The cn= filter isn't really valid - there's nothing guaranteeing that format. Instead, look up Ambigous Name Resolution - it's designed for this: http://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx.
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = new DirectoryEntry("LDAP://" + domain, domain + #"\" + userName, password);
ds.Filter = "(&(objectClass=user)(cn=*" + key + "*))";
ds.PropertyNamesOnly = true;
ds.PropertiesToLoad.Add("name");
ds.PropertiesToLoad.Add("cn");
foreach (SearchResult searchResults in ds.FindAll())
{
foreach (string propertyName in searchResults.Properties.PropertyNames)
{
foreach (Object retEntry in searchResults.Properties[propertyName])
{
var user = retEntry.ToString().Split('/').Where(x => x.Contains("CN")).Select(y => y).FirstOrDefault().Split(',').Where(z => z.Contains("CN")).Select(c => c).FirstOrDefault().Split(',').FirstOrDefault().Split('=')[1];
if(!string.IsNullOrWhiteSpace(user))
userNames.Add(user);
}
}
}
Reduced to 30-40 seconds.
If I have the name of an Active Directory group, how can I get a list of the members or their email addresses?
void Main()
{
string groupName = "somegroup";
string domainName = "somedomain";
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName))
{
using(GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, groupName))
{
var sams = from x in grp.GetMembers(true) select new {x.SamAccountName, };
var users = from sam in sams.Distinct()
let usr = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam.SamAccountName)
select new { usr.SamAccountName, usr.DisplayName, usr.EmailAddress};
//do something with users...
}
}
}
in this url are a good and complete articule about AD with C# http://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#42, user are stored as DiretoryEntry review the property Properties
http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.properties(v=vs.100).aspx
The following code lists some, but not all, Active Directory Groups. Why?
I am trying to list all security groups, distribution groups, computer groups etc. Have I specified the wrong objectClass?
private static void ListGroups()
{
DirectoryEntry objADAM = default(DirectoryEntry);
DirectoryEntry objGroupEntry = default(DirectoryEntry);
DirectorySearcher objSearchADAM = default(DirectorySearcher);
SearchResultCollection objSearchResults = default(SearchResultCollection);
SearchResult myResult=null;
objADAM = new DirectoryEntry(LDAP);
objADAM.RefreshCache();
objSearchADAM = new DirectorySearcher(objADAM);
objSearchADAM.Filter = "(&(objectClass=group))";
objSearchADAM.SearchScope = SearchScope.Subtree;
objSearchResults = objSearchADAM.FindAll();
// Enumerate groups
try
{
fileGroups.AutoFlush = true;
if (objSearchResults.Count != 0)
{
foreach (SearchResult objResult in objSearchResults)
{
myResult = objResult;
objGroupEntry = objResult.GetDirectoryEntry();
Console.WriteLine(objGroupEntry.Name);
fileGroups.WriteLine(objGroupEntry.Name.Substring(3));
}
}
else
{
throw new Exception("No groups found");
}
}
catch (PrincipalException e)
{
fileErrorLog.AutoFlush = true;
fileErrorLog.WriteLine(e.Message + " " + myResult.Path);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
If you're on .NET 3.5 or newer, you can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a GroupPrincipal
GroupPrincipal qbeGroup = new GroupPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeGroup);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement
Try filter "(objectcategory=group)"
Found solution here
DirectoryEntry entry = new DirectoryEntry("ldap://ldap.gaurangjadia.com", "scott", "tiger");
DirectorySearcher dSearch = new DirectorySearcher(entry);
dSearch.Filter = "(&(objectClass=group))";
dSearch.SearchScope = SearchScope.Subtree;
SearchResultCollection results = dSearch.FindAll();
for (int i = 0; i < results.Count; i++) {
DirectoryEntry de = results[i].GetDirectoryEntry();
//TODO with "de"
}
I tried this and it worked
public ArrayList GetAllGroupNames(string ipAddress, string ouPath)
{
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = GetRootDirectoryEntry(ipAddress, ouPath);
deSearch.Filter = "(&(objectClass=group))";
SearchResultCollection results = deSearch.FindAll();
if (results.Count > 0)
{
ArrayList groupNames = new ArrayList();
foreach (SearchResult group in results)
{
var entry = new DirectoryEntry(group.Path, UserName, Password);
string shortName = entry.Name.Substring(3, entry.Name.Length - 3);
groupNames.Add(shortName);
}
return groupNames;
}
else
{
return new ArrayList();
}
}
private DirectoryEntry GetRootDirectoryEntry(string ipAddress, string domainPath, string username, string password)
{
var ldapPath = "LDAP://" + ipAddress + "/" + domainPath;
return new DirectoryEntry(ldapPath, username, password, AuthenticationTypes.Secure);
}
To retrieve a set of results that is larger than 1000 items, you must set SizeLimit to its default value (zero) and set PageSize to a value that is less than or equal to 1000.
objSearchADAM.PageSize = 1000;
you can get the all ad group details by the below powershell and if you want particular Name against of AD Group then write filter instead of *
Get-ADGroup -Filter * -properties * | Export-csv c:\csv\new.csv