How to get all members of a local WinNT group? - c#

When I retrieve members of a local WinNT group, someway somehow not all members are returned. I do add:
Active Directory users
Active Directory groups
Both successful (see picture), but only the users show up afterwards.
Question is:
What happens to added groups?
See last method in code sample 'GetMembers()'
Is this a known issue?
Any workaround available?
Many thanks!!
string _domainName = #"MYDOMAIN";
string _basePath = #"WinNT://MYDOMAIN/myserver";
string _userName = #"MYDOMAIN\SvcAccount";
string _password = #"********";
void Main()
{
CreateGroup("lg_TestGroup");
AddMember("lg_TestGroup", #"m.y.username");
AddMember("lg_TestGroup", #"Test_DomainGroup");
GetMembers("lg_TestGroup");
}
// Method added for reference.
void CreateGroup(string accountName)
{
using (DirectoryEntry rootEntry = new DirectoryEntry(_basePath, _userName, _password))
{
DirectoryEntry newEntry = rootEntry.Children.Add(accountName, "group");
newEntry.CommitChanges();
}
}
// Add Active Directory member to the local group.
void AddMember(string groupAccountName, string userName)
{
string path = string.Format(#"{0}/{1}", _basePath, groupAccountName);
using (DirectoryEntry entry = new DirectoryEntry(path, _userName, _password))
{
userName = string.Format("WinNT://{0}/{1}", _domainName, userName);
entry.Invoke("Add", new object[] { userName });
entry.CommitChanges();
}
}
// Get all members of the local group.
void GetMembers(string groupAccountName)
{
string path = string.Format(#"{0}/{1}", _basePath, groupAccountName);
using (DirectoryEntry entry = new DirectoryEntry(path, _userName, _password))
{
foreach (object member in (IEnumerable) entry.Invoke("Members"))
{
using (DirectoryEntry memberEntry = new DirectoryEntry(member))
{
string accountName = memberEntry.Path.Replace(string.Format("WinNT://{0}/", _domainName), string.Format(#"{0}\", _domainName));
Console.WriteLine("- " + accountName); // No groups displayed...
}
}
}
}
Update #1
The sequence of the group members seems to be essential. As soon as the enumerator in GetMembers() stumbles on an Active Directory group, the remaining items are not displayed either. So if 'Test_DomainGroup' is listed first in this example, GetMembers() does not display anything at all.

I know it's an old question and you've likely found the answers you need, but just in case someone else stumbles accross this...
The WinNT ADSI provider you're using in your DirectoryEntry [ie. WinNT://MYDOMAIN/myserver] has pretty limited capabilities for working with Windows Domains that are not stuck in the old Windows 2000/NT functional level (https://support.microsoft.com/en-us/kb/322692).
In this case the problem is that the WinNT provider doesn't know how to handle Global or Universal security groups (which didn't exist in Windows NT and are activated as soon as you raise your domain level above Windows 2000 mixed mode). So, if any groups of those types are nested under a local group you'll generally have problems like the one you described.
The only solution/workaround I've found is to determine if the group you're enumerating is from a domain and if so, then switch to the LDAP provider which will display all members properly when invoking "Members".
Unfortunately I don't know of an "easy" way to just switch from using the WinNT provider to using the LDAP provider using the DirectoryEntry you already have bound to the WinNT provider. So, in the projects I've worked on, I generally prefer to get the SID of the current WinNT object and then use LDAP to search for domain objects with that same SID.
For Windows 2003+ domains you can convert your SID byte array to the usual SDDL format (S-1-5-21...) and then bind to an object with a matching SID using something like:
Byte[] SIDBytes = (Byte[])memberEntry.Properties["objectSID"].Value;
System.Security.Principal.SecurityIdentifier SID = new System.Security.Principal.SecurityIdentifier(SIDBytes, 0);
memberEntry.Dispose();
memberEntry = new DirectoryEntry("LDAP://" + _domainName + "/<SID=" + SID.ToString() + ">");
For Windows 2000 domains you can't bind directly to an object by SID. So, you have to convert your SID byte array to an array of the hex values with a "\" prefix (\01\06\05\16\EF\A2..) and then use the DirectorySearcher to find an object with a matching SID. A method to do this would look something like:
public DirectoryEntry FindMatchingSID(Byte[] SIDBytes, String Win2KDNSDomainName)
{
using (DirectorySearcher Searcher = new DirectorySearcher("LDAP://" + Win2KDNSDomainName))
{
System.Text.StringBuilder SIDByteString = new System.Text.StringBuilder(SIDBytes.Length * 3);
for (Int32 sidByteIndex = 0; sidByteIndex < SIDBytes.Length; sidByteIndex++)
SIDByteString.AppendFormat("\\{0:x2}", SIDBytes[sidByteIndex]);
Searcher.Filter = "(objectSid=" + SIDByteString.ToString() + ")";
SearchResult result = Searcher.FindOne();
if (result == null)
throw new Exception("Unable to find an object using \"" + Searcher.Filter + "\".");
else
return result.GetDirectoryEntry();
}
}

Related

How to Get the list of all User Accounts of Windows in C#? [duplicate]

How can I get a list of users from active directory? Is there a way to pull username, firstname, lastname? I saw a similar post where this was used:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
I have never done anything with active directory so I am completely lost. Any help would be greatly appreciated!
If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.
Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory
The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.
There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.
For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
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();
}
}
}
Console.ReadLine();
Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.
If you want to filter y active accounts add this to Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
after the first using. Then add
searcher.QueryFilter = userPrin;
before the find all. And that should get you the active ones.
PrincipalContext for browsing the AD is ridiculously slow (only use it for .ValidateCredentials, see below), use DirectoryEntry instead and .PropertiesToLoad() so you only pay for what you need.
Filters and syntax here:
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Attributes here:
https://learn.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://{Domain}"))
{
using (var searcher = new DirectorySearcher(root))
{
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={username}))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
{
// ... iterate over all the groups the user is a member of
}
}
}
Clean, simple, fast. No magic, no half-documented calls to .RefreshCache to grab the tokenGroups or to .Bind or .NativeObject in a try/catch to validate credentials.
For authenticating the user:
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(username, password);
}
Certainly the credit goes to #Harvey Kwok here, but I just wanted to add this example because in my case I wanted to get an actual List of UserPrincipals. It's probably more efficient to filter this query upfront, but in my small environment, it's just easier to pull everything and then filter as needed later from my list.
Depending on what you need, you may not need to cast to DirectoryEntry, but some properties are not available from UserPrincipal.
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
{
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
{
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
}
}
Include the System.DirectoryServices.dll, then use the code below:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName == "User")
{
userNames += child.Name + Environment.NewLine ;
}
}
MessageBox.Show(userNames);

Adding custom attributes in Active Directory

Can anyone help me to find out how to add custom attributes/classes to Active Directory schema using C#? I can add manually by using MMC /a. But now I want to do it using C# code. Can anyone provide me the sample for this?
Thanks in advance!
This is a very big HOW TO:
https://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#6
Target Specific Domain Controllers or Credentials
Everywhere in the code that you see: LDAP:// you can replace with LDAP://MyDomainControllerNameOrIpAddress as well as everywhere you see a DirectoryEntry class being constructed you can send in specific credentials as well. This is especially helpful if you need to work on an Active Directory for which your machine is not a member of it's forest or domain or you want to target a DC to make the changes to.
//Rename an object and specify the domain controller and credentials directly
public static void Rename(string server,
string userName, string password, string objectDn, string newName)
{
DirectoryEntry child = new DirectoryEntry("LDAP://" + server + "/" +
objectDn, userName, password);
child.Rename("CN=" + newName);
}
Managing local accounts with DirectoryEntry
It is important to note that you can execute some of these methods against a local machine as opposed to an Active Directory if needed by simply replacing the LDAP:// string with WinNT:// as demonstrated below
//create new local account
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" +
Environment.MachineName);
DirectoryEntry newUser = localMachine.Children.Add("localuser", "user");
newUser.Invoke("SetPassword", new object[] { "3l!teP#$$w0RDz" });
newUser.CommitChanges();
Console.WriteLine(newUser.Guid.ToString());
localMachine.Close();
newUser.Close();
Managing local groups with DirectoryEntry
A few configuration changes need to be made to the code but it's pretty straightforward. Below you can see an example of using DirectoryEntry to enumerate the members of the local "administrator" group.
DirectoryEntry localMachine = new DirectoryEntry
("WinNT://" + Environment.MachineName + ",Computer");
DirectoryEntry admGroup = localMachine.Children.Find
("administrators", "group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
Console.WriteLine(member.Name);
}
Managing IIS with DirectoryEntry
In addition to managing local & directory services accounts, the versatile DirectoryEntry object can manage other network providers as well, such as IIS. Below is an example of how you can use DirectoryEntry to provision a new virtual directory in IIS.
//Create New Virtual Directory in IIS with DirectoryEntry()
string wwwroot = "c:\\Inetpub\\wwwroot";
string virtualDirectoryName = "myNewApp";
string sitepath = "IIS://localhost/W3SVC/1/ROOT";
DirectoryEntry vRoot = new DirectoryEntry(sitepath);
DirectoryWntry vDir = vRoot.Children.Add(virtualDirectoryName,
"IIsWebVirtualDir");
vDir.CommitChanges();
vDir.Properties["Path"].Value = wwwroot + "\\" + virtualDirectoryName;
vDir.Properties["DefaultDoc"].Value = "Default.aspx";
vDir.Properties["DirBrowseFlags"].Value = 2147483648;
vDir.Commitchanges();
vRoot.CommitChanges();
NOW Active Directory Objects:
Enumerate Multi-String Attribute Values of an Object
This method includes a recursive flag in case you want to recursively dig up properties of properties such as enumerating all the member values of a group and then getting each member group's groups all the way up the tree.
public ArrayList AttributeValuesMultiString(string attributeName,
string objectDn, ArrayList valuesCollection, bool recursive)
{
DirectoryEntry ent = new DirectoryEntry(objectDn);
PropertyValueCollection ValueCollection = ent.Properties[attributeName];
IEnumerator en = ValueCollection.GetEnumerator();
while (en.MoveNext())
{
if (en.Current != null)
{
if (!valuesCollection.Contains(en.Current.ToString()))
{
valuesCollection.Add(en.Current.ToString());
if (recursive)
{
AttributeValuesMultiString(attributeName, "LDAP://" +
en.Current.ToString(), valuesCollection, true);
}
}
}
}
ent.Close();
ent.Dispose();
return valuesCollection;
}
Enumerate Single String Attribute Values of an Object
public string AttributeValuesSingleString
(string attributeName, string objectDn)
{
string strValue;
DirectoryEntry ent = new DirectoryEntry(objectDn);
strValue = ent.Properties[attributeName].Value.ToString();
ent.Close();
ent.Dispose();
return strValue;
}
Enumerate an Object's Properties: The Ones with Values
public static ArrayList GetUsedAttributes(string objectDn)
{
DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + objectDn);
ArrayList props = new ArrayList();
foreach (string strAttrName in objRootDSE.Properties.PropertyNames)
{
props.Add(strAttrName);
}
return props;
}
Interesting that neither the proposed answer nor the example there by the question author really answer the original question:
How to add custom attributes/classes to Active Directory schema in C#?
By this question, I understand, adding an attribute which is NOT YET present in the AD Schema. I doubt it is supported via C# code when you see that adding an attributes actually means updating the AD schema:
https://social.technet.microsoft.com/wiki/contents/articles/20319.how-to-create-a-custom-attribute-in-active-directory.aspx

How to find what user last logged onto a given computer through Active Directory in C#?

I am trying to programmatically find who last logged onto a given computer and when with C#. Given the name of a computer as a string, I have learned about Getting last Logon Time on Computers in Active Directory. However, there doesn't seem to be a property for which user was the one that actually logged in. Do I have to take a different approach for this? Anything I found online that was remotely related to this was in VBScript, but this must be done in C#.
Simply query the necessary information from the System Registry. The following method will set the Registry View based on whether the machine is 64-bit or 32-bit - although if you're doing this remotely - then the approach to obtain this information may need to be altered, but the general approach should be the same.
The Base Key is selected using the name of the machine that you pass an argument along with the Registry View and of course the Registy Hive as Local Machine. Then you open up the Base Key and finally the necessary Sub Key where the information you desire resides.
The location where that information is contained is:
SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI
And from there grab the value from LastLoggedOnUser.
Here is the code in C#:
private static string GetLastUserLoggedOn(string machineName)
{
string location = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI";
var registryView = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
using (var hive = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName, registryView))
{
using (var key = hive.OpenSubKey(location))
{
var item = key.GetValue("LastLoggedOnUser");
string itemValue = item == null ? "No Logon Found" : item.ToString();
return itemValue;
}
}
}
Here is some code I found:
using System;
// has DateTime class
using System.Collections.Generic;
// has the Dictionary class
using System.DirectoryServices;
// has all the LDAP classes such as DirectoryEntry
using ActiveDs;
// has the IADsLargeInteger class
// Get the root entry
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
string configurationNamingContext =
(string)rootDSE.Properties["configurationNamingContext"].Value;
string defaultNamingContext =
(string)rootDSE.Properties["defaultNamingContext"].Value;
// Get all the domain controllers
// Get all the domain controllers
DirectoryEntry deConfig =
new DirectoryEntry("LDAP://" + configurationNamingContext);
DirectorySearcher dsConfig = new DirectorySearcher(deConfig);
dsConfig.Filter = "(objectClass=nTDSDSA)";
foreach (SearchResult srDomains in dsConfig.FindAll())
{
DirectoryEntry deDomain = srDomains.GetDirectoryEntry();
if (deDomain != null)
{
string dnsHostName =
deDomain.Parent.Properties["DNSHostName"].Value.ToString();
// Get all the users for that domain
}
}
// Get all the users for that domain
DirectoryEntry deUsers =
new DirectoryEntry("LDAP://" + dnsHostName + "/" + defaultNamingContext);
DirectorySearcher dsUsers = new DirectorySearcher(deUsers);
dsUsers.Filter = "(&(objectCategory=person)(objectClass=user))";
foreach (SearchResult srUsers in dsUsers.FindAll())
{
DirectoryEntry deUser = srUsers.GetDirectoryEntry();
if (deUser != null)
{
// Get the distinguishedName and lastLogon for each user
// Save the most recent logon for each user in a Dictionary object
}
}
//Create Dictionary
Dictionary<string, Int64> lastLogons = new Dictionary<string, Int64>();
// Get the distinguishedName and lastLogon for each user
string distinguishedName =
deUser.Properties["distinguishedName"].Value.ToString();
Int64 lastLogonThisServer = new Int64();
if (deUser.Properties["lastLogon"].Value != null)
{
IADsLargeInteger lgInt =
(IADsLargeInteger)deUser.Properties["lastLogon"].Value;
lastLogonThisServer = ((long)lgInt.HighPart << 32) + lgInt.LowPart;
}
// Save the most recent logon for each user in a Dictionary object
if (lastLogons.ContainsKey(distinguishedName))
{
if (lastLogons[distinguishedName] < lastLogonThisServer)
{
lastLogons[distinguishedName] = lastLogonThisServer;
}
}
else
{
lastLogons.Add(distinguishedName, lastLogonThisServer);
}
//Get the time
// Convert the long integer to a DateTime value
string readableLastLogon =
DateTime.FromFileTime(lastLogonThisServer).ToString();
Here is the website where all of this code came from. The developer explained the code in detail.
http://www.codeproject.com/Articles/19181/Find-LastLogon-Across-All-Windows-Domain-Controlle

How can I get a list of users from active directory?

How can I get a list of users from active directory? Is there a way to pull username, firstname, lastname? I saw a similar post where this was used:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
I have never done anything with active directory so I am completely lost. Any help would be greatly appreciated!
If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.
Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory
The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.
There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.
For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
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();
}
}
}
Console.ReadLine();
Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.
If you want to filter y active accounts add this to Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
after the first using. Then add
searcher.QueryFilter = userPrin;
before the find all. And that should get you the active ones.
PrincipalContext for browsing the AD is ridiculously slow (only use it for .ValidateCredentials, see below), use DirectoryEntry instead and .PropertiesToLoad() so you only pay for what you need.
Filters and syntax here:
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Attributes here:
https://learn.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://{Domain}"))
{
using (var searcher = new DirectorySearcher(root))
{
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={username}))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
{
// ... iterate over all the groups the user is a member of
}
}
}
Clean, simple, fast. No magic, no half-documented calls to .RefreshCache to grab the tokenGroups or to .Bind or .NativeObject in a try/catch to validate credentials.
For authenticating the user:
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(username, password);
}
Certainly the credit goes to #Harvey Kwok here, but I just wanted to add this example because in my case I wanted to get an actual List of UserPrincipals. It's probably more efficient to filter this query upfront, but in my small environment, it's just easier to pull everything and then filter as needed later from my list.
Depending on what you need, you may not need to cast to DirectoryEntry, but some properties are not available from UserPrincipal.
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
{
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
{
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
}
}
Include the System.DirectoryServices.dll, then use the code below:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName == "User")
{
userNames += child.Name + Environment.NewLine ;
}
}
MessageBox.Show(userNames);

How to check if Windows user account name exists in domain?

What is the simplest and most efficient way in C# to check if a Windows user account name exists? This is in a domain environment.
Input: user name in [domain]/[user] format (e.g. "mycompany\bob")
Output: True if the user name exists, false if not.
I did find this article but the examples there are related to authenticating and manipulating user accounts, and they assume you already have a user distinguished name, whereas I am starting with the user account name.
I'm sure I can figure this out using AD, but before I do so I was wondering if there is a simple higher level API that does what I need.
* UPDATE *
There are probably many ways to do this, Russ posted one that could work but I couldn't figure out how to tweak it to work in my environment. I did find a different approach, using the WinNT provider that did the job for me:
public static bool UserInDomain(string username, string domain)
{
string path = String.Format("WinNT://{0}/{1},user", domain, username);
try
{
DirectoryEntry.Exists(path);
return true;
}
catch (Exception)
{
// For WinNT provider DirectoryEntry.Exists throws an exception
// instead of returning false so we need to trap it.
return false;
}
}
P.S.
For those who aren't familiar with the API used above: you need to add a reference to System.DirectoryServices to use it.
The link I found that helped me with this: How Can I Get User Information Using ADSI
The examples use ADSI but can be applied to .NET DirectoryServices as well. They also demonstrate other properties of the user object that may be useful.
The System.DirectoryServices namespace in the article is exactly what you need and intended for this purpose. If I recall correctly, it is a wrapper around the Active Directory Server Interfaces COM interfaces
EDIT:
Something like the following should do it (it could probably do with some checking and handling). It will use the domain of the current security context to find a domain controller, but this could easily be amended to pass in a named server.
public bool UserInDomain(string username, string domain)
{
string LDAPString = string.Empty;
string[] domainComponents = domain.Split('.');
StringBuilder builder = new StringBuilder();
for (int i = 0; i < domainComponents.Length; i++)
{
builder.AppendFormat(",dc={0}", domainComponents[i]);
}
if (builder.Length > 0)
LDAPString = builder.ToString(1, builder.Length - 1);
DirectoryEntry entry = new DirectoryEntry("LDAP://" + LDAPString);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "sAMAccountName=" + username;
SearchResult result = searcher.FindOne();
return result != null;
}
and tested with the following
Console.WriteLine(UserInDomain("username","MyDomain.com").ToString());
Found a simple way to do this if you're on a high enough framework version:
using System.DirectoryServices.AccountManagement;
bool UserExists(string userName, string domain) {
using (var pc = new PrincipalContext(ContextType.Domain, domain))
using (var p = Principal.FindByIdentity(pc, IdentityType.SamAccountName, userName)) {
return p != null;
}
}

Categories

Resources