Different result with DirectorySearcher - c#

I wrote a small app to check AD group members. When I execute the following code on my pc, It works well, the SearchResult contains the "member" property, however when I run the same exe on the server or on an another computer the "member" property is missing. The usnchanged and usncreated will be different also. I run the exe with the same user on every pc. What can cause this?
...
using (DirectorySearcher searcher = new DirectorySearcher())
{
searcher.CacheResults = false;
searcher.Filter = "(&(objectClass=group)(cn=" + ADName + "))";
searcher.SizeLimit = int.MaxValue;
searcher.PageSize = int.MaxValue;
if (!DirectoryEntry.Exists(ADPath))
{
return null;
}
searcher.SearchRoot = new DirectoryEntry(ADPath);
using (SearchResultCollection collection = searcher.FindAll())
{
if (collection.Count == 1)
{
return collection[0];
}
}
}
...

The group membership data is not replicated to the global catalog. The query might work sometimes, if you happen to connect to the domain controller with the actual membership data. On other machines, you probably connect to other domain controllers, of different domains, where the information is not available.
You might want to connect to a domain controller in the actual domain, not to the global catalog.

Related

DirectorySearcher object data retrieving not working on Azure hosted application

This code used to work for me in order to retrieve the AD information of a user when passing ID by parameter.
public UsersDTO GetUserFromActiveDirectoryByID(string userID)
{
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "(&(objectClass=user)(objectcategory=person)(name=" + userID + "))";
SearchResultCollection results = ds.FindAll();
SearchResult userProperty = results[0];
UsersDTO user = new UsersDTO();
if (userProperty.Properties["mail"].Count > 0)
{
user.fullName = userProperty.Properties["displayname"][0].ToString();
user.email = userProperty.Properties["mail"][0].ToString();
}
return user;
}
It worked while the application service was hosted in another server, but now that it has been migrated to Azure, the FindAll command (also FindOne was tested) returns "There was an error retrieving the data.","Status":400,"Detail":"Access is denied."
You aren't setting the SearchRoot of your DirectorySearcher. The documentation for SearchRoot says:
If SearchRoot is a null reference (Nothing in Visual Basic), the search root is set to the root of the domain that your server is currently using.
If the other server was joined to the domain that you are trying to search, then that's why it was working. But that is no longer true when you're on Azure.
So you need to specify the SearchRoot to point it at your domain:
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = new DirectoryEntry("LDAP://example.com");
This may also introduce issue of whether you can actually access your domain controllers from Azure. You may need to open firewall rules to allow it, depending on how your environment is setup.

Loading the active directory user and their associated Computers

Hello Everyone i am trying to develop a program that will list will all the user of Active Directory and when i select one user the program should be able to show the computers associated with that user. i.e the Computers that are accessible to that AD user.
I have written code to list all user but no idea how to list the computers associated with that user.
Here is my code to load AD User into datatable:
DataTable dtUser= new DataTable();
try
{
DirectoryEntry dom = Domain.GetComputerDomain().GetDirectoryEntry();
DirectorySearcher dsAllUsers = new DirectorySearcher(dom);
dsAllUsers.SearchScope = SearchScope.Subtree;
dsAllUsers.Filter = "(objectCategory=Person)";
SearchResultCollection result = dsAllUsers.FindAll();
dtUser.Columns.Add("CustodianName");
dtUser.Columns.Add("Email");
dtUser.Columns.Add("Title");
dtUser.Columns.Add("Dept.");
foreach (SearchResult rs in result)
{
DataRow newRow = dtUser.NewRow();
if (rs.GetDirectoryEntry().Properties["samaccountname"].Value != null)
newRow["CustodianName"] = rs.GetDirectoryEntry().Properties["samaccountname"].Value.ToString();
if (rs.GetDirectoryEntry().Properties["mail"].Value != null)
newRow["Email"] = rs.GetDirectoryEntry().Properties["mail"].Value.ToString();
if (rs.GetDirectoryEntry().Properties["title"].Value != null)
newRow["Title"] = rs.GetDirectoryEntry().Properties["title"].Value.ToString();
if (rs.GetDirectoryEntry().Properties["department"].Value != null)
newRow["Dept."] = rs.GetDirectoryEntry().Properties["department"].Value.ToString();
dtUser.Rows.Add(newRow);
}
return dtUser;
}
catch (Exception)
{
throw;
}
I don't believe standard LDAP/Active Directory has anything like this.
Computers are just another class of AD objects - but there's no "link" between a user and one (or multiple) computer(s) - there's no belongsTo attribute on the computer class, nor is there a computers collection on User.
If your organization has implemented an extension to the default AD schema (which is entirely possible!), it's a custom solution, and then you must know what it is! :-)

Directory Searcher - Searching multiple OU's

I'm currently working on a project where i have to pull some Active Directory information on all our servers.
The thing is i ONLY wan't the servers in AD, and not all the desktop computers.
The servers are located in multiple sub directories, for each location.
I was able to filter on operating system like this, but as some of them are cluster servers AD havn't picked up the OS of the ones hosted on those so I need to create a filter or some logic that only looks in the OU's called Servers.
This is what i currently have:
DirectoryEntry entry = new DirectoryEntry(path);
// Create a DirectorySearcher object.
DirectorySearcher mySearcher = new DirectorySearcher(entry);
// Set a filter for users with the name test.
// Search specific computer replace OperatingSystem=Windows*Server* with anr=<servername>
mySearcher.Filter = "(&(objectClass=computer)(OperatingSystem=Windows*Server*))";
mySearcher.SearchScope = SearchScope.Subtree;
results = mySearcher.FindAll();
mySearcher.PropertiesToLoad.Add("cn");
mySearcher.PropertiesToLoad.Add("dnshostname");
mySearcher.PropertiesToLoad.Add("Site");
mySearcher.PropertiesToLoad.Add("Description");
mySearcher.PropertiesToLoad.Add("Location");
mySearcher.PropertiesToLoad.Add("operatingsystem");
mySearcher.PropertiesToLoad.Add("operatingsystemservicepack");
mySearcher.PropertiesToLoad.Add("operatingsystemversion");
mySearcher.PropertiesToLoad.Add("managedBy");
con.Open();
foreach (SearchResult searchResult in results)
{
cmd.Parameters.AddWithValue("#Servername", Convert.ToString(searchResult.GetDirectoryEntry().Properties["cn"].Value));
cmd.Parameters.AddWithValue("#DNSHostname", Convert.ToString(searchResult.GetDirectoryEntry().Properties["dnshostname"].Value));
cmd.Parameters.AddWithValue("#Description", Convert.ToString(searchResult.GetDirectoryEntry().Properties["description"].Value));
cmd.Parameters.AddWithValue("#Site", Convert.ToString(searchResult.GetDirectoryEntry().Properties["Site"].Value));
cmd.Parameters.AddWithValue("#Location", Convert.ToString(searchResult.GetDirectoryEntry().Properties["Location"].Value));
cmd.Parameters.AddWithValue("#OS", Convert.ToString(searchResult.GetDirectoryEntry().Properties["operatingsystem"].Value));
cmd.Parameters.AddWithValue("#OSServicePack", Convert.ToString(searchResult.GetDirectoryEntry().Properties["operatingsystemservicepack"].Value));
cmd.Parameters.AddWithValue("#OSVersion", Convert.ToString(searchResult.GetDirectoryEntry().Properties["operatingsystemversion"].Value));
cmd.Parameters.AddWithValue("#ManagedBy", Convert.ToString(searchResult.GetDirectoryEntry().Properties["managedBy"].Value));
if (searchResult.Path.Contains("OU=Test") == true)
{
cmd.Parameters.AddWithValue("#IsTestServer", 1);
}
else
{
cmd.Parameters.AddWithValue("#IsTestServer", 0);
}
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
}
After some more search today i found the solution here on the website
get computer from OU

Active Directory not working for offsite

Technology used: asp.net(C#), MVC, LINQ, Entity
I use the Active Directory for our company website. This system works for all of our employees on site and when we take our laptop offsite and use VPN.
However we recently hired some developers(offsite) who we have given VPN access to. But they are unable to run our code and I am unsure why. We have active directory users set up for them and they can connect to us through VPN.
They are running into two errors.
A username/password error.
The Server is not Operational error.
Does the VPN username/password have to match the active directory account?
If they log into their development machine does that username/password have to match the active directory account?
Is there some restriction on Active Directory and offsite I am unaware of as to why this would work for us but not our off site developers?
Below is the section of code that is giving the error: It errors on the line SearchResult rs = ds.FindOne();
public AD_CurrentUserProfile GetUserProfile(string userName)
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
using (DirectorySearcher ds = new DirectorySearcher(de))
{
ds.SearchScope = SearchScope.Subtree;
ds.Filter = ("(&(objectCategory=person)(objectClass=User)(samaccountname=" + userName + "))");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("manager");
ds.PropertiesToLoad.Add("directreports");
SearchResult rs = ds.FindOne();
AD_CurrentUserProfile userProfile = new AD_CurrentUserProfile();
userProfile.currentUser = GetProfileFromDN(rs.Properties["distinguishedName"][0].ToString());
userProfile.managerProfile = GetProfileFromDN(rs.Properties["manager"][0].ToString(), true);
int departmentID = db.IPACS_Department.First(v => (v.name == userProfile.currentUser.department)).departmentID;
userProfile.ipacs_department = db.IPACS_Department.Find(departmentID);
if (userProfile.currentUser.userName == userProfile.ipacs_department.owner)
{
userProfile.currentUser.isManager = true;
}
// Override for Provana and upper management
if (userProfile.currentUser.department == "Provana" || userProfile.currentUser.department == "JM")
{
userProfile.currentUser.isManager = true;
}
if (rs.Properties["DirectReports"].Count > 0)
{
if (!userProfile.currentUser.isManager)
{
userProfile.currentUser.isSupervisor = true;
}
userProfile.directReports = new HashSet<AD_User>();
foreach (string value in rs.Properties["DirectReports"])
{
userProfile.directReports.Add(GetProfileFromDN(value));
}
}
return userProfile;
}
}
I never pass 'password' anywhere in any of my code, so is this something Active Directory does by default?
A connection to AD will always require windows credentials. Your code, as posted, does not supply any credentials to AD. (You pass in a user name that you are looking up, but that is not the same as supplying credentials for the connection). This will work for users whose machines are attached to the domain...because your network credentials are passed in implicitly. For the external devs, when they VPN in, they supply credentials to the VPN protocol, which allows their machines to access your network, but that doesn't mean their machines are 'joined' to the domain...so AD will still require explicit credentials from them, including a personal password or a service account password that has permissions to access AD.
This line:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"))
essentially needs to allow a user name and password:
using (DirectoryEntry de = new DirectoryEntry("LDAP://server.com"), userName, pwd)
{
de.AuthenticationType = AuthenticationTypes.None | AuthenticationTypes.ReadonlyServer;
}
...you'll have to experiment with the AuthenticationTypes flags depending on how your network admins have it set up.

Directory Services, Search all available providers

I have the following method used for searching for a User Group either on the local computer (done first) or in the Current Forest.
public string FindUserGroup(string group)
{
//Search local computer
using (DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
//Search current forest
Forest forest = Forest.GetCurrentForest();
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
searcher.Filter = "(&(objectClass=group)(|(cn=" + group + ")(dn=" + group + ")))";
SearchResult result = searcher.FindOne();
if (result != null)
return TranslateDirectoryEntryPath(result.GetDirectoryEntry().Path);
}
}
return string.Empty;
}
My problem is that we as an example have say "domain.local" and "mydomain.local", and my current login is bound to "domain.local", then using below won't be able to find anything in "mydomain.local", even if I through the Windows User Interface is able to.
How can I search all viewable providers from my computers perspective when I don't nessesarily know them all? Do I REALLY have to do the Registry Work my self?
Edit:
One difference in the 2 domains is the "level" they are on when I in an object browser dialog chooses "Locations", it layouts as:
Computer
Entire Direction
domain.local
mydomain.local
So "mydomain.local" excists outside what is referred to as "Entire Directory", yet my computer can locate it, if that makes any difference?
I don't see a problem as this code here would have already be binded to the other domains.
foreach (Domain domain1 in forest.Domains)
{
using (DirectorySearcher searcher = new DirectorySearcher(domain1.GetDirectoryEntry()))
{
Are you trying to say that later on you're binding a DirectoryEntry on your own, and you can't find objects from other domain?

Categories