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;
}
Related
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;
}
}
}
I am trying to convert some old code to use new namespaces to get the users, groups, subgroups from active directory and insert them into DB. The new code is not returning the complete list of groups a user is associated to. Some of the groups the user is associated to is not returned by the new code.
Old Code :
DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry(directoryPath));
searcher.PageSize = 1000;
searcher.Filter = "(&(objectCategory=user))";
//adding these properties assist in assuring they are returned by Active Directory
searcher.PropertiesToLoad.Add("memberOf");
searcher.PropertiesToLoad.Add("member");
searcher.PropertiesToLoad.Add("samaccountname");
searcher.PropertiesToLoad.Add("cn");
searcher.PropertiesToLoad.Add("description");
searcher.PropertiesToLoad.Add("whencreated");
searcher.PropertiesToLoad.Add("mail");
SearchResultCollection resultUsers = searcher.FindAll();
Dictionary<string, Group> groups = new Group().GetAllGroups(); //get the list of groups that were inserted into DB
foreach (SearchResult resultUser in resultUsers)
{
DirectoryEntry userEntry = resultUser.GetDirectoryEntry();
string userName = GetProperty(userEntry, "samaccountname"); //Custom method to get samaccountname
//make sure the username exists
if (userName.Length != 0)
{
User user = new User();
//set user properties
user.FullName = user.Email = GetProperty(userEntry, "cn");
user.Email = GetProperty(userEntry, "mail");
string createdDateString = GetProperty(userEntry, "whencreated");
user.CreatedDate = (createdDateString.Length > 0) ? DateTime.Parse(createdDateString) : DateTime.MinValue;
user.UserName = userName;
user.DomainName = _dataSource.UserDomainName;
//check to see if the account is disabled
ActiveDs.IADsUser objIADsUser = (ActiveDs.IADsUser)userEntry.NativeObject;
user.Inactive = objIADsUser.AccountDisabled;
//save the user
user.SaveNew();
userEntry.RefreshCache(new string[] { "tokenGroups" });
//now the attribute will be available
int count = userEntry.Properties["tokenGroups"].Count;
IdentityReferenceCollection irc = ExpandTokenGroups(userEntry).Translate(typeof(NTAccount));
foreach (IdentityReference ir in irc)
{
NTAccount testAccount;
bool isNTAccount = false;
try
{
testAccount = (NTAccount)ir;
isNTAccount = true;
}
catch
{
}
if (isNTAccount)
{
NTAccount account = (NTAccount)ir;
string groupName = account.Value.Split('\\')[1];
if (groups.ContainsKey(groupName.ToLower()))
{
Group group = groups[groupName.ToLower()];
//add user to group
UserGroupLink userGroupLink = new UserGroupLink();
userGroupLink.GroupId = group.GroupId;
userGroupLink.UserId = user.UserID;
userGroupLink.SaveNew();
}
}
}
}
}
//close connection to active directory
searcher.Dispose();
New code:
var section = (NameValueCollection)ConfigurationManager.GetSection("DataSource_ActiveDirectory1");
string domainName = section["userDomainName"];
string domainFilter = section["domainFilter"];
PrincipalContext MyPrincipalContext = new PrincipalContext(ContextType.Domain, domainName, domainFilter); //Create your domain context
GroupPrincipal FindAllGroups = new GroupPrincipal(MyPrincipalContext); //group principal to search for all group.
UserPrincipal FindAllUsers = new UserPrincipal(MyPrincipalContext); //user principal to search for all users.
PrincipalSearcher UserSearcher = new PrincipalSearcher(FindAllUsers); //search results for user
PrincipalSearchResult<Principal> UserReults = UserSearcher.FindAll(); //find all users
PrincipalSearcher MySearcher = new PrincipalSearcher(FindAllGroups); //search results for groups
PrincipalSearchResult<Principal> GroupResults = MySearcher.FindAll(); //search all groups
ArrayList AllUsers = new ArrayList();
foreach (Principal UserReult in UserReults)
{
UserPrincipal UserName = UserPrincipal.FindByIdentity(MyPrincipalContext, IdentityType.SamAccountName, UserReult.SamAccountName);
//logic to save users to DB
}
foreach (Principal UserName in AllUsers)
{
foreach (Principal FindParentGroup in UserName.GetGroups())
{
string ParentGroupEntry = FindParentGroup.SamAccountName;
if (ParentGroupEntry != null || ParentGroupEntry.Length > 0)
{
//Insert into DB
}
}
}
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);
}
}
I use following code to retrieve all users of active directory but not all users retrieved. Should I include something else in Filter?
this is simplified code.
DirectoryEntry entry = new DirectoryEntry("LDAP://" + Bous.DomainName, Bous.UserName, Bous.Password);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(&(objectCategory=person)(objectClass=user))");
foreach (SearchResult result in mySearcher.FindAll())
{
dr = dt.NewRow();
ResultPropertyCollection myResultPropColl = result.Properties;
dr[0] = myResultPropColl["samaccountname"][0].ToString() + "#" + Bous.DomainName;
dt.Rows.Add(dr);
}
Try this one:
DirectoryEntry entry = new DirectoryEntry("LDAP://" + Bous.DomainName, Bous.UserName, Bous.Password);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.PageSize = 1000; // <--- Important!
mySearcher.Filter = ("(&(objectCategory=person)(objectClass=user))");
foreach (SearchResult result in mySearcher.FindAll())
{
dr = dt.NewRow();
ResultPropertyCollection myResultPropColl = result.Properties;
dr[0] = myResultPropColl["samaccountname"][0].ToString() + "#" + Bous.DomainName;
dt.Rows.Add(dr);
}
If you're on .NET 3.5 or newer, 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))
{
// define a "query-by-example" principal - here, we search for UserPrincipal (users)
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.
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
...
protected static UserPrincipal QueryFilterUsers(string FilterFullName, string FilterDescription, string FilterLogin, string FilterPhone, string FilterEmail)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, _Domain);
UserPrincipal _UserPrincipal = new UserPrincipal(ctx);
_UserPrincipal.SamAccountName = "*" + ((!string.IsNullOrEmpty(FilterLogin)) ? FilterLogin.Trim() + "*" : "");
if (!string.IsNullOrEmpty(FilterFullName)) _UserPrincipal.DisplayName = "*" + FilterFullName.Trim() + "*";
if (!string.IsNullOrEmpty(FilterDescription)) _UserPrincipal.Description = "*" + FilterDescription.Trim() + "*";
if (!string.IsNullOrEmpty(FilterPhone)) _UserPrincipal.VoiceTelephoneNumber = "*" + FilterPhone.Trim() + "*" ;
if (!string.IsNullOrEmpty(FilterEmail)) _UserPrincipal.EmailAddress = "*" + FilterEmail.Trim() + "*";
return _UserPrincipal;
}
//Получение списков
public static List<Principal> GetUsersPrincipalList(string FilterFullName, string FilterDescription, string FilterLogin, string FilterPhone, string FilterEmail)
{
PrincipalSearcher _PrincipalSearcher = new PrincipalSearcher();
_PrincipalSearcher.QueryFilter = QueryFilterUsers(FilterFullName, FilterDescription, FilterLogin, FilterPhone, FilterEmail);
(_PrincipalSearcher.GetUnderlyingSearcher() as DirectorySearcher).PageSize = _PageSize;
(_PrincipalSearcher.GetUnderlyingSearcher() as DirectorySearcher).SizeLimit = _SizeLimit;
return _PrincipalSearcher.FindAll().ToList();
}
The following code lists some, but not all, Active Directory Groups. Why?
I am trying to list all security groups, distribution groups, computer groups etc. Have I specified the wrong objectClass?
private static void ListGroups()
{
DirectoryEntry objADAM = default(DirectoryEntry);
DirectoryEntry objGroupEntry = default(DirectoryEntry);
DirectorySearcher objSearchADAM = default(DirectorySearcher);
SearchResultCollection objSearchResults = default(SearchResultCollection);
SearchResult myResult=null;
objADAM = new DirectoryEntry(LDAP);
objADAM.RefreshCache();
objSearchADAM = new DirectorySearcher(objADAM);
objSearchADAM.Filter = "(&(objectClass=group))";
objSearchADAM.SearchScope = SearchScope.Subtree;
objSearchResults = objSearchADAM.FindAll();
// Enumerate groups
try
{
fileGroups.AutoFlush = true;
if (objSearchResults.Count != 0)
{
foreach (SearchResult objResult in objSearchResults)
{
myResult = objResult;
objGroupEntry = objResult.GetDirectoryEntry();
Console.WriteLine(objGroupEntry.Name);
fileGroups.WriteLine(objGroupEntry.Name.Substring(3));
}
}
else
{
throw new Exception("No groups found");
}
}
catch (PrincipalException e)
{
fileErrorLog.AutoFlush = true;
fileErrorLog.WriteLine(e.Message + " " + myResult.Path);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
If you're on .NET 3.5 or newer, 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 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
Try filter "(objectcategory=group)"
Found solution here
DirectoryEntry entry = new DirectoryEntry("ldap://ldap.gaurangjadia.com", "scott", "tiger");
DirectorySearcher dSearch = new DirectorySearcher(entry);
dSearch.Filter = "(&(objectClass=group))";
dSearch.SearchScope = SearchScope.Subtree;
SearchResultCollection results = dSearch.FindAll();
for (int i = 0; i < results.Count; i++) {
DirectoryEntry de = results[i].GetDirectoryEntry();
//TODO with "de"
}
I tried this and it worked
public ArrayList GetAllGroupNames(string ipAddress, string ouPath)
{
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = GetRootDirectoryEntry(ipAddress, ouPath);
deSearch.Filter = "(&(objectClass=group))";
SearchResultCollection results = deSearch.FindAll();
if (results.Count > 0)
{
ArrayList groupNames = new ArrayList();
foreach (SearchResult group in results)
{
var entry = new DirectoryEntry(group.Path, UserName, Password);
string shortName = entry.Name.Substring(3, entry.Name.Length - 3);
groupNames.Add(shortName);
}
return groupNames;
}
else
{
return new ArrayList();
}
}
private DirectoryEntry GetRootDirectoryEntry(string ipAddress, string domainPath, string username, string password)
{
var ldapPath = "LDAP://" + ipAddress + "/" + domainPath;
return new DirectoryEntry(ldapPath, username, password, AuthenticationTypes.Secure);
}
To retrieve a set of results that is larger than 1000 items, you must set SizeLimit to its default value (zero) and set PageSize to a value that is less than or equal to 1000.
objSearchADAM.PageSize = 1000;
you can get the all ad group details by the below powershell and if you want particular Name against of AD Group then write filter instead of *
Get-ADGroup -Filter * -properties * | Export-csv c:\csv\new.csv