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
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
}
}
}
I am trying to print users of only certain OUs in my company AD.
So far I've come up with this:
string groupName = "Domain Users";
string domainName = "domain";
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName);
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, groupName);
if (grp != null)
{
foreach (Principal p in grp.GetMembers(false))
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
grp.Dispose();
ctx.Dispose();
Console.ReadLine();
}
else
{
Console.WriteLine("\nWe did not find that group in that domain, perhaps the group resides in a different domain?");
Console.ReadLine();
}
The problem is that it prints every user and not a specific OU like "Employees" or "Students".
How do I add a parameter to specify 1 or 2 OU's that it should cycle through instead of a group?
If you want to limit your search to a single OU / container, you can just bind to that using another PrincipalContext constructor:
string groupName = "Domain Users";
string domainName = "domain";
string ouName = "CN=Users,DC=yourcompany,DC=com";
// bind to the specified container you want
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName, ouName);
Then, of course, you're only searching in that CN=Users container - no where else.
I don't know your code exactly so this is pseudocode.
When you have some property e.g. a string to specify the type then use Where:
var groupName = "Student";
foreach (Principal p in grp.GetMembers(false).Where(princ => princ.OUName.Equals(groupName))
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
Or is it some kind of inheritance then OfType can be used:
foreach (Principal p in grp.GetMembers(false).OfType<Student>())
{
Console.WriteLine(p.SamAccountName + " - " + p.DisplayName);
}
You are searching the entire Domain, and "Domain Users" is probably not the OU you would wan´t, change the name of the variable and add:
string domainName = "Domain Users";
string groupName = "Students";
Then add the OU to your PrincipalContext:
var ctx = new PrincipalContext(ContextType.Domain, domainName, groupName);
I use the following code in my Application.
This might be a bit overkill for you, but i think it mostly fits your needs.
public static void DoStuff(UserPrincipal princ) {
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain => {
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
searcher.Filter = $"(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{princ.SamAccountName}*)))";
return searcher;
});
var directoryEntriesFound =
allSearcher.SelectMany(searcher =>
searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var memberOf = directoryEntriesFound.Select(entry => {
using (entry) {
return new {
Name = entry.Name,
GroupName = ((object[])entry.Properties["MemberOf"].Value)
.Select(obj => obj.ToString())
};
}
}
);
var result1 = new List<string>();
foreach (var member in memberOf) {
if(member.GroupName.Contains("Student") )
Console.WriteLine(princ.SamAccountName + " is Student");
if (member.GroupName.Contains("Employee"))
Console.WriteLine(princ.SamAccountName + " is Employee");
}
}
Just call this in your foreach (Principal p in grp.GetMembers(false))
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();
}
}
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;
}