List only client computer objects in AD - c#

I want to get all client-only computer objects from AD, I mean, Windows Clients machines. I am using the sample code from this thread, but I would like to know how I can filter servers out.
Here's my code:
class Program
{
public static List<string> GetComputers()
{
List<string> ComputerNames = new List<string>();
DirectoryEntry dirEntry = new DirectoryEntry("LDAP://neuventus.local");
DirectorySearcher mySearcher = new DirectorySearcher(dirEntry);
mySearcher.Filter = ("(objectClass=computer)");
mySearcher.SizeLimit = int.MaxValue;
mySearcher.PageSize = int.MaxValue;
foreach (SearchResult resEnt in mySearcher.FindAll())
{
string ComputerName = resEnt.GetDirectoryEntry().Name;
if (ComputerName.StartsWith("CN="))
ComputerName = ComputerName.Remove(0, "CN=".Length);
ComputerNames.Add(ComputerName);
}
mySearcher.Dispose();
dirEntry.Dispose();
return ComputerNames;
}
static void Main(string[] args)
{
List<string> newList = GetComputers();
newList.ForEach(Console.WriteLine);
System.IO.File.WriteAllLines("C:\\SavedLists.txt", newList);
}
}
Can anyone help me?

Try adding an operatingSystem condition to your filter.
mySearcher.Filter = "(&(objectClass=computer)(!(operatingSystem=*server*)))";
Note that this will only work if the all your servers are running a server operating system with server in the name. Also assume that no client machines run a server operating system.

Related

Global catalog says that user from DomainA belongs to Domain Users from DomainB

I am trying to get detailed information about a user's group membership using directory services queries to the global catalog. I don't want to use GetAuthorizationGroups() because it's flaky.
There are 2 domains: DomainA and DomainB. The global catalog server is a domain controller for DomainB. Finally, there is a user (UserA) which is part of DomainA.
I find UserA in the global catalog and look at the tokenGroups property to get the SIDs of all groups to which UserA belongs.
To my great surprise, I find that DomainB\Domain Users is included in the list. Why is this being included, given that UserA is not part of DomainB?
Here is the code I'm running:
using (DirectoryEntry gc = new DirectoryEntry("GC:"))
{
string userPrincipalName = "UserA#DomainA.local";
DirectoryEntry searchRoot = null;
gc.AuthenticationType = System.DirectoryServices.AuthenticationTypes.Secure;
// There is only 1 child under "GC:".
foreach (DirectoryEntry de in gc.Children)
{
searchRoot = de;
break;
}
using (searchRoot)
{
SearchResult samResult;
using (var samSearcher = new DirectorySearcher())
{
// Find the user.
samSearcher.SearchRoot = searchRoot;
samSearcher.Filter = "(userPrincipalName=" + userPrincipalName + ")";
samSearcher.PropertiesToLoad.Add("distinguishedName");
samResult = samSearcher.FindOne();
}
List<byte[]> tokenGroups;
using (DirectoryEntry theUser = samResult.GetDirectoryEntry())
{
theUser.RefreshCache(new string[] { "tokenGroups" });
tokenGroups = theUser.Properties["tokenGroups"].Cast<byte[]>().ToList();
IdentityReferenceCollection irc = new IdentityReferenceCollection(tokenGroups.Count);
foreach (byte[] groupSidBytes in tokenGroups)
{
irc.Add(new SecurityIdentifier(groupSidBytes, 0));
}
List<string> groupNames =
irc.Translate(typeof(NTAccount), true)
.Cast<NTAccount>()
.Select(a => a.Value.ToString())
.ToList();
return groupNames;
}
}
}

Unable to get the user name and role of the user accessing the website

I am trying to get the name and role of the user who is currently accessing the web application but the code I have written fetches the server user name.
Could you please review the below code that I have written and tell a solution to this problem.
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string[] stringSeparators = new string[] { "\\" };
string[] uname = userName.Split(stringSeparators, StringSplitOptions.None);
userName = uname[1];
List<string> userRoles = new List<string>();
userRoles = getUserRole(userName);
public List<string> getUserRole(string userName)
{
List<string> userNestedMembership = new List<string>();
DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain
DirectorySearcher samSearcher = new DirectorySearcher();
samSearcher.SearchRoot = domainConnection;
samSearcher.Filter = "(samAccountName=" + userName + ")";
samSearcher.PropertiesToLoad.Add("displayName");
SearchResult samResult = samSearcher.FindOne();
if (samResult != null)
{
DirectoryEntry theUser = samResult.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 = domainConnection;
sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";
sidSearcher.PropertiesToLoad.Add("distinguishedName");
SearchResult sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
string role = (string)sidResult.Properties["distinguishedName"][0];
role = role.Substring(3, role.Length - 3);
string[] roles = role.Split(',');
userNestedMembership.Add(roles[0]);
}
}
}
}
I have not done any changes in web config.
userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
That is getting you the wndows user name, which is likely the apppool name.
Have you set thr web config and IIS to know thet you want to use windows auth?
(or if you are not, try using the HTTP Context
HttpContext.Current.User.Identity.Name
Hope this helps, or at least gives you a steer in the right direction
The issue probably doesn't lie in the code, but in environment configuration. There are specific requirements to be met, to make System.Security.Principal.WindowsIdentity.GetCurrent().Name work as you want, as it gets the user on the server side. Here is nice post describing what could be done to make IIS work under user account (with Windows Authentication).

C# Get IP of Remote Computer on Separate DNS Server

I am working on a utility for managing multiple computers in a specified domain (not necessarily the domain the computer the application is running on is a member of) within a specified directory root. The problem I am running into is once I have the collection of computer names from the external AD OU, I am unable to manage based on computer name because of DNS. Is it possible to perform DNS lookup on an external DNS server provided the IP of DNS server and credentials for that domain?
Here is the code that I have for the initialization process (works within the same domain). Really appreciate any input!
private Task InitializeComputers()
{
try
{
Computers.Clear();
object cLock = new object();
PrincipalContext context = new PrincipalContext(ContextType.Domain, CurrentConfiguration.LDAPAddress,
CurrentConfiguration.DirectoryRoot, ContextOptions.Negotiate,
CurrentConfiguration.AdminUser, CurrentConfiguration.AdminPassword);
ComputerPrincipal computer = new ComputerPrincipal(context);
computer.Name = "*";
PrincipalSearcher searcher = new PrincipalSearcher(computer);
PrincipalSearchResult<Principal> result = searcher.FindAll();
Parallel.ForEach(result, (r) =>
{
ComputerPrincipal principal = r as ComputerPrincipal;
DirectoryObject cObject = new DirectoryObject(CurrentConfiguration)
{
Name = principal.Name
};
lock (cLock)
{
Computers.Add(cObject);
}
});
}
... // Catch stuff here
}
private async Task InitializeConnections()
{
Task[] tasks = Computers.Select(x => x.CheckConnectionAsync()).ToArray();
await Task.WhenAll(tasks);
}
// This is where I need to be able to get the IP Address. Thoughts???
public Task CheckConnectionAsync()
{
return Task.Run(() =>
{
try
{
Ping PingCheck = new Ping();
PingReply Reply = PingCheck.Send(Name); // <--- need IP Address instead
if (Reply.Status == IPStatus.Success)
{
IsAvailable = true;
IPHostEntry host = Dns.GetHostEntry(Name); // Does not work for machines on different domain.
IPAddress = host.AddressList.FirstOrDefault().ToString();
}
else
{
IsAvailable = false;
IsWMIActive = false;
}
}
... // catch stuff here ...
});
}
To follow up, the solution that I found is derived from Heijden's DNS Resolver. I wrote a simple DnsManager class with a single static GetIPAddress method for extracting the IP out of an A Record.
public static string GetIPAddress(string name)
{
Resolver resolver = new Resolver();
resolver.DnsServer = ((App)App.Current).CurrentConfiguration.DNSAddress;
resolver.Recursion = true;
resolver.Retries = 3;
resolver.TimeOut = 1;
resolver.TranportType = TransportType.Udp;
Response response = resolver.Query(name, QType.A);
if (response.header.ANCOUNT > 0)
{
return ((AnswerRR)response.Answer[0]).RECORD.ToString();
}
return null;
}
Then, the updated CheckConnectionAsync method is now written like so:
public Task CheckConnectionAsync()
{
return Task.Run(() =>
{
try
{
IPAddress = DnsManager.GetIPAddress(Name + "." + CurrentConfig.DomainName);
... // check availability by IP rather than name...
}
// catch stuff here...
});
}
As soon as you plan to query dedicated server as DNS source, you have to step aside from default libs. I tried this (if remember correctly) once investigating dedicated DNS requests:
http://www.codeproject.com/Articles/12072/C-NET-DNS-query-component

Retrieve name of user from Active Directory

I am admittedly very new to AD. I have a dropdown list that I have bound with a list of members within our organization. My end goal is to find their manager name, but I'm starting with baby steps.
I've done enough searching to get the right result. I'm having a problem getting the right data (verified by using breakpoints etc) out of the result
private void cmbUserList_SelectedIndexChanged(object sender, EventArgs e)
{
var userName = cmbUserList.SelectedValue.ToString();
DirectorySearcher search = new DirectorySearcher();
search.Filter = String.Format("(cn={0})", userName);
search.PropertiesToLoad.Add("givenName");
SearchResult result = search.FindOne();
if (result != null)
{
// For now I'm trying to just retrieve their name
lblManagerName.Text = result.GetDirectoryEntry().Name;
}
}
EDIT: I'm using .net version 4.0
Could someone point me towards retrieving the correct name, and then maybe even a link or resources to pull the manager name?
I think the problem with your code is you are using "(cn={0})", userName. You need to pass fully qualified name like
CN=Doe,John,OU=Users,OU=Headquarters,DC=company,DC=net
If you only have login ID, then the code below should work
DirectorySearcher directorySearcher = new DirectorySearcher("LDAP://RootDSE");
directorySearcher.Filter = "sAMAccountName=" + acctName;
directorySearcher.PropertiesToLoad.Add("manager");
SearchResult searchResult = directorySearcher.FindOne();
if (searchResult != null)
DirectoryEntry user = searchResult.GetDirectoryEntry();
Note that acctName is Windows login ID. If you want to play with AD and check out vearious properties and how they are stored, try dsquery and dsget command line tools. The command below will return a user record based on login id and will display contents of the manager field:
dsquery user domainroot -samid "loginid" | dsget user -samid -mgr
helper class and enum
public enum ActiveDirectoryObjectClass
{
Computer,
User,
Domain,
Group,
}
public static class ActiveDirectorySearcher
{
public static string GetCurrentDomainName()
{
string result;
using (Domain domain = Domain.GetCurrentDomain())
{
result = domain.Name;
}
return result;
}
public static IEnumerable<T> Select<T>(
ActiveDirectoryObjectClass activeDirectoryObjectClass,
Func<DirectoryEntry, ActiveDirectoryObjectClass, bool> condition,
Func<DirectoryEntry, T> selector
)
{
List<T> list = new List<T>();
using (Domain domain = Domain.GetCurrentDomain())
using (DirectoryEntry root = domain.GetDirectoryEntry())
{
string filter = string.Format("(objectClass={0})", activeDirectoryObjectClass);
using (DirectorySearcher searcher = new DirectorySearcher(filter))
{
searcher.SearchRoot = root;
searcher.SearchScope = SearchScope.Subtree;
using (SearchResultCollection result = searcher.FindAll())
{
foreach (SearchResult item in result)
{
using (DirectoryEntry entry = item.GetDirectoryEntry())
{
if (condition(entry, activeDirectoryObjectClass))
{
list.Add(selector(entry));
}
}
}
}
}
}
return list;
}
}
how to use
public IEnumerable<User> GetUsers()
{
return ActiveDirectorySearcher.Select(
ActiveDirectoryObjectClass.User,
(entry, adObjectClass) => string.Compare(entry.SchemaClassName, adObjectClass.ToString(), StringComparison.InvariantCultureIgnoreCase) == 0,
_ => new User
{
Name = _.Name.Substring(3),
Domain = ActiveDirectorySearcher.GetCurrentDomainName(),
});
}
Note: User in sample - custom class with properties Name, Domain, etc.
to find name and/or manager name:
if (sResult != null)
{
string userName = sResult.Properties["name"][0].ToString();
string managerDN = sResult.Properties["manager"][0].ToString();
DirectoryEntry man = new DirectoryEntry("LDAP://server_name/"+managerDN);
string managerName = man.Properties["name"][0].ToString();
}
server_name can be just domain component of FQDN i.e yourcompany.com, that way it will find catalog server on its own via DNS.
Edit:
I also recomend Active Directory Explorer from Sysinternals. It is great tool for exploring and understanding structure of AD

How to Determine Which Printers are Connected using WMI

I've been trying to find a way to figure out which installed printers are 'connected'. After some Googling I figured I had to dive into WMI.
So I've built this test:
// Struct to store printer data in.
public struct MyPrinter
{
public string Availability;
public string ExtendedPrinterStatus;
public string Name;
public string PrinterStatus;
public string Status;
public string StatusInfo;
public MyPrinter(string a, string eps, string n, string ps, string s, string si)
{
Availability = a;
ExtendedPrinterStatus = eps;
Name = n;
PrinterStatus = ps;
Status = s;
StatusInfo = si;
}
}
var installedPrinters = new string[numPrinters];
PrinterSettings.InstalledPrinters.CopyTo(installedPrinters, 0);
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer");
var data = new List<MyPrinter>();
foreach (var printer in searcher.Get())
{
if (installedPrinters.Contains(printer["Name"].ToString()))
{
var availability = (printer["Availability"] ?? "").ToString();
var extendedPrinterStatus = (printer["ExtendedPrinterStatus"] ?? "").ToString();
var name = (printer["Name"] ?? "").ToString();
var printerStatus = (printer["PrinterStatus"] ?? "").ToString();
var status = (printer["Status"] ?? "").ToString();
var statusInfo = (printer["StatusInfo"] ?? "").ToString();
data.Add(new MyPrinter(availability, extendedPrinterStatus, name, printerStatus, status, statusInfo));
}
}
I have 6 printers from which 2 are network printers. I've run this with all printers connected and all results looked like this:
Availability = "" // printer["Availability"] = null
ExtendedPrinterStatus = "2" // 2 = Unknown
Name = "{printer name here}"
PrinterStatus = "3" // 3 = Idle
Status = "Unknown"
StatusInfo = "" // Null
So the only difference between the printers is the name.
I ran the script again but this time I disconnected my laptop from the network. So 2 of the printers were not connected anymore in this case.
The strange thing (for me) is, the results were exactly the same.
The reason I ran this test is, to figure out which field I'd need to use for my case.
So at the end, I have not been able to figure out how to figure out if a printer is connected or not.
So what I'd like, is a way to figure out the installed + connected printers in C#. If there is a way to do it without the use of WMI classes, that's also fine by me, as long as it works.
Me and a colleague have tried lots of stuff to find a solution for this and we figured this worked:
private string[] GetAvailablePrinters()
{
var installedPrinters = new string[PrinterSettings.InstalledPrinters.Count];
PrinterSettings.InstalledPrinters.CopyTo(installedPrinters, 0);
var printers = new List<string>();
var printServers = new List<string>();
var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer");
foreach (var printer in searcher.Get())
{
var serverName = #"\\" + printer["SystemName"].ToString().TrimStart('\\');
if (!printServers.Contains(serverName))
printServers.Add(serverName);
}
foreach (var printServer in printServers)
{
var server = new PrintServer(printServer);
try
{
var queues = server.GetPrintQueues();
printers.AddRange(queues.Select(q => q.Name));
}
catch (Exception)
{
// Handle exception correctly
}
}
return printers.ToArray();
}
The trick is that when a printserver is not available, GetPrintQueues will throw some specific exception. By only adding the printers that don't throw such an exception, we get a list of all the connected printers. This doesn't check if a printer is turned on/off because that actually doesn't matter. If it is turned off, the document will just be placed in the print queue and it can be printed later on.
I hope this helps others who bump into this problem.
Sidenote:
The reason I decided not to catch that specific exception, is because I would have to reference a dll just for that exception.

Categories