I have this code currently,
string defaultNamingContext;
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
defaultNamingContext = rootDSE.Properties["defaultNamingContext"].Value.ToString();
rootDSE = new DirectoryEntry("LDAP://" + defaultNamingContext);
//DirectoryEntry domain = new DirectoryEntry((string)"LDAP://" + defaultNamingContext);
DirectorySearcher ouSearch = new DirectorySearcher(rootDSE,"(objectCategory=Organizational-Unit)",
null, SearchScope.Subtree);
MessageBox.Show(rootDSE.Path.ToString());
try
{
SearchResultCollection collectedResult = ouSearch.FindAll();
foreach (SearchResult temp in collectedResult)
{
comboBox1.Items.Add(temp.Properties["name"][0]);
DirectoryEntry ou = temp.GetDirectoryEntry();
}
}
When i use the debugger i can see that rootDSE.Path is infact pointing to the right place, in this case DC=g-t-p,DC=Local but the directory searcher doesn't find any results. Can anyone help?
Stephen - my bad - for some reason, the search using objectCategory doesn't work.
Even though the objectCategory is displayed as CN=Organizational-Unit, for searching, you still need to use the same value as for the objectClass:
So try to use the filter (objectCategory=organizationalUnit) - that definitely works for me!
UPDATE: in order to get some properties in your search result (in order to display them in the combo box), you need to include those when you create the DirectorySearcher:
DirectorySearcher ouSearch = new DirectorySearcher(rootDSE);
ouSearch.Filter = "(objectCategory=Organizational-Unit)";
ouSearch.SearchScope = SearchScope.Subtree;
ouSearch.PropertiesToLoad.Add("name");
// add more properties if you want to ...
With this, you should definitely be able to grab the temp.Properties["name"][0] and stick it into the combobox's list of items.
I don't really see what you need the line
DirectoryEntry ou = temp.GetDirectoryEntry();
after grabbing the name property .....
Related
I regularly read out the active directory for an application to store the current state in a database.
To reduce the amount of data I use DirectorySynchronization to get only the changes since the last query.
But now I also need the attribute "memberOf" which is not provided by the DirectorySearcher when using DirectorySynchronization.
At the moment I get the DirectoryEntry for each found entry, but then more attributes are delivered than I need, which contradicts the actual intention.
Is there a way to set which attributes are read with DirectoryEntry, similar to PropertiesToLoad with DirectorySearcher or is there better way to read the attribute "memberOf" when using DirectorySynchronization?
Code excerpt:
using System.DirectoryServices;
DirectoryEntry searchRoot = new DirectoryEntry(domain, username,password, AuthenticationTypes.Secure);
DirectorySearcher dirSearch = new DirectorySearcher(searchRoot);
dirSearch.PropertiesToLoad.Add("samaccountname");
dirSearch.PropertiesToLoad.Add("distinguishedname");
dirSearch.PropertiesToLoad.Add("mail");
//filter to user objects
dirSearch.Filter = "(objectCategory=person)";
dirSearch.Filter = "(objectClass=user)";
byte[] cookie = null;
DirectorySynchronization sync = new DirectorySynchronization(DirectorySynchronizationOptions.ObjectSecurity,cookie);
dirSearch.DirectorySynchronization = sync;
using (searchRoot)
{
using (SearchResultCollection results = dirSearch.FindAll())
{
foreach (SearchResult result in results)
{
DirectoryEntry dirEntry = result.GetDirectoryEntry();
List<string> memberOf = new List<string>();
PropertyValueCollection prop = dirEntry.Properties["memberof"];
foreach(var member in prop)
{
memberOf.Add((string)member);
}
}
}
cookie = sync.GetDirectorySynchronizationCookie();
}
But now I also need the attribute "memberOf" which is not provided by the DirectorySearcher when using DirectorySynchronization.
memberOf is a special attribute computed by the directory, so it's not provided by DirectorySynchronization (which uses in fact DirSync control). The only thing that could provide DirectorySynchronization is the directory's real modification, which belongs to a user's DN added to a group object in the member attribute (which kind of triggers a memberOf recomputation)
At the moment I get the DirectoryEntry for each found entry, but then more attributes are delivered than I need, which contradicts the actual intention.
GetDirectoryEntry performs a new directory search and loads all the attributes (it has no link with the DirectorySearcher settings and previous search).
So, instead of the GetDirectoryEntry, you can use another DirectorySearcher (that will load just the memberOf) that will do a single search on each loop (example without any strong tests, ie. null values etc.):
using (searchRoot)
{
using (SearchResultCollection results = dirSearch.FindAll())
{
// A new DirectorySearcher
DirectorySearcher anotherDirectorySearcher = new DirectorySearcher(searchRoot);
anotherDirectorySearcher.PropertiesToLoad.Add("memberOf");
foreach (SearchResult result in results)
{
// A new filter on each loop in order to look for an single entry
anotherDirectorySearcher.Filter = $"(samaccountname={(string)result.Properties["samaccountname"][0]})";
List<string> memberOf = new List<string>();
foreach(var member in anotherDirectorySearcher.FindOne().Properties["memberOf"])
{
memberOf.Add((string)member);
}
}
}
cookie = sync.GetDirectorySynchronizationCookie();
}
Note that:
dirSearch.Filter = "(objectCategory=person)";
// Here you override the previous one (the Filter property is a string)
dirSearch.Filter = "(objectClass=user)";
// If you want both, you can use:
dirSearch.Filter = "(&(objectCategory=person)(objectClass=user))";
I'll try to explain myself better.
I'm using C# to build a mini-program to myself. I want to search a user within my active directory, but I want to search a user without a full display name. Let me explain myself.
For example, my display name can be: "David Holonka\Jeramy".
Is there a way to search the letters "lonka" and it'll find me all the users that has these combinations of letters within their display name?
My current code:
using (var pc = new PrinicpalContext(ContextType.Domain, "MyDomain"))
{
UserPrincipal user = new UserPrinicpal(pc);
User.DisplayName = "Holonka";
PrinicpalSearcher scrh = new PrinicpalSearcher(user);
Prinicpal found = scrh.FindOne();
}
}
Right now it doesn't find anything becasue there isn't a user that its display name is only "Holonka", but I want it to find the user that i've mentioned before
Thank you very much!
You can use query string in the PrinicpalSearcher:
UserPrincipal user = new UserPrinicpal(pc);
User.DisplayName = "*Holonka*";
PrinicpalSearcher searcher = new PrinicpalSearcher(user);
var results = searcher.FindAll();
You can also using PrincipalSearcher to find users with “or” parameters like in the example below:
List<UserPrincipal> searchPrinciples = new List<UserPrincipal>();
searchPrinciples.Add(new UserPrincipal(context) { DisplayName="*Holonka*"});
searchPrinciples.Add(new UserPrincipal(context) { SamAccountName = "*Holonka*" });
searchPrinciples.Add(new UserPrincipal(context) { MiddleName = "*Holonka*" });
searchPrinciples.Add(new UserPrincipal(context) { GivenName = "*Holonka*" });
List<Principal> results = new List<Principal>();
foreach (var item in searchPrinciples)
{
var searcher = new PrincipalSearcher(item);
// Results may contains duplicate values because of separate searchers can handle the same user
results.AddRange(searcher.FindAll());
}
You can also try using Ambiguous Name Resolution, which is a special AD query that looks for partial matches in several attributes (the list of those attributes is in that article).
You can't use PrincipalSearcher do use ANR though. You have to use DirectorySearcher (which is what PrincipalSearcher uses in the background anyway).
Here's an example:
var searchRoot = new DirectoryEntry("LDAP://domain.com");
var searcher = new DirectorySearcher(searchRoot) {
Filter = "(anr=Holonka)"
};
searcher.PropertiesToLoad.Add("displayName");
using (var results in = searcher.FindAll()) {
foreach (SearchResult result in results) {
if (result.Properties.Contains("displayName")) {
var displayName = (string) result.Properties["displayName"][0];
//do something else
}
}
}
When using DirectorySearcher, it's wise to use PropertiesToLoad. If you don't, it will return every attribute that has a value, which is likely way more data than you need. It's a waste of network traffic and time.
The using statement is also wise, since SearchResultCollection can't be completely cleaned up by the garbage collector.
I wrote some more about that in an article I wrote: Active Directory: Better Performance
I've found some examples here and there, but I can't seem to find one that addresses retrieving the managedby property from a computer record. (I don't know the username, but it's stored in the managedby)
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://"+lblDomain.Text);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = ("(objectClass=computer)");
search.PropertiesToLoad.Add("managedBy");
search.PropertiesToLoad.Add("distinguishedName");
search.PropertiesToLoad.Add("cn");
SearchResultCollection groups = search.FindAll();
foreach (SearchResult sr in groups)
{
if (sr.Properties.Contains("managedby"))
{
lblManagedBy.Text=(sr.Properties["managedBy"][0].ToString());
}
else
{
lblManagedBy.Text = "No owner specified in ManagedBy";
}
}
Your example works fine for me, so a few things to check.
1) Is the domain name correct in lblDomain.Text? Could you provide an example of what you are using for it? Fake names are fine.
2) Do you actually have computer accounts with the Managed By set? It is empty by default.
Other than that, I used you exact code and got back the DN of the user I assigned to a computer account, so it's probably in how you are connecting.
search.Filter = "(&(objectClass=computer)(cn=pcname))";
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://"+lblDomain.Text);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(objectClass=computer)(name=" + host + "))";
search.PropertiesToLoad.Add("managedBy");
search.PropertiesToLoad.Add("distinguishedName");
search.PropertiesToLoad.Add("cn");
SearchResultCollection groups = search.FindAll();
foreach (SearchResult sr in groups)
{
if (sr.Properties["managedBy"].Count > 0)
{
lblManagedBy.Text=(sr.Properties["managedBy"][0].ToString());
}
else
{
lblManagedBy.Text = "No owner specified in ManagedBy";
}
}
i want to get list of OU from Active Directory.
i have only domain name.
how can i achieve this using c#?
Try something like this:
// connect to "RootDSE" to find default naming context
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
string defaultContext = rootDSE.Properties["defaultNamingContext"][0].ToString();
// bind to default naming context - if you *know* where you want to bind to -
// you can just use that information right away
DirectoryEntry domainRoot = new DirectoryEntry("LDAP://" + defaultContext);
// set up directory searcher based on default naming context entry
DirectorySearcher ouSearcher = new DirectorySearcher(domainRoot);
// SearchScope: OneLevel = only immediate subordinates (top-level OUs);
// subtree = all OU's in the whole domain (can take **LONG** time!)
ouSearcher.SearchScope = SearchScope.OneLevel;
// ouSearcher.SearchScope = SearchScope.Subtree;
// define properties to load - here I just get the "OU" attribute, the name of the OU
ouSearcher.PropertiesToLoad.Add("ou");
// define filter - only select organizational units
ouSearcher.Filter = "(objectCategory=organizationalUnit)";
// do search and iterate over results
foreach (SearchResult deResult in ouSearcher.FindAll())
{
string ouName = deResult.Properties["ou"][0].ToString();
}
If you have a domain name (e.g. mycompany.com), then the LDAP root domain typically will be called dc=mycompany,dc=com - that's a convention, it doesn't have to be that way though. That's why I'm connecting to the LDAP://RootDSE virtual LDAP root and I read out the property Default Naming Context which gives me the default LDAP path.
If you know where you want to connect to - feel free to skip that first step and just provide the valid LDAP path (e.g. LDAP://dc=YourCompany,dc=co,dc=jp or whatever) to create the domainRoot directory entry.
Add a reference to System.DirectoryServices in the project
public static List<string> ListOu()
{
List<string> ous = new List<string>();
using (DirectoryEntry root = new DirectoryEntry("LDAP://dc=DOMAIN,dc=COM"))
{
DirectorySearcher searcher = new DirectorySearcher(root);
searcher.Filter = "(&(objectClass=organizationalUnit))";
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("distinguishedName");
var result = searcher.FindAll();
foreach (SearchResult entry in result)
{
ous.Add(entry.GetDirectoryEntry().Properties["distinguishedName"].Value.ToString());
}
result.Dispose();
searcher.Dispose();
}
return ous;
}
When I run this query
// Next row is used to login to AD
DirectoryEntry entry = GetEntry(domain, adminUser, adminPassword);
// Here starts the query
DirectorySearcher search = new DirectorySearcher(entry)
{
SearchScope = SearchScope.Subtree,
Filter = "(&" +
"(objectClass=user)" +
// "(distinguishedname=*OU=Ingegneria*)" +
"(givenname=s*)" +
"(samaccountname=*100)" +
")"
};
search.PropertiesToLoad.Add("distinguishedname");
SearchResultCollection result = search.FindAll();
I get six entries and that's correct.
All records, if I use record.GetDirectoryEntry() have
distinguishedname: CN=xxx,OU=Utenti,OU=Ingegneria,DC=xxx,DC=xxx
Anyway if I remove comment on distinguishedname part of the filter, I get zero entries!!
I also tried to use search.PropertiesToLoad.Add("distinguishedname"); without luck.
How can I search distinguishedname in filter?
UPDATE:
If I try to use "(distinguishedname=*)" + in filter , I still get six records, so I think I can search on distinguishedname...
UPDATE2:
I also tried to use code in Search Active Directory for an OU using a partial path to the OU:
Filter = "(&(objectClass=user)(ou=Ingegneria))";
but I have zero entries (I got two if I remove (objectClass=user) part)
If you want to query just that, then you should bind to that container in your initial connect:
// Next row is used to login to AD
string ldapPath = "LDAP://OU=Ingegneria,DC=xxx,DC=xxx";
DirectoryEntry searchRoot = GetEntry(ldapPath, adminUser, adminPassword);
// Here starts the query
DirectorySearcher search = new DirectorySearcher(searchRoot)
{
SearchScope = SearchScope.Subtree,
Filter = "(&" +
"(objectClass=user)" +
"(givenname=s*)" +
"(samaccountname=*100)" +
")"
};
search.PropertiesToLoad.Add("distinguishedname");
SearchResultCollection result = search.FindAll();
That way, you also massively reduce the space in AD that needs to be searched, thus speeding up your search.
And if you're using .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, "YOURDOMAIN", "OU=Ingegneria,DC=xxx,DC=xxx");
// define a "query-by-example" principal - here, we search for a UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = "s*";
qbeUser.SamAccountName = "*100";
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal"
UserPrincipal userFound = found as UserPrincipal;
if(userFound != null)
{
// do something with your user principal here....
}
}
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