I wanted to query windows local user accounts from C# using ADSI.
Since WinNt provider is used, Directory SEarcher is not supported.
I am doing the following :
DirectoryEntries entries = hostMachineDirectory.Children;
var users = from DirectoryEntry childEntry in hostMachineDirectory.Children
where (childEntry.Name.StartsWith("User1"))
select childEntry;
Querying with Name is possible. It is not possible to get the description like childEntry.Description. It is possible to get the description by using properties childEntry.Properties["Description"].So is it possible to query with attributes like 'Description' ?
I don't know much about AD, but I guess, assuming that Properties["Description"].Value returns string, you can try it this way :
var users = from DirectoryEntry childEntry in entries
where ((string)childEntry.Properties["Description"].Value).StartsWith("User1")
select childEntry;
Ok since you confirmed that my assumption was wrong, Properties["Description"].Value doesn't always return string, it returns NULL sometimes, we need to check for null first before checking with .StartsWith or Contains or any other string checking operation :
var users = from DirectoryEntry childEntry in entries
where childEntry.Properties["Description"].Value != null &&
((string)childEntry.Properties["Description"].Value).StartsWith("User1")
select childEntry;
If you are trying to get string, you should do it this way:
DirectoryEntries entries = hostMachineDirectory.Children;
var users = (from DirectoryEntry childEntry in hostMachineDirectory.Children
where (childEntry.Name.StartsWith("User1"))
select childEntry).SingleOrDefault<string>();
Here's how I got it to work.
var users = from DirectoryEntry childEntry in hostMachineDirectory.Children
where childEntry.SchemaClassName == "User"
select childEntry;
try {
var UserName = users.Single(s => s.Properties["Description"].Value.ToString().StartsWith("Whatever description")).Name;
Console.WriteLine("User: " + UserName);
}
catch (Exception e) {
var err = e.Message;
Console.WriteLine("Error message: " + err);
}
Related
I have been searching around for a solution on getting both users and contacts from a group in Active Directory, but cant find one.
I understand that I cant get contacts the same way as users because they are not security principals?
I use this code to get all users in my group, is it possible to extend this to retrieve name, and mobile number from contacts? Or do I need to write something new?
var context = new PrincipalContext(ContextType.Domain, "MY_DOMAIN");
using (var searcher = new PrincipalSearcher())
{
var groupName = "MY_GROUP";
var sp = new GroupPrincipal(context, groupName);
searcher.QueryFilter = sp;
var group = searcher.FindOne() as GroupPrincipal;
if (group == null)
Console.WriteLine("Invalid Group Name: {0}", groupName);
foreach (var f in group.GetMembers())
{
var principal = f as UserPrincipal;
if (principal == null || string.IsNullOrEmpty(principal.Name))
continue;
DirectoryEntry entry = (principal.GetUnderlyingObject() as DirectoryEntry);
DirectorySearcher entrySearch = new DirectorySearcher(entry);
entrySearch.PropertiesToLoad.Add("mobile");
entrySearch.PropertiesToLoad.Add("sAMAccountName");
entrySearch.PropertiesToLoad.Add("name");
SearchResultCollection results = entrySearch.FindAll();
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "mobile")
Console.WriteLine(rpc["mobile"][0].ToString());
if(rp == "sAMAccountName")
Console.WriteLine(rpc["sAMAccountName"][0].ToString());
}
You cannot use the System.DirectoryServices.AccountManagement namespace to query contact information from Active Directory because as you point out, they are not security principles. You'll need to read and parse the member property of the group directly from the group's DirectoryEntry. This will be a list of distinguished names of all the objects which are a member of the group. There's no way to know from this what kind of object they are so you'll need to query AD for each to find out.
You have all the code needed to accomplish this already in what you posted, just add the member property to the load list and then loop though it loading new DirectoryEntry objects. The objectClass property will tell you if it's a user, group or contact.
I want to retrieve the first name and last name of the user that is logged in his/her machine using AD. I use the following code:
string server = ConfigurationManager.AppSettings["ActiveDirectory.Server"];
DirectoryEntry entry = new DirectoryEntry(#"LDAP://" + server);
DirectorySearcher searcher = new DirectorySearcher(entry);
User user = GetUser(entry);
searcher.Filter = "sAMAccountName=" + user.UserAD;
searcher.PropertiesToLoad.Add("memberof");
SearchResult result = searcher.FindOne();
private static User GetUser(DirectoryEntry userEntry)
{
Usuario user = new User();
string[] username = HttpContext.Current.Request.ServerVariables["AUTH_USER"].Split('\\');
//THIS IS WHAT I NEED BUT IT DOES RETURN null.
//User.Name= (string)userEntry.Properties["givenName"].Value;
//User.LastName= (string)userEntry.Properties["sn"].Value;
user.Domain = username[0];
user.UserAD = username[1];
return user;
}
Now, I know searcher.PropertiesToLoad have a [memberof] and [adspath], the last one gives me the first and last name separated with a comma, something like CN="gates, billy" but I dont want to use substrings and index, is there any property like [firstName], [lastName] in the list properties?
I did search that DirectoryEntry have a property called givenName and sn but this returns null
The PropertiesToLoad set is exactly what you need to modify. Active Directory will return only the properties which are defined in this set, that's why you don't see givenName and sn. Just add these properties as well:
searcher.PropertiesToLoad.Add("givenName");
searcher.PropertiesToLoad.Add("sn");
Alternatively, just add the property * to load all of them:
searcher.PropertiesToLoad.Add("*");
I have a strange problem when I tried to retrieve the "AccountExpirationDate" from the active directory.
I use the following code to retrieve the user:
DirectoryEntry dirEntry = new DirectoryEntry(Path);
DirectorySearcher search = new DirectorySearcher(dirEntry);
// specify the search filter
search.Filter = "(&(objectClass=user)(mail=" + email + "))";
// perform the search
SearchResult result = search.FindOne();
DirectoryEntry user = result.GetDirectoryEntry();
And then I retrieve the "AccountExpirationDate":
object o1 = user.Properties["accountExpires"].Value; //return a COM object and I cannot retrieve anything from it
object o2 = user.Properties["AccountExpirationDate"].Value; //return null
object o3 = user.InvokeGet("AccountExpirationDate"); //return the DateTime
So I would like to what happened here?
Why I cannot use DirectoryEntry.Properties to retrieve the AccountExpirationDate?
What is the different between DirectoryEntry.Properties vs DirectoryEntry.InvokeGet?
Thanks a lot.
You can tell a directorySearcher which properties to load as follows:
// specify the search filter
search.Filter = "(&(objectClass=user)(mail=" + email + "))";
search.PropertiesToLoad.Add("AccountExpirationDate");
search.PropertiesToLoad.Add("displayname");
after performing search you need to go through the properties of the SearchResult to get values
i.e.
object o1 = result.Properties["AccountExpirationDate"][0];
DirectoryEntry.Properties - Gets the Active Directory Domain Services properties for this DirectoryEntry object.
DirectoryEntry.InvokeGet - Gets a property from the native Active Directory Domain Services object.
//Microsoft doesn't recommend the use of InvokeGet method.
I'm trying to get a list of users from the Active Directory, who have a specified manager.
I used the following LDAP filter without success:
(manager=CN=Misterboss_n*)
However, it returns no result. Users have the following value in the manager attribute:
"CN=Misterboss_n,OU=xyz user,DC=xyz,DC=local"
What am I doing wrong? If I replace the above filter with something like this:
(givenName=John*)
it works okay (returns all users whose given name is John).
Wider context:
public List<ADUserDetail> GetAllEmployeesUnderMisterboss()
{
List<ADUserDetail> userlist = new List<ADUserDetail>();
string filter = "";
_directoryEntry = null;
DirectorySearcher directorySearch = new DirectorySearcher(SearchRoot);
directorySearch.Asynchronous = true;
directorySearch.CacheResults = true;
filter = "(manager=CN=Misterboss_n*)";
directorySearch.Filter = filter;
SearchResultCollection userCollection = directorySearch.FindAll();
foreach (SearchResult users in userCollection)
{
DirectoryEntry userEntry = new DirectoryEntry(users.Path, LDAPUser, LDAPPassword);
ADUserDetail userInfo = ADUserDetail.GetUser(userEntry);
userlist.Add(userInfo);
}
return userlist;
}
Thanks for the help!
I don't think there is a start-of-field search available for DN-typed properties. You will have to use the full DN of the manager. If you don't know the full DN, find the manager's LDAP object first and use its distinguishedName property.
Be sure to escape the DN value properly before building your filter - not every character that is valid in a DN is also valid in an LDAP filter expression:
* as \2a
( as \28
) as \29
\ as \5c
NUL as \00
/ as \2f
For code samples, see this related thread where I answered a very similar question: Getting all direct Reports from Active Directory
I have a question about determining the type (User or Group) of a account name.
For example, I have two strings, say "Adventure-works\david" and "Adventure-works\admins",
the first represents a user named david, and the second represents an AD group.
My question is how can I determin the type(User or AD group) of these account? Are there convenient method I can use?
Any comments are appreciated.
Thanks.
What version of .NET are you on??
If you're on .NET 3.5, see this excellent MSDN article on how the Active Directory interface has changed quite a bit.
If you're on .NET 3.5, you could write:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
Principal myObject = Principal.FindByIdentity(ctx, "your name value");
Typically, you'd have to pass in just the user name - the part after the backslash - not the whole DOMAIN\USERNAME string.
This "Principal" now either is a UserPrincipal or a GroupPrincipal (or it could some other type of principal, e.g. ComputerPrincipal):
if(myObject is UserPrincipal)
{
// you have a user
}
else if(myObject is GroupPrincipal)
{
// you have a group
}
and you can go on from there.
If you're on .NET 1.x/2.0/3.0, you'd have to use the slightly more involved procedure of creating a DirectorySearcher and searching for your object:
// create root DirectoryEntry for your search
DirectoryEntry deRoot = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// create searcher
DirectorySearcher ds = new DirectorySearcher(deRoot);
ds.SearchScope = SearchScope.Subtree;
// define LDAP filter - all you can specify is the "anr" (ambiguous name
// resolution) attribute of the object you're looking for
ds.Filter = string.Format("(anr={0})", "YourNameValue");
// define properties you want in search result(s)
ds.PropertiesToLoad.Add("objectCategory");
ds.PropertiesToLoad.Add("displayName");
// search
SearchResult sr = ds.FindOne();
// check if we get anything back, and if we can check the "objectCategory"
// property in the search result
if (sr != null)
{
if(sr.Properties["objectCategory"] != null)
{
// objectType will be "Person" or "Group" (or something else entirely)
string objectType = sr.Properties["objectCategory"][0].ToString();
}
}
Marc
Warning: In case of using DirectorySearcher the accepted answer might fail, since objectCategory it doesn't return consistent results.
Consider using objectClass instead:
SearchResult sr = ds.FindOne();
bool isUser = sr.Properties["objectClass"]?.Contains("user") == true;
// OR
bool isGroup = sr.Properties["objectClass"]?.Contains("group") == true;
using System.DirectoryServices.AccountManagement;
...
..
Principal myPrincipal = Principal.FindByIdentity(ctx, "your name value");
if (myPrincipal.GetType() == typeof(GroupPrincipal)) {
GroupPrincipal myGroup = (GroupPrincipal)myPrincipal;
} else {
UserPrincipal myUser = (UserPrincipal)myPrincipal;
}