C# Active Directory Browsing best practices - c#

Straight to the point, I have a request for a new project on which I have to find the primary and secondary owners for a specific list of Active Directory groups. When I get the array of secondary owners for each group, each of the owners are identified by their "distinguishedName" which led me to use a snippet like this to get the owner's info:
using (DirectoryEntry entry = new DirectoryEntry("LDAP://" + distinguishedName))
{
using (DirectorySearcher dSearch = new DirectorySearcher(entry))
{
SearchResult found = dSearch.FindOne();
if (found != null)
{
using (DirectoryEntry userEntry = found.GetDirectoryEntry())
{
Console.WriteLine("Username: " + userEntry.Properties["namefield"].Value + " : " + userEntry.Properties["emailfield"].Value);
}
}
else
{
Console.WriteLine("User not found with distinguishedName: " + distinguishedName);
}
}
}
GC.Collect();
I am a little concerned by the performance of this task since I have to get this information at the page loading sequence to check if the logged user is an owner or not. I have other AD browsing task to do and I've been doing some research on best practices with C# and AD and haven't found anything helpful yet so I though that you guys could provide some input on this.
Thanks for all your help.

f you have distinguished name of an object, you can bind to the object directly. Searching with DirectorySearcher is an excessive operation. Just create DirectoryEntry object and call its RefreshCache method. The fastest performance in ad is provided by classes located under System.DirectoryServices.Protocols namespace. Also one more optimization can be done: at the start of your program create a DirectoryEntry object and bind, e. g. To rootdse. This will establish ldap connection under the hood. All other queries will use this ldap connection. Keep the object alive until the program finishes
Credit to: oldovets

Related

DirectoryEntry and Domain Controller

We had a script in Powershell where we manipulated the Active Directory. I programmed it now in C#. My collegues say that they had to specify the domain controller in PS because otherwise it can happen that you read with DC A and write on DC B which could cause problems.
Do I really have to specify the Domain Controller if I'm using DirectorySearcher to find an entry and manipulate it? Or does per definition the same Domain Controller is user for finding the object (DirectorySearcher) and saving it (CommitChanges)?
I do not think so, since I can only specify it in the searching part (DirectoryEntry object for the DirectorySearcher) but not when it is written back to the AD (CommitChanges). So I suppose that the same DC is used for writing like the one used for reading.
Below I have an example where I search for a specific entry and change a property.
string filter = "(proxyaddresses=SMTP:johndoe#abc.com)";
string searchOU = "ou=Users,dc=abc,dc=com";
DirectoryEntry entry = new DirectoryEntry("LDAP://" + searchOU);
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = filter;
SearchResult result = search.FindOne();
search.Dispose();
entry.Close();
DirectoryEntry toContact = result.GetDirectoryEntry();
toContact.Properties["showInAddressBook"].Value = addressbook;
toContact.CommitChanges();
toContect.Close();
Might I recommend using the objects available in the System.DirectoryServices.AccountManagement namespace? It's a more recent addition to .NET and handles AD work much more gracefully. It's recently saved me a whole lot of heartache which the older DirectorySearcher way of doing things was in part causing. Let the framework take the strain! There are lots of really useful articles around on the web, such as here.
As an example of how to use PrincipalContext for searching Active directory, try this:
var adContext = new PrincipalContext(ContextType.Domain);
var queryTemplateUser = new UserPrincipal(adContext);
queryTemplateUser.SamAccountName = "HenryCrunn";
var ldapSearcher = new PrincipalSearcher(queryTemplateUser);
var searchResults = ldapSearcher.FindAll();

WPF Active Directory Calls performance issues

I have a C# WPF applicaiton that I am trying to perform a light check with the Active Directory Server and am running into serious performance issues of 20-30 seconds for the funciton to run. Using the identical code, I can place it in a Winforms application and it takes about 1 second or less. Since it is a big AD, I am guessing it is pulling all properties for the end user, but I really only want the first and last name of the person (for other purposes), and to ensure the user is in Active Directory.
Here is the code:
public string DomainUserNameGet(string ActiveDirectoryServer, string WindowsUserID) {
/// queries AD to get logged on user's name
string results = "";
try {
// create your domain context
PrincipalContext oPrincipalContext = new PrincipalContext(
ContextType.Domain
, ActiveDirectoryServer
);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(
oPrincipalContext
, IdentityType.SamAccountName
, ActiveDirectoryServer + #"\" + WindowsUserID
);
results =
oUserPrincipal.GivenName.ToString()
+ " "
+ oUserPrincipal.Surname.ToString();
} catch { }
return results;
}
The crazy thing is I can do the following via command line and get the response in about 1 second:
NET USER /DOMAIN LANID | find "LANID" /c
Any ideas on how I can improve performance?
RenniePet had it right; Turns out there was a DNS issue; I am unsure why this showed up in WPF vs. win forms though.

Microsoft Exchange / Active Directory properties for mails to be used to read a GAL

I'm currently creating an application which is using outlook as well as the exchange server / active directory in my company to create mails (I've had a few other questions here thus already).
I'm currently trying to read the GAL for it to be used when sending mails over my application. From the solutions I've seen so far it seems to me that the variant where I read the mail addresses from the active directory instead of connecting to the exchange server (I first tried outlook but aside from getting only the account names with the type "EX" thus that they are stored on the exchange server I didn't get much info there).
What I've done so far is gtting access to teh active directory and reading all users from there
DirectorySearcher objsearch = new DirectorySearcher();
String strrootdse = objsearch.SearchRoot.Path;
DirectoryEntry objdirentry = new DirectoryEntry(strrootdse);
objsearch.Filter = "(& (mailnickname=*)(objectClass=user))";
objsearch.SearchScope = System.DirectoryServices.SearchScope.Subtree;
objsearch.PropertiesToLoad.Add("cn");
objsearch.PropertiesToLoad.Add("mail");
objsearch.PropertyNamesOnly = true;
objsearch.Sort.Direction = System.DirectoryServices.SortDirection.Ascending;
objsearch.Sort.PropertyName = "cn";
SearchResultCollection colresults = objsearch.FindAll();
List<String> arrGal = new List<String>();
foreach (SearchResult objresult in colresults)
{
arrGal.Add(objresult.GetDirectoryEntry().Properties["cn"].Value + ": " + objresult.GetDirectoryEntry().Properties["mail"].Value);
}
Now after looking at the active directory I saw that there are also proxies and that (at least at my company) the "mail" property is not necessarily one of the mail addresses listed in the proxies.
Thus I found these two attributes: msExchShadowProxyAddresses, proxyAddresses
From what I've seen so far from them by looking at samples they look like they are identical, but even searching I didn't find anything on the web so far there.
Thus my Question when I'm trying to get the GAL from active directory Can I use both of these properties (thus they are always identical) or should I only use the ShadowProxy property or is there something I need to take into special consideration there?
You need to use AddressEntry.GetExchangeuser method. See my reply to your other post.

How do you use LsaEnumerateLogonSessions in C#?

In a C# Windows Forms Application, I'm trying to get a list of the users currently logged into a workstation (both local and domain users). Every search about this has led me to threads mentioning "just use LsaEnumerateLogonSessions".
So...how do you actually use this? The MSDN page is very sparse and doesn't seem to offer any clues.
This article shows how to use LsaEnumerateLogonSessons:
http://www.codeproject.com/Articles/18179/Using-the-Local-Security-Authority-to-Enumerate-Us
You should use Cassia, an open source wrapper.
ITerminalServicesManager manager = new TerminalServicesManager();
using (ITerminalServer server = manager.GetRemoteServer("your-server-name"))
{
server.Open();
foreach (ITerminalServicesSession session in server.GetSessions())
{
Console.WriteLine("Session ID: " + session.SessionId);
Console.WriteLine("User: " + session.UserAccount);
Console.WriteLine("State: " + session.ConnectionState);
Console.WriteLine("Logon Time: " + session.LoginTime);
}
}
I'm not sure how this will handle domain users; try it in LINQPad.
To answer your question, you need to declare it as a P/Invoke method that takes an out in and an out long[].

How to get streetaddress property of an organizational unit in windows active directory with LDAP in C#.Net

Each of our users is assigned to a primary organizational unit (OU) based on which global office they are in. So the "Chicago" OU contains all the associates in our Chicago office.
Using c# and .net 3.5, my task is to extract all of these users.
Unless the users are in a satellite or home office, their street address, city, state, etc. are empty, but the OU contains these details. When in Windows' Active Directory interface, right clicking on the OU and selecting properties gives a place to put all of this information just as on a user. However, when I try to access these properties like I do a user, I get an object reference error, suggesting these attributes do not exist the same way for an OU that they do for a user.
How do/can I access these location parameters from an OU object?
Here is a sample of the code I am using, showing streetaddress as an example, the statement trying to assign the value of streetaddress from the OU fails, where the assignment from associate succeeds.
foreach (SearchResult subOU in results)
{
ResultPropertyValueCollection subColl = subOU.Properties["distinguishedname"];
string subPath = subColl[0].ToString();
DirectoryEntry subEntry = new DirectoryEntry("LDAP://" + subPath);
DirectorySearcher userSearcher = new DirectorySearcher(subEntry);
userSearcher.SearchScope = SearchScope.OneLevel;
userSearcher.Filter = "(objectClass=user)";
foreach (SearchResult user in userSearcher.FindAll())
{
ResultPropertyValueCollection userColl = user.Properties["distinguishedname"];
string userPath = userColl[0].ToString();
DirectoryEntry userEntry = new DirectoryEntry("LDAP://" + userPath);
PropertyCollection associateProperties = userEntry.Properties;
PropertyCollection ouProperties = subEntry.Properties;
string streetAddress = string.Empty;
if (associateProperties["streetaddress"].Value == null)
{ streetAddress = ouProperties["streetaddress"].Value.ToString(); }
else
{ streetAddress = associateProperties["streetaddress"].Value.ToString(); }
}
}
If you change the Street-field on the General-tab in Active Directory Users & Computers for a user the value is stored in the streetAddress-attribute in the directory. If however you change the same field for an OU that value is stored in the street-attribute of that OU in the directory.
This is because OU objects are not (as defined in the Active Directory default schema) permitted to contain the streetAddress-attribute.
So (not having analyzed your code further) if you change ouProperties["streetaddress"] to ouProperties["street"] you'll might get the result you're looking for.
To avoid the ObjectReference exception you should check the collection contains the required attribute using the Contains(string) method. See here
I believe that AD will only stored valued attributes on an object, if a particular attribute has never been assigned a value it won't be available.
I found the AD schema references at:
http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-ADA1%5D.pdf A-L
http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-ADA2%5D.pdf Just M
http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-ADA3%5D.pdf N-Z
http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-ADTS%5D.pdf AD technical info
That would answer this question for you.
Also, the Win2K8 ADUC MMC snapin if you go to View, select Advanced Features, (enable the tick) then you get the Attribute Editor. (Something ConsoleOne for eDirectory has had for probably close to a decade now!).
One small note, in AD schema, first character is always lower case, and I run at sufficiently high res that the lower case L's are hard to see as L's. (Need a better screen font, but mea culpa).

Categories