Trying to pull 'ManagedBy' property from Computer object in AD - c#

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";
}
}

Related

C# Active Directory calls very slow

I have an application that needs to have a list of names and email addresses of users in a specific security group. I am currently doing this with the code below. When I run on VPN it comes back right away within a second or two usually but when I run on either on ethernet or wireless (both on the domain), it takes about 40 seconds for this to come back. Is there any way I can improve the time of this method on ethernet or wireless?
...
DirectoryEntry entry = new DirectoryEntry(ldap);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectCategory=group)(objectClass=group)(groupType:1.2.840.113556.1.4.803:=2147483648))";
mySearcher.PropertiesToLoad.Add("member");
SearchResultCollection results = mySearcher.FindAll();
foreach (SearchResult result in results)
{
foreach (string distinguishedMember in result.Properties["member"])
{
string memberPath = "LDAP://" + distinguishedMember;
DirectoryEntry member = new DirectoryEntry(memberPath);
DirectorySearcher Searcher = new DirectorySearcher(member);
Searcher.Filter = "(&(objectCategory=user))";
Searcher.PropertiesToLoad.Add("mail");
Searcher.PropertiesToLoad.Add("name");
SearchResult memberFound = Searcher.FindOne();
if (memberFound != null)
{
String memberEmail = memberFound.Properties["mail"][0].ToString();
String memberName = memberFound.Properties["name"][0].ToString();
users.Add(new KeyValuePair<String, String>(memberName, memberEmail));
}
}
}
Maybe it would help to get all of the users in one go, instead of fetching them one by one*:
Searcher.Filter = "(&(objectCategory=user)(memberOf=" + myGroupsDistinguishedName + "))"
Searcher.PropertiesToLoad.Add("mail");
Searcher.PropertiesToLoad.Add("name");
var allMembers = Searcher.FindAll();
var users = allMembers.Cast<SearchResult>().ToDictionary(sr=>sr.Properties["name"].ToString(), sr=>sr.Properties["mail"].ToString());
*This doesn't handle scenarios with over 1000 users.
Do not fetch all members at one time. Instead, I recommend using the pagesize property of the DirectorySearcher class:
mySearcher.PageSize = 10;

DirectorySearcher Filter

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

Active Directory List OU's

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 .....

Correct method to search for AD user by email address from .NET

I'm having some issues with code that is intended to find a user in Active Directory by searching on their email address. I have tried 2 methods but I'm sometimes finding that the FindOne() method will not return any results on some occasions. If I look up the user in the GAL in Outlook I see the SMTP email address listed.
My end goal is to confirm that the user exists in AD. I only have the email address as search criteria, so no way to use first or last name.
Method 1: Using mail property:
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(mail=" + email + ")";
search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();
Method 2: proxyAddresses property:
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(proxyAddresses=SMTP:" + email + ")"; // I've also tried with =smtp:
search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();
I've tried changing the case of the email address input but it still does not return a result. Is there a problem here with case sensitivity? If so, what is the best way to resolve it?
If you are using Exchange Server, proxyAddresses is the most reliable means of getting their email address. The primary smtp address is indicated by all caps "SMTP:" and additional email addresses will be prefixed with lowercase "smtp:". The attribute "mail" does not necessarily have to be the primary SMTP address, though usually it is.
Here is a variation of some code I used:
public static SearchResult FindAccountByEmail(string email)
{
string filter = string.Format("(proxyaddresses=SMTP:{0})", email);
using (DirectoryEntry gc = new DirectoryEntry("GC:"))
{
foreach (DirectoryEntry z in gc.Children)
{
using (DirectoryEntry root = z)
{
using (DirectorySearcher searcher = new DirectorySearcher(root, filter, new string[] { "proxyAddresses", "objectGuid", "displayName", "distinguishedName" }))
{
searcher.ReferralChasing = ReferralChasingOption.All;
SearchResult result = searcher.FindOne();
return result;
}
}
break;
}
}
return null;
}
static void Main(string[] args)
{
SearchResult result = FindAccountByEmail("someone#somewhere.com");
string distinguishedName = result.Properties["distinguishedName"][0] as string;
string name = result.Properties["displayName"] != null
? result.Properties["displayName"][0] as string
: string.Empty;
Guid adGuid = new Guid((byte[]) (result.Properties["objectGUID"][0]));
string emailAddress;
var emailAddresses = (from string z in result.Properties["proxyAddresses"]
where z.StartsWith("SMTP")
select z);
emailAddress = emailAddresses.Count() > 0 ? emailAddresses.First().Remove(0, 5) : string.Empty;
Console.WriteLine(string.Format("{1}{0}\t{2}{0}\t{3}{0}\t{4}",
Environment.NewLine,
name,
distinguishedName,
adGuid,
emailAddress));
}
I've found that using SysInternals ADExplorer is great for testing out/debugging Active Directory queries. As you can build the queries and run them against Active Directory you can see the results as well as easily view objects and see all their properties...
I've never had any issues with case sensitivity searching for users' email addresses - what happens if you search for the address, exactly as it appears in ADSIEDIT? Does it find the address when it is cased correctly?
BTW, I've always used the "mail" property, as it returns the user's single default outgoing email address, even if there are multiple addresses attached to the account. The "proxyAddresses" property is actually a multi-value property, and you're just searching for the value that starts with "smtp:" (it's lowercase in the property). However, a user could have multiple SMTP addresses on their AD account (we do), so between the two, the "mail" property may be what you're looking for.
var ds = new DirectorySearcher(new DirectoryEntry(strLDAPDomain));
ds.Filter = "(&(|(objectClass=User)(objectCategory=Person))(anr=" + user.userId + "))";// "(Name=*" + search + "*)";
ds.PropertiesToLoad.Add("mail");
var results = ds.FindOne();
if (results.Properties["mail"].Count > 0)
{
objUserInfo.UserEmail = results.Properties["mail"][0].ToString();
}
else
{
objUserInfo.UserEmail = user.useridID + "#gmail.com";
}

LDAP DirectorySearcher with MemberOf property

I want to find all the users that are a member of a group in a certain OU, so my filter would look something like this:
(&(objectClass=user)(memberOf=*OU=something,OU=yep,DC=dev,DC=local))
Is there a way to run a directorysearcher on the memberof property with a wildcard?
You need to set the OU you want to search as the root of your DirectorySearcher:
DirectoryEntry myOU = new DirectoryEntry("OU=something,OU=yep,DC=dev,DC=local");
DirectorySearcher srch = new DirectorySearcher(myOU);
srch.SearchScope = SearchScope.Subtree;
and then use just the objectCategory=person for your filter - I would use objectCategory which is single-valued and indexed and thus fast rather than objectClass (which is multi-valued and not indexed):
srch.Filter = "(objectCategory=person)";
If you still want to check for membership in a group in addition to being part of the OU, you can add this as a member-of part to the filter:
srch.Filter = "(&(objectCategory=person)(memberOf=cn=Group,ou=yep,dc=dev,dc=local))";
Not totally sure about the wildcards - in general, LDAP search filters do support wildcards, but I'm a bit hesitant about using a wildcard in a RDN like this group DN here.
Marc
According to this thread, wildcard search for DNs are not supported in Active Directory.
Don't specify a memberOf clause.
Don't specify the memberOf clause. Just use "(objectClass=user)"
Here is how i did this
is the LDAP name
is the group for which you need members
DirectoryEntry entry = new DirectoryEntry("LDAP://<COMPANYLDAP>/CN=<Group Name>,OU=something,OU=yep,DC=dev,DC=local");
DirectorySearcher Dsearch = new DirectorySearcher(entry);
SearchResult sResultSet = Dsearch.FindOne();
GetProperty(sResultSet, "member");
public static void GetProperty(SearchResult searchResult, string PropertyName)
{
StringBuilder strb = new StringBuilder();
if (searchResult.Properties.Contains(PropertyName))
{
ResultPropertyValueCollection rc = searchResult.Properties[PropertyName];
foreach (string name in rc)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://<COMPANYLDAP>/" + name);
DirectorySearcher Dsearch = new DirectorySearcher(entry);
//Dsearch.Filter = name;
SearchResult sResultSet = Dsearch.FindOne();
strb.AppendLine(GetPropertyvalue(sResultSet, "displayname") + "," + GetPropertyvalue(sResultSet, "mail"));
}
}
File.WriteAllText(strb.ToString(), "c:\\Users.txt");
}

Categories

Resources