Active Directory Connection C# - c#

My purpose is to connect to the Active Directory (which runs on a virtual machine (Win SRV 2008R2)) within a console C# application and write down all the user names in the domain. Since I'm a newbie on AD I just got stuck setting a connection.
Now first thing is first;
Root DomainName = frt.local
IP : 192.168.x.x
Username: admin
Pass : yyyy
I have written the code below to set a connection but getting errors. Please tell me the point I missed.
DirectoryEntry entry = new DirectoryEntry();
entry.Path = "LDAP://192.168.x.x/dc=frt.local";
entry.Username = #"frt.local\admin";
entry.Password = "yyyy";
After pointing what I missed any help would be mostly welcome about writing down the usernames to the console.
Kind Regards

Nesim's answer is good - in the beginning. But I don't really see any point or need in using that
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
line - the result of the PrincipalSearcher already is a UserPrincpial and you can access its properties much easier like this:
using (var searcher = new PrincipalSearcher(new UserPrincipal(ctx)))
{
foreach (var result in searcher.FindAll())
{
UserPrincipal foundUser = result as UserPrincipal;
if(foundUser != null)
{
Console.WriteLine("First Name: {0}", foundUser.GivenName);
Console.WriteLine("Last Name : {0}", foundUser.Surname);
Console.WriteLine("SAM account name; {0}", foundUser.SamAccountName);
Console.WriteLine("User principal name: {0}", foundUser.UserPrincipalName);
Console.WriteLine();
}
}
}
The UserPrincipal already and very nicely exposes the most frequently used attributes as properties on the object itself - no need for the rather messy code with the DirectoryEntry...

var username = "your username";
var password = "your password";
var domain = "your domain";
var ctx = new PrincipalContext(ContextType.Domain, domain, username, password);
using (var searcher = new PrincipalSearcher(new UserPrincipal(ctx)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
}
}

Related

Connect to another domain Active Directory in ASP.NET

I am trying to connect to another domain using from my C# code but I get an error.
Here is my code:
doLog("Going For establishing Connection");
var username = "cn=Directory Manager";
var password = "somepassword";
using (var context = new PrincipalContext(ContextType.Domain, "LDAP://10.10.10.132:2232", username, password))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
doLog("In the principal searcher");
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
doLog("Looping result:" + de.Properties["givenName"].Value);
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
}
}
}
It is working in java but from .net it always throw an error on this line
using (var context = new PrincipalContext(ContextType.Domain, "LDAP://10.10.10.132:2232", username, password))
This is the error I get:

C# Search Active Directory error

I am pretty new to using C# and this is my second time using it with active directory. I keep getting the error: Object reference not set to an instance of an object. Below is my code. I know that my null reference is in the line var result = searcher.FindOne(); I am unsure of what I need to do to fix this.
static void Main(string[] args)
{
List<string> userList = new List<string>();
try
{
string[] newUsers = { List of users is here ex: jsmith#xyz.com, bsmith#xyz.com, ... };
PrincipalContext AD = new PrincipalContext(ContextType.Domain, "xyz.com");
UserPrincipal u = new UserPrincipal(AD);
PrincipalSearcher search = new PrincipalSearcher(u);
DirectorySearcher searcher = new DirectorySearcher();
foreach (string x in newUsers)
{
searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", x);
var result = searcher.FindOne();
userList.Add(string.Format("{0} {1}", result.Properties["DisplayName"][0].ToString(), result.Properties["Company"][0].ToString()));
search.Dispose();
}
foreach(string y in userList)
{
Console.WriteLine(y);
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
File.WriteAllLines(file location, userList);
}
Your problem is that you're declaring PrincipalSearcher and DirectorySearcher, yet you're only populating the PrincipalSearcher with UserPrincipal object.
...
UserPrincipal u = new UserPrincipal(AD);
PrincipalSearcher search = new PrincipalSearcher(u);
...
However, your DirectorySearcher object searcher is empty.
DirectorySearcher searcher = new DirectorySearcher();
In foreach loop you're searching for one user using DirectorySearcher object not PrincipalSearcher:
var result = searcher.FindOne();
The above line will always return null. You need to populate DirectorySearcher.
DirectorySearcher searcher = new DirectorySearcher(/*need a DirectoryEntry*/);
I would suggest you take full advantage of UserPrincipal class. It seems that you want to search for users in Active Directory and you know their UserPrincipal names.
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
string [] newUsers; //need to declare list of new users
foreach (string user in newUsers)
{
using (UserPrincipal newUser = UserPrincipal.FindByIdentity(ctx, IdentityType.UserPrincipalName, user))
{
if (newUser != null)
{
//do what you need to do
//newUser will contain all info on a particular user
}
}
}
}
As several commenters have noted, your code is not handling the situation where no user is found by DirectorySearcher.FindOne - and, as noted in the MSDN documentation, FindOne returns null if no user is found:
If more than one entry is found during the search, only the first entry is returned. If no entries are found to match the search criteria, a null reference (Nothing in Visual Basic) is returned.
So you'll need to handle the case where the user you're looking for isn't there:
foreach (string x in newUsers)
{
Console.WriteLine("looking for user {0}", x);
searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", x);
var result = searcher.FindOne();
if (result == null)
{
userList.Add(String.Format("user {0} not found!", x));
}
else
{
userList.Add(string.Format("{0} {1}", result.Properties["DisplayName"][0].ToString(), result.Properties["Company"][0].ToString()));
}
search.Dispose();
}

Getting last Logon Time on Computers in Active Directory

How can I get a list of users from active directory?
Please see the page above. It answered most of my questions, but I get a problem when I try to get last logon time for a computer. Sorry if there was some way to comment on that page instead of making a whole new question because I didn't find such an option.
using (var context = new PrincipalContext(ContextType.Domain, "cat.pcsb.org"))
{
using (var searcher = new PrincipalSearcher(new ComputerPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("Name: " + de.Properties["name"].Value);
Console.WriteLine("Last Logon Time: " + de.Properties["lastLogon"].Value);
Console.WriteLine();
}
}
}
Console.ReadLine();
I replaced UserPrincipal with ComputerPrincipal. Name and some other properties work fine, but logon doesn't. I've tried doing different things like casting it to DateTime (the cast failed) but nothing worked. The above just results in System.__ComObject. So what can I do to get it to get last logon time correctly?
Why aren't you just using the the LastLogon property returned by ComputerPrincipal? (ComputerPrincipal is a AuthenicatablePrincipal)
using (var context = new PrincipalContext(ContextType.Domain, "cat.pcsb.org"))
{
using (var searcher = new PrincipalSearcher(new ComputerPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
var auth = result as AuthenticablePrincipal;
if(auth != null)
{
Console.WriteLine("Name: " + auth.Name);
Console.WriteLine("Last Logon Time: " + auth.LastLogon);
Console.WriteLine();
}
}
}
}
Console.ReadLine();
Note that LastLogon is not a replicated property, so if you have more than one domain controller you need to query each controller and find out who gives the most recent result.
You need to iterate through all domain controllers and find the lastest logon time.
The below code finds last logon time for a user.
public DateTime findlastlogon(string userName)
{
DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, "domainName");
DateTime latestLogon = DateTime.MinValue;
DomainControllerCollection dcc = DomainController.FindAll(context);
Parallel.ForEach(dcc.Cast<object>(), dc1 =>
{
DirectorySearcher ds;
DomainController dc = (DomainController)dc1;
using (ds = dc.GetDirectorySearcher())
{
try
{
ds.Filter = String.Format(
"(sAMAccountName={0})",
userName
);
ds.PropertiesToLoad.Add("lastLogon");
ds.SizeLimit = 1;
SearchResult sr = ds.FindOne();
if (sr != null)
{
DateTime lastLogon = DateTime.MinValue;
if (sr.Properties.Contains("lastLogon"))
{
lastLogon = DateTime.FromFileTime(
(long)sr.Properties["lastLogon"][0]
);
}
if (DateTime.Compare(lastLogon, latestLogon) > 0)
{
latestLogon = lastLogon;
//servername = dc1.Name;
}
}
}
catch (Exception)
{
}
}
});
return latestLogon;
}
To get last logon time for a computer replace sAMAccountName to Name.
ds.Filter = String.Format(
"(Name={0})",
userName
);

How to programmatically update and delete LDAP users from SQL?

So i'm looking to grab some ldap values, and insert them into a database with encryption. I've got the insert working but i need to check if the user is still part of the group and if not remove them from the DB, and if there was a new user added it inserts them instead of inserting existing users. Can you give me some direction on best practices for this? I'd prefer not to truncate the table and re-insert all.
try
{
/* Connection to Active Directory */
DirectoryEntry deBase = new DirectoryEntry("LDAP://" + txtLDAP.Text + ":" + txtLDapPort.Text + "/" + txtBadeDN.Text, txtUsername.Text, txtPassword.Text, AuthenticationTypes.Secure);
/* Directory Search*/
DirectorySearcher dsLookForGrp = new DirectorySearcher(deBase);
dsLookForGrp.Filter = String.Format("(cn={0})", txtGroup.Text);
dsLookForGrp.SearchScope = SearchScope.Subtree;
dsLookForGrp.PropertiesToLoad.Add("distinguishedName");
SearchResult srcGrp = dsLookForGrp.FindOne();
/* Directory Search
*/
DirectorySearcher dsLookForUsers = new DirectorySearcher(deBase);
dsLookForUsers.Filter = String.Format("(&(objectCategory=person)(memberOf={0}))", srcGrp.Properties["distinguishedName"][0]);
dsLookForUsers.SearchScope = SearchScope.Subtree;
dsLookForUsers.PropertiesToLoad.Add("objectSid");
dsLookForUsers.PropertiesToLoad.Add("sAMAccountName");
SearchResultCollection srcLstUsers = dsLookForUsers.FindAll();
StringBuilder sbUsers = new StringBuilder();
foreach (SearchResult sruser in srcLstUsers)
{
SecurityIdentifier sid = new SecurityIdentifier((byte[])sruser.Properties["objectSid"][0], 0);
string ConnString = "ConnectionString Removed";
string SqlString = "spInsertADAuthorization";
using (OleDbConnection conn = new OleDbConnection(ConnString))
{
using (OleDbCommand cmd = new OleDbCommand(SqlString, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("AD_Account", SpartaCrypto.SpartaEncryptAES(sruser.Properties["sAMAccountName"][0].ToString(), "thisisasharedsecret"));
cmd.Parameters.AddWithValue("AD_SID", SpartaCrypto.SpartaEncryptAES(sid.ToString(), "thisisasharedsecret"));
cmd.Parameters.AddWithValue("AD_EmailAddress", "user#host.com");
cmd.Parameters.AddWithValue("DateImported", DateTime.Now.ToString());
cmd.Parameters.AddWithValue("Active", 1);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
}
lblResults.Text = srcLstUsers.Count + " Users granted access.";
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Logon failure"))
{
lblResults.Text = "Logon Failure. Check your username or password.";
}
if (ex.Message.Contains("The server is not operational"))
{
lblResults.Text = "LDAP Error. Check your hostname or port.";
}
if (ex.Message.Contains("Object reference not set to an instance of an object"))
{
lblResults.Text = "LDAP Error. Check your hostname, port, or group name and try again.";
}
}
Since you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
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 UserPrincipal
// and with the first name (GivenName) of "Bruce"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = "Bruce";
// 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" - it could be user, group, computer.....
}
For working with a single principal, the programming interface is also much nicer:
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here... you can access most of the commonly used properties easily
user.GivenName = "....";
user.Surname = "......";
user.SamAccountName = ".....";
}
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
// if found....
if (group != null)
{
// iterate over members
foreach (Principal p in group.GetMembers())
{
Console.WriteLine("{0}: {1}", p.StructuralObjectClass, p.DisplayName);
// do whatever you need to do to those members
}
}
The new S.DS.AM makes it really much easier to play around with users and groups in AD!

get local groups and not the primary groups for a domain user

i have a code to get the groups a user belongs to.
try
{
DirectoryEntry adRoot = new DirectoryEntry(string.Format("WinNT://{0}", Environment.UserDomainName));
DirectoryEntry user = adRoot.Children.Find(completeUserName, "User");
object obGroups = user.Invoke("Groups");
foreach (object ob in (IEnumerable)obGroups)
{
// Create object for each group.
DirectoryEntry obGpEntry = new DirectoryEntry(ob);
listOfMyWindowsGroups.Add(obGpEntry.Name);
}
return true;
}
catch (Exception ex)
{
new GUIUtility().LogMessageToFile("Error in getting User MachineGroups = " + ex);
return false;
}
the above code works fine when i have to find the groups of a local user but
for a domain user it returns a value "Domain User" which is kind of wierd as it is a part of 2 local groups.
Please can some1 help in solving this mystery. thanks
Research
I did some finding and got that i am being returned the primary group of the domain user
called "Domain User" group
but what i actually want is the groups of the local machines the domain user is a part of... i cannot get that.. any suggestions
another code using LDAP
string domain = Environment.UserDomainName;
DirectoryEntry DE = new DirectoryEntry("LDAP://" + domain, null, null, AuthenticationTypes.Secure);
DirectorySearcher search = new DirectorySearcher();
search.SearchRoot = DE;
search.Filter = "(SAMAccountName=" + completeUserName + ")"; //Searches active directory for the login name
search.PropertiesToLoad.Add("displayName"); // Once found, get a list of Groups
try
{
SearchResult result = search.FindOne(); // Grab the records and assign them to result
if (result != null)
{
DirectoryEntry theUser = result.GetDirectoryEntry();
theUser.RefreshCache(new string[] { "tokenGroups" });
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0);
DirectorySearcher sidSearcher = new DirectorySearcher();
sidSearcher.SearchRoot = DE;
sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";
sidSearcher.PropertiesToLoad.Add("distinguishedName");
SearchResult sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
listOfMyWindowsGroups.Add((string)sidResult.Properties["distinguishedName"][0]);
}
}
}
else
{
new GUIUtility().LogMessageToFile("no user found");
}
return true;
}
catch (Exception ex)
{
new GUIUtility().LogMessageToFile("Error obtaining group names: " + ex.Message + " Please contact your administrator."); // If an error occurs report it to the user.
return false;
}
this works too but i get the same result "Domain Users" . Please can some1 tell me how to get the local machine groups...????
If you are using .NET 3.5, you can use System.DirectoryService.AccountManagement to do all the user and group management. In particular, UserPrincipal.GetAuthorizationGroups is exactly what you are looking for. It retrieves both local group and machine group for a particular users. If the group is a local group, GroupPrincipal.Context.Name is showing the machine name where the group come from. If the group is a domain group, GroupPrincipal.Context.Domain is showing the domain name where the group comes from.
PrincipalContext context = new PrincipalContext(ContextType.Domain, "yourdomain.com");
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "youruser");
foreach (GroupPrincipal group in userPrincipal.GetAuthorizationGroups())
{
Console.Out.WriteLine("{0}\\{1}", group.Context.Name, group.SamAccountName);
}
I would say the problem is that you're search is starting in the domain. You want to change the location of the search to the local machine.
Something like this would do it;
DirectoryEntry AD = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");

Categories

Resources