Retrieve users from specific group and OU in Active Dirctory - c#

I want to retrieve all users in a given group from specific OU in Active Directory. My code throws an exception
The operation was aborted because the client side timeout limit was exceeded
I get this exception at
foreach (SearchResultEntry entry in searchResponse.Entries)
My group name is Arya and OU name is TestOU
However when I write my filter as
string searchFilter = "(&(objectCategory=user)"
it works and returns users from all OU's, I think which I do not want.
bool bMoreData = false;
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://" + domain);
string[] attributes = { "samaccountname", "displayname", "name", "initials" };
System.Net.NetworkCredential credential = new System.Net.NetworkCredential(admin, password, "IP address");
LdapDirectoryIdentifier directoryIdentifier = new LdapDirectoryIdentifier("ip address"); //389 (unsecured LDAP)
LdapConnection connection = new LdapConnection(directoryIdentifier, credential);
connection.Bind();
string searchFilter = "(&(objectCategory=user)(memberOf=cn=Arya,ou=TestOU,dc=Maintenance,dc=org))";
SearchRequest request = new SearchRequest("DC=Maintenance,DC=org", searchFilter, System.DirectoryServices.Protocols.SearchScope.Base, attributes);
// getCookie();
DirSyncRequestControl dirSyncRC = new DirSyncRequestControl(cookie, System.DirectoryServices.Protocols.DirectorySynchronizationOptions.IncrementalValues, Int32.MaxValue);
request.Controls.Add(dirSyncRC);
SearchResponse searchResponse = (SearchResponse)connection.SendRequest(request);
foreach (SearchResultEntry entry in searchResponse.Entries)// Exception thrown here
{
Console.WriteLine("{0}:{1}",
searchResponse.Entries.IndexOf(entry),
entry.DistinguishedName);
}
foreach (DirectoryControl control in searchResponse.Controls)
{
if (control is DirSyncResponseControl)
{
DirSyncResponseControl dsrc = control as DirSyncResponseControl;
cookie = dsrc.Cookie;
bMoreData = dsrc.MoreData;
break;
}
}

I found problem with the below line
DirSyncRequestControl dirSyncRC = new DirSyncRequestControl(cookie, System.DirectoryServices.Protocols.DirectorySynchronizationOptions.IncrementalValues, Int32.MaxValue);
when replaced it with it worked for me.
DirSyncRequestControl dirSyncRC = new DirSyncRequestControl(cookie, System.DirectoryServices.Protocols.DirectorySynchronizationOptions.ObjectSecurity, Int32.MaxValue);

You can bind to a PrincipalContext for your OU, and then find that group you're looking for:
// create your domain context - bind to the OU you're interested in
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, "OU=TestOU"))
{
// define a "query-by-example" principal - here, we search for any GroupPrincipal
GroupPrincipal group = ctx.FindByIdentity("Arya");
// if group is found - enumerate its members
if(group != null)
{
foreach(var found in group.GetMembers())
{
//
}
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 (downloadable .CHM file from Microsoft - January 2008 issue of MSDN Magazine) which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.

Related

With access to Active Directory, can I get an object GUID for a user from an username/email address alone?

Background
I've been experimenting with active directory access in C# to find out how to connect/validate credentials in various ways. At the bottom of this answer I've included some code snippets to give an idea of what I've done, maybe this can be built upon to fulfill my aim.
Main Aim
If I have valid credentials for connecting to an Active Directory, can I take an string representing a username/email address (assuming it exists in the userPrincipalName or similar field), and get back the objectGUID?
Or do I need to take other things into account like: the permissions those credentials have to search other users; knowledge of the structure of different ADs; if userPrincipalName is the correct field to search?
Code Snippets (experimental beginnings, not fully functional for my aim)
var credentials = new NetworkCredential(username, password, hostname);
var serverId = new LdapDirectoryIdentifier(hostname);
var connection = new LdapConnection(serverId, credentials);
try
{
connection.Bind();
}
catch (Exception e)
{
//error
Console.WriteLine(e);
connection.Dispose();
return;
}
//success
var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", hostname, baseDn), username, password);
var searcher = new DirectorySearcher(dirEntry)
{
Filter = "(&(&(objectClass=user)(objectClass=person)))"
};
var resultCollection = searcher.FindAll();
searcher.Dispose();
You're on the right track with DirectorySeacher. You just need a proper query, and a few other tweaks.
Modify the Filter so you find what you're looking for.
a. If you have the email address:
(&(objectClass=user)(objectClass=person)(mail=email#example.com))
Or, (&(objectClass=user)(objectClass=person)(proxyAddresses=smtp:email#example.com)) (this will match against secondary email addresses too)
b. If you have a username, it depends which username you have.
User Principal Name: (&(objectClass=user)(objectClass=person)(userPrincipalName=username#example.com))
What is normally called the "username", which is often formatted like DOMAIN\username: (&(objectClass=user)(objectClass=person)(sAMAccountName=myusername))
Use DirectorySeacher.PropertiesToLoad. If you don't, it will retrieve every attribute that has a value, which is just wasted network traffic.
You don't need to dispose the DirectorySearcher, but you do need to dispose resultCollection since the documentation says you can end up with a memory leak if you leave it up to garbage collection.
So, assuming you have the userPrincipalName, you would have something like this:
var userToLookFor = "username#example.com";
var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", hostname, baseDn), username, password);
var searcher = new DirectorySearcher(dirEntry)
{
Filter = $"(&(objectClass=user)(objectClass=person)(userPrincipalName={userToLookFor}))",
SizeLimit = 1 //we're only looking for one account
};
searcher.PropertiesToLoad.Add("objectGuid");
using (var resultCollection = searcher.FindAll())
{
if (resultCollection.Count == 1)
{
var userGuid = new Guid((byte[]) resultCollection[0].Properties["objectGuid"][0]);
}
else
{
//the account was not found - do something else
}
}

Why is my LDAP query failing?

On my development machine I have a series of VM's. One of which is a domain controller. The domain controller is indeed working because I cannot login to other VM's without authenticating to it.
I am trying to test an LDAP query against this DC and it keeps failing
MY DOMAIN CONTROLLER TREE LOOKS LIKE:
DC Machine Name = ESDEV-DC01
Active Directory Name = ESDEV.COM
Canonical Name of Target Node = ESDEV.COM/Users
MY SUBTREE TARGETS LOOK LIKE:
Attribute Name = objectCategory
Attribute Value = CN=Person,CN=Schema,CN=Configuration,DC=ESDEV,DC=COM
MY PARAMETERS ARE:
DirectoryPath = "LDAP://OU=Users, DC=ESDEV-DC01,DC=ESDEV,DC=Com"
SearchFilter = "(&(objectCategory=Person))"
QUESTIONS:
I keep getting "there is no such object on the server".
Does this mean it is finding the server directory?
Why is the query failing?
Is the LDAP query case sensitive?
MY CONSOLE APP CODE LOOKS LIKE:
I think my question can be answered without this piece, but for those who care about the code I am using to test the query...
namespace LDAPQueryTester
{
class Program
{
static void Main(string[] args)
{
try
{
string directoryPath = ConfigurationManager.AppSettings["DirectoryPath"];
string searchFilter = ConfigurationManager.AppSettings["SearchFilter"];
DirectoryEntry rootEntry = new DirectoryEntry(directoryPath);
DirectorySearcher srch = new DirectorySearcher(rootEntry);
srch.SearchScope = SearchScope.Subtree;
if (searchFilter.Length > 0)
{
srch.Filter = searchFilter;
}
SearchResultCollection res = srch.FindAll();
if (res.Count <= 0)
{
Console.WriteLine("Your query did NOT return results");
}
else
{
Console.WriteLine("Your query returned results");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.ReadLine();
}
}
}
As far as I remember, the Users is a generic container - not an OU - so you should try this LDAP path:
LDAP://CN=Users,DC=ESDEV-DC01,DC=ESDEV,DC=Com
Note: CN=Users instead of OU=Users.
And the LDAP prefix MUST be in all uppercase
But if you're on .NET 3.5 or higher, I would recommend to look at the new System.DirectoryServices.AccountManagement namespace which makes a lot of things a lot easier to use!
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "ESDEV.COM", "CN=Users, DC=ESDEV-DC01,DC=ESDEV,DC=Com"))
{
// define a "query-by-example" principal - here, we search for a UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// 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.....
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.

An operation error occured enumerating DirectoryEntry group

I've been going through previous post trying to resolve this issue all morning, but none of them seem to work.
I have been righting a user management interface for AD for our course admins, the idea being to only display exactly what they need, while the solution works fine on the dev servers, i get the above error on prod.
I have tried every thing i can find, like HostingEnvironment.Impersonate, promoting the service account to a domain admin, but noting works.
public static List<GroupPrincipal> GetGroups(string client)
{
List<GroupPrincipal> List = new List<GroupPrincipal>();
DirectoryEntry ou = null;
GroupPrincipal group = null;
PrincipalContext context = null;
if (domain.Path.ToLower().Contains(DevDN.ToLower()))
{
context = new PrincipalContext(ContextType.Domain,
DevDom,
DevDN,
DevService,
DevServicePass);
}
else
{
context = new PrincipalContext(
ContextType.Domain,
LiveDom,
LiveDN,
LiveService,
LiveServicePass);
}
DirectorySearcher searcher = new DirectorySearcher(domain, "(&(ou=" + client + ")(objectClass=organizationalUnit))");
try
{
ou = new DirectoryEntry(searcher.FindOne().Path);
}
catch (System.Exception ex)
{
Log.WriteError("SUGM.ADLink.GetGroups", "Unable to locate client: " + ex.Message);
List = null;
return List;
}
try
{
foreach (DirectoryEntry groups in ou.Children)
{
if (groups.SchemaClassName == "group")
{
string name = groups.Name.Replace("CN=", "");
group = GroupPrincipal.FindByIdentity(context, name);
List.Add(group);
}
}
}
catch (System.Exception ex)
{
Log.WriteError("SUGM.ADLink.GetGroups", "Unable to add groups to list: " + ex.Message);
List = null;
return List;
}
return List;
}
While debugging I have check and all the correct values are being passed, but it alway fails on the foreach block.
Can anyone point out what I'm doing wrong.
Cheers
You should avoid mixing the System.DirectoryServices and System.DirectoryServices.AccountManagement namespaces - that's not a very good strategy!
You can do all you want in S.DS.AM (.NET 3.5), too! And much easier, too.
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context and specify the initial container to work from
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "OU=YourStartingPoint,DC=YourCompany,DC=com");
// define a "query-by-example" principal - here, we search for a GroupPrincipal
GroupPrincipal qbeGroup = new GroupPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeGroup);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.

Get a list of all computers and also if it is logged into AD

I'm trying to recive all computers in my AD and also which of them whos currently logged in. I've tryed doing this by checking the "lastLogonStamp" but that returns the wrong value, saying my server was logged into AD eight days ago. Even if I restart the server it says the same. I got the code from another question here:
How to list all computers and the last time they were logged onto in AD?
public DataTable GetListOfComputers(string domain, string userName, string password)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,
userName, password, AuthenticationTypes.Secure);
DirectorySearcher search = new DirectorySearcher(entry);
string query = "(objectclass=computer)";
search.Filter = query;
search.PropertiesToLoad.Add("name");
search.PropertiesToLoad.Add("lastLogonTimestamp");
SearchResultCollection mySearchResultColl = search.FindAll();
DataTable results = new DataTable();
results.Columns.Add("name");
results.Columns.Add("lastLogonTimestamp");
foreach (SearchResult sr in mySearchResultColl)
{
DataRow dr = results.NewRow();
DirectoryEntry de = sr.GetDirectoryEntry();
dr["name"] = de.Properties["Name"].Value;
dr["lastLogonTimestamp"] = DateTime.FromFileTimeUtc(long.Parse(sr.Properties["lastLogonTimestamp"][0].ToString()));
results.Rows.Add(dr);
de.Close();
}
return results;
}
If you're using .NET 3.5 and up, 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 ComputerPrincipal
ComputerPrincipal qbeComputer = new ComputerPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeComputer);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
ComputerPrincipal cp = found as ComputerPrincipal;
if(cp != null)
{
string computerName = cp.Name;
DateTime lastLogon = cp.LastLogon;
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.

how to delete computer account from Active Directory using c#

Is there any sample that deletes computer account from AD using C#?
I have searched many sources, but all are about user account.
added my code here, i always got errors for some reason.
public static bool checkExistingPC(string compName,string userName,string userPwd )
{
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://test.com",userName,userPwd,AuthenticationTypes.Secure);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=computer)(|(cn=" + compName + ")(dn=" + compName + ")))";
foreach (SearchResult result in mySearcher.FindAll())
{
if (result != null)
{
MessageBox.Show("computer GetDirectoryEntry():" + result.Path+"\n"+"computer path: "+result.Path);
DirectoryEntry entryToRemove = new DirectoryEntry(result.Path,userName,userPwd);
entry.Children.Remove(entryToRemove);
return true;
}
else
{
return false;
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
return false;
}
If you're on .NET 3.5 and up (if you're not - time to upgrade!), 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
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find the computer in question
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity(ctx, "NAME");
// if found - delete it
if (computer != null)
{
computer.Delete();
}
The new S.DS.AM makes it really easy to play around with users, computers and groups in AD!
Using ADSI which is under System.DirectoryServices use a commit mechanism, here is a working sample :
/* Retreiving RootDSE infos
*/
string ldapBase = "LDAP://WM2008R2ENT:389/";
string sFromWhere = ldapBase + "rootDSE";
DirectoryEntry root = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
string defaultNamingContext = root.Properties["defaultNamingContext"][0].ToString();
/* Retreiving the computer to remove
*/
sFromWhere = ldapBase + defaultNamingContext;
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", ".biènèsph^r^.1966");
DirectorySearcher dsLookForDomain = new DirectorySearcher(deBase);
dsLookForDomain.Filter = "(&(cn=MACHSUPR))"; // MACHSUPR is the computer to delete
dsLookForDomain.SearchScope = SearchScope.Subtree;
dsLookForDomain.PropertiesToLoad.Add("cn");
dsLookForDomain.PropertiesToLoad.Add("distinguishedName");
SearchResultCollection srcComputer = dsLookForDomain.FindAll();
foreach (SearchResult aComputer in srcComputer)
{
/* For each computer
*/
DirectoryEntry computerToDel = aComputer.GetDirectoryEntry();
computerToDel.DeleteTree();
computerToDel.CommitChanges();
}
Use WMI and or System.DirectoryServices namespace (http://msdn.microsoft.com/en-us/library/system.directoryservices.aspx).
It may not be exactly what you are looking for, but this site provides a number of code examples for working with AD in C#, including deleting a security group and removing a user from a group

Categories

Resources