Active Directory group search C# - c#

I'm finding a group that is under a domain to see the group entered by user is valid or not. I have checked with few groups. it is working fine. But there is one group which is in the domain(I'm able to see it from AD) but still not succeding my code condition.
Can you please review if I'm missing something?
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain");
GroupPrincipal qbeGroup = new GroupPrincipal(ctx);
PrincipalSearcher srch = new PrincipalSearcher(qbeGroup);
IsValidGroup = 0;
foreach (var found in srch.FindAll())
{
if (found.ToString() == TxtAccName.Text)
{
IsValidGroup = 1;
break;
}
}

You've got the essence actually. But your code is not feaseble. Try below, let me know.
bool groupExists;
using( var ctx = new PrincipalContext(ContextType.Domain, "mydomain") )
{
var filter = new GroupPrincipal(ctx) { Name = TxtAccName.Text }
using ( var searcher = new PrincipalSearcher(filter) )
{
groupExists = searcher.FindAll().Any();
}
}

Related

Convert list of samaccount names to distinguished names

I have a list of account names (thousands) that I need to add as members of a group, so I need to look up the distinguished names of those accounts so I can add them as members of a group.
There are 21000 user accounts. It takes hours to look up the distinguished names, is there a faster way?
DirectoryEntry deDomain = new DirectoryEntry();
deDomain.Path = "LDAP://DC=my,DC=ca;
deDomain.Username = "me";
deDomain.Password = "mypassword";
DirectorySearcher dsSearch = new DirectorySearcher(deDomain);
dsSearch.CacheResults = false;
dsSearch.PropertiesToLoad.Add("distinguishedname");
foreach (string sam in lstSamAccountNames)
{
dsSearch.Filter = $"(&(objectCategory=person)(objectClass=user)(SAMAccountName={ReplaceSpecialCharacters(sSamAccountName)}))";
SearchResult srPerson = dsSearch.FindOne();
yield return srPerson.Properties["distinguishedname"][0].ToString();
}
I don't have an AD domain handy that I can do performance testing with at the moment, but have you tried the AccountManagement library instead of using DirectorySearcher?
using System.DirectoryServices.AccountManagement;
// ...
var domainName = "MyDomainName";
using (var context = new PrincipalContext(ContextType.Domain, domainName))
{
foreach (var sam in lstSamAccountNames)
{
var userName = ReplaceSpecialCharacters(sam);
using (var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, $#"{domainName}\{userName}"))
{
yield return user.DistinguishedName;
}
}
}

AD PrincipalSearcher: Search where property does not contain some value

Principal Searcher seems to do a great job when building a filter to find an object with a particular value. What about without? For example How do I build a filter to exclude everyone with "Joe" in their name. The code below would not work.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal qbeUser = new UserPrincipal(ctx);
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
//this is the problem line. How to format to exclude values with Joe?
qbeUser.Name != "*Joe*";
srch.QueryFilter = qbeUser;
foreach (var found in srch.FindAll())
{ do something to non Joe users... }
....
Seems it's not possible with PrincipalSearcher.
Two possible workaround:
Use PrincipalSearcher to get all users and filter at client side
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal qbeUser = new UserPrincipal(ctx);
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
srch.QueryFilter = qbeUser;
foreach (var found in srch.FindAll())
{ //filter out users with "Joe" in its name }
Use DirectorySearcher
DirectoryEntry de = new DirectoryEntry("LDAP://domain.com/dc=domain,dc=com", "user", "pwd");
DirectorySearcher srch = new DirectorySearcher(de);
srch.Filter = "(&(objectCategory=person)(objectClass=user)(!(name=*Joe*)))";
srch.SearchScope = SearchScope.Subtree;
// add the attributes
srch.PropertiesToLoad.Add("distinguishedName");
using (SearchResultCollection results = srch.FindAll())
{
foreach (SearchResult result in results)
{
string dn = result.Properties["distinguishedName"][0] as string;
Console.WriteLine("- {0}", dn);
}
}

Get list of email addresses from ADGroup

If I have the name of an Active Directory group, how can I get a list of the members or their email addresses?
void Main()
{
string groupName = "somegroup";
string domainName = "somedomain";
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName))
{
using(GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, groupName))
{
var sams = from x in grp.GetMembers(true) select new {x.SamAccountName, };
var users = from sam in sams.Distinct()
let usr = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam.SamAccountName)
select new { usr.SamAccountName, usr.DisplayName, usr.EmailAddress};
//do something with users...
}
}
}
in this url are a good and complete articule about AD with C# http://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#42, user are stored as DiretoryEntry review the property Properties
http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.properties(v=vs.100).aspx

Using PrincipalSearcher to find users with "or" parameters

Is it possible to use System.DirectoryServices.AccountManagement.PrincipalSearcher to search based on multiple parameters using "or" (not "and").
i.e.
// This uses an and
//(&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(&(SAMAccountName=tom*)(DisplayName=tom*)))
var searchPrinciple = new UserPrincipal(context);
searchPrinciple.DisplayName = "tom*";
searchPrinciple.SamAccountName = "tom*";
var searcher = new PrincipalSearcher();
searcher.QueryFilter = searchPrinciple;
var results = searcher.FindAll();
and I would like a search similar to this (in LDAP) using PrincipalSearcher (not DirectorySearcher)
// (&(objectCategory=person)(!UserAccountControl:1.2.840.113556.1.4.803:=2)(|(SAMAccountName=tom*)(DisplayName=tom*)))
It's obviously not possible, here is a workaround:
List<UserPrincipal> searchPrinciples = new List<UserPrincipal>();
searchPrinciples.Add(new UserPrincipal(context) { DisplayName="tom*"});
searchPrinciples.Add(new UserPrincipal(context) { SamAccountName = "tom*" });
searchPrinciples.Add(new UserPrincipal(context) { MiddleName = "tom*" });
searchPrinciples.Add(new UserPrincipal(context) { GivenName = "tom*" });
List<Principal> results = new List<Principal>();
var searcher = new PrincipalSearcher();
foreach (var item in searchPrinciples)
{
searcher = new PrincipalSearcher(item);
results.AddRange(searcher.FindAll());
}
Not necessarily as clean as some of the other answers but here is how I've implemented this in a project I'm working on. I wanted both searches to be run async to try and reduce any slow down due to running two AD queries.
public async static Task<List<ADUserEntity>> FindUsers(String searchString)
{
searchString = String.Format("*{0}*", searchString);
List<ADUserEntity> users = new List<ADUserEntity>();
using (UserPrincipal searchMaskDisplayname = new UserPrincipal(domainContext) { DisplayName = searchString })
using (UserPrincipal searchMaskUsername = new UserPrincipal(domainContext) { SamAccountName = searchString })
using (PrincipalSearcher searcherDisplayname = new PrincipalSearcher(searchMaskDisplayname))
using (PrincipalSearcher searcherUsername = new PrincipalSearcher(searchMaskUsername))
using (Task<PrincipalSearchResult<Principal>> taskDisplayname = Task.Run<PrincipalSearchResult<Principal>>(() => searcherDisplayname.FindAll()))
using (Task<PrincipalSearchResult<Principal>> taskUsername = Task.Run<PrincipalSearchResult<Principal>>(() => searcherUsername.FindAll()))
{
foreach (UserPrincipal userPrincipal in (await taskDisplayname).Union(await taskUsername))
using (userPrincipal)
{
users.Add(new ADUserEntity(userPrincipal));
}
}
return users.Distinct().ToList();
}
My ADUserEntity class has an equality check based on the SID. I tried to add the Distinct() on to the Union() of the two searcher results but that didn't work.
I welcome any constructive criticism on my answer as I'd like to know if there is any way I can improve it.
I know this is kind of late, but this is the construct I use when searching AD:
public static Task<IEnumerable<SomeUserModelClass>> GetUsers(//Whatever filters you want)
{
return Task.Run(() =>
{
PrincipalContext context = new PrincipalContext(ContextType.Domain);
UserPrincipal principal = new UserPrincipal(context);
principal.Enabled = true;
PrincipalSearcher searcher = new PrincipalSearcher(principal);
var users = searcher.FindAll().Cast<UserPrincipal>()
.Where(x => x.SomeProperty... // Perform queries)
.Select(x => new SomeUserModelClass
{
userName = x.SamAccountName,
email = x.UserPrincipalName,
guid = x.Guid.Value
}).OrderBy(x => x.userName).AsEnumerable();
return users;
});
}
The FindAll method searches the domain specified in the principal
context for objects that have identical properties to those set on the
query filter. The FindAll method returns all objects that match the
supplied object whereas the FindOne method returns only a single
matching principal object.
http://msdn.microsoft.com/en-us/library/bb384378(v=vs.90).aspx
I don't know what you need, but you could do a search by 1 proprety and 1 by other and then use LINQ on the lists to merge,filter and etc...
PrincipalContext pContext = new PrincipalContext(ContextType.Machine, Environment.MachineName);
GroupPrincipal gp = GroupPrincipal.FindByIdentity(pContext, "Administrators");
bool isMember = UserPrincipal.Current.IsMemberOf(gp);

How to retrieve SAMAccountName from Active Directory

I implemented a method that returns a list of Active Directory users, I would like to retrieve SAMAccountName like this Domain\Administrator.
This is the method I use:
public Collection<software_user> GetUsersFromAD(String adConnectionString)
{
var users = new Collection<software_user>();
using (var directoryEntry = new DirectoryEntry(adConnectionString))
{
var directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = "(&(objectClass=user))";
var propertiesToLoad = new[]
{
"SAMAccountName",
"displayName",
"givenName",
"sn",
"mail",
"userAccountControl",
"objectSid"
};
directorySearcher.PropertiesToLoad.AddRange(propertiesToLoad);
foreach (SearchResult searchEntry in directorySearcher.FindAll())
{
var userEntry = searchEntry.GetDirectoryEntry();
var ldapUser = new software_user();
ldapUser.User_name = NullHandler.GetString(userEntry.Properties["displayName"].Value);
if (string.IsNullOrEmpty(ldapUser.User_name))
continue;
ldapUser.User_name = NullHandler.GetString(userEntry.Properties["SAMAccountName"].Value);
ldapUser.email = NullHandler.GetString(userEntry.Properties["mail"].Value);
ldapUser.user_shortname = NullHandler.GetString(userEntry.Properties["givenName"].Value);
var userAccountControl = (int)userEntry.Properties["userAccountControl"].Value;
//ldapUser.IsActive = (userAccountControl & UF_ACCOUNTDISABLE) != UF_ACCOUNTDISABLE;
var sid = new SecurityIdentifier((byte[])userEntry.Properties["objectSid"][0], 0).Value;
//ldapUser.SId = sid;
users.Add(ldapUser);
}
}
return users;
}
First off: Domain\Administrator is NOT a SAM account name! The SAM account name is a unique (over the whole domain) name of up to 20 characters in length - typically it's your "Windows user name" (e.g. Administrator) - but it does NOT include the domain name. That value made up of domain\username is NOT stored in Active Directory anywhere!
If 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
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 a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
string samAccountName = user.SamAccountName;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
If you want to search for a whole group of users (or groups or computers), 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 last name (Surname) of "Miller"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.Surname = "Miller";
// 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.....
}
You can translate a user as a Distinguished name to DOMAIN\SAMaccount form using the Object's SID, and the System.Security.Principal.SecurityIdentifier.Translate command.
public Collection<software_user> GetUsersFromAD(String adConnectionString)
{
var users = new Collection<software_user>();
using (var directoryEntry = new DirectoryEntry(adConnectionString))
{
var directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = "(&(objectClass=user))";
var propertiesToLoad = new[]
{
"SAMAccountName",
"displayName",
"givenName",
"sn",
"mail",
"userAccountControl",
"objectSid"
};
directorySearcher.PropertiesToLoad.AddRange(propertiesToLoad);
foreach (SearchResult searchEntry in directorySearcher.FindAll())
{
var userEntry = searchEntry.GetDirectoryEntry();
var ldapUser = new software_user();
ldapUser.User_name = NullHandler.GetString(userEntry.Properties["displayName"].Value);
if (string.IsNullOrEmpty(ldapUser.User_name))
continue;
ldapUser.User_name = NullHandler.GetString(userEntry.Properties["SAMAccountName"].Value);
ldapUser.email = NullHandler.GetString(userEntry.Properties["mail"].Value);
ldapUser.user_shortname = NullHandler.GetString(userEntry.Properties["givenName"].Value);
var userAccountControl = (int)userEntry.Properties["userAccountControl"].Value;
//ldapUser.IsActive = (userAccountControl & UF_ACCOUNTDISABLE) != UF_ACCOUNTDISABLE;
SecurityIdentifier sid = new SecurityIdentifier((byte[])userEntry.Properties["objectSid"][0], 0).Value;
--> NTAccount account = (NTAccount) sid.Translate(typeof(NTAccount));
--> ldapUser.User_name = account.ToString();
//ldapUser.SId = sid;
users.Add(ldapUser);
}
}
return users;
}

Categories