I'm searching to get the current members of a dynamic distribution group by exchange servers. Dynamic distribution groups are based on a specified filter. The "Recipient Update Service" (RUS) find each contact by runtime, based on this filter.
I've found a lot of information to solve the problem by using a wrapper class of exchange powershell in interaction of classic commandline arguments. But this is not my intended way.
I thought there should be a special command of "Exchange Web Services" (EWS) to get the dynamic members by runtime or by interop. I was unable to find some information about this.
Does anybody have an idea or some information to solve this problem via c#?
DirectoryServices seems to do the trick for me. Create a DirectoryEntry pointing to the dynamic distribution list (schema class name = "msExchDynamicDistributionList"), and then use the "msExchDynamicDLBaseDN" and "msExchDynamicDLFilter" properties to search for the members:
using (var group = new DirectoryEntry("LDAP://CN=MyGroup,OU=MyOU,DC=company,DC=com"))
{
string baseDN = (string)group.Properties["msExchDynamicDLBaseDN"].Value;
string filter = (string)group.Properties["msExchDynamicDLFilter"].Value;
using (var searchRoot = new DirectoryEntry("LDAP://" + baseDN))
using (var searcher = new DirectorySearcher(searchRoot, filter, propertiesToLoad))
using (var results = searcher.FindAll())
{
foreach (SearchResult result in results)
{
// Use the result
}
}
}
Remember that the members of the group could be regular groups or other dynamic distribution groups as well as users, contacts and public folders.
Related
I am trying to learn how to create a new Windows group (using C#) and assign an AccessRule to it by using the local user/group directory services.
I have written the following code which is attempting to firstly create the group, obtain the DirectoryEntry for it, and then creating and assigning a new custom AccessRule:
using System;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Security.AccessControl;
...
...
var principalContext = new PrincipalContext(ContextType.Machine);
var group = GroupPrincipal.FindByIdentity(principalContext, "groupName");
if (group == null)
{
group = new GroupPrincipal(principalContext)
{
Name = "groupName",
GroupScope = GroupScope.Local,
Description = "groupName description",
SamAccountName = "groupName",
};
group.Save();
}
var path = $"WinNT://{Environment.MachineName}/groupName,group";
var directoryEntry = new DirectoryEntry(path);
var accessRule = new ActiveDirectoryAccessRule(
group.Sid,
ActiveDirectoryRights.WriteProperty,
AccessControlType.Allow,
PermissionsDataSource.CanOverrideExpiredKeysPermissionId,
ActiveDirectorySecurityInheritance.None);
directoryEntry.ObjectSecurity.AddAccessRule(accessRule);
directoryEntry.Options.SecurityMasks = SecurityMasks.Dacl;
directoryEntry.CommitChanges();
The line that is causing me problems at the moment is the following which attempts to add the newly created access rule to the security objects:
directoryEntry.ObjectSecurity.AddAccessRule(accessRule);
The ObjectSecurity property is null. Similarly, the Options property is null. I am therefore not convinced I am creating the GroupPrincipal correctly.
It would be amazing if someone with some experience or knowledge in this area could help me understand what I need to do to be able to add access rules to the group object like I am trying to do above.
Thanks in advance!
P.S. The value PermissionsDataSource.CanOverrideExpiredKeysPermissionId is simply an arbitrary Guid which relates to the specific unique permission mapping that the application I am writing uses when checking if the groups a user belongs to has an access rule with this value.
You're working with a local group. Local Windows groups don't have permissions.
You can see this by opening Computer Management (compmgmt.msc) -> Local Users and Groups -> Groups. Right-click on a group and click Properties. You'll see there is no Security tab.
I'm struggling a bit with my simple console-dump-program. I connect to AD using
DirectoryEntry entry =
new DirectoryEntry("LDAP://" + domain, username, password);
and from there I recursively loop thru every child by
foreach (DirectoryEntry child in entry.Children)
{
Traverse(child);
}
Then I start getting mambo jambo data, users popping up more then once and null objects so I wonder if the way I handle the AD that it is just a handle and not a copy so its not loaded completely when I start traversing it?
Any tips/pointers on what to do?
If you can, move up to .NET 3.5 and use the new System.DirectoryServices.AccountManagement namespace - much easier to use.
See: Managing Directory Security Principals in the .NET Framework 3.5
Also: you need to understand Active Directory isn't just a flat list of users and groups - it's a hierachical system of OU's (organizational units) that can be nested into one another, and can contain users, groups, computers and more.
So what exactly do you want to do?? Get the users for a given OU (e.g. "Sales")?? Or really get all users from your AD?? You do understand this could take quite a while, depending on the size of your company's AD.......
If you really want to get ALL users and ALL groups from your entire AD - you should probably set up a DirectorySearcher at the root level:
// set search root
DirectoryEntry deRoot = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// declare directory searcher
DirectorySearcher dsUsers = new DirectorySearcher(deRoot);
// scope is full subtree, filter defines to search for users
dsUsers.SearchScope = SearchScope.SubTree;
dsUsers.Filter = "(objectCategory=person)";
// define what properties you want to have loaded into your search results
dsUsers.PropertiesToLoad.Add("givenName");
dsUsers.PropertiesToLoad.Add("surname");
dsUsers.PropertiesToLoad.Add("samAccountName");
// loop through results of search
foreach(SearchResult result in dsUsers.FindAll())
{
if(result.Properties["givenName"] != null)
string givenName = result.Properties["givenName"][0].ToString();
if(result.Properties["surname"] != null)
string surname = result.Properties["surname"][0].ToString();
if(result.Properties["sAMAccountName"] != null)
string samAccountName = result.Properties["sAMAccountName"][0].ToString();
}
When reading out the properties of your SearchResult, you need to check to make sure you did actually get a value back - otherwise your assignment will crash and burn....
For the groups, just use this filter instead:
dsUsers.Filter = "(objectCategory=group)";
If you can narrow your search, e.g. to a given OU, you can get much better performance, since the search tree gets smaller and thus the search would be a lot faster. To do so, just define a different LDAP path for your deRoot directory entry (e.g. LDAP://OU=Sales,DC=YourCOmpany,DC=com or whatever OU you want to search in).
Update: as I said - with .NET 3.5, it gets a lot easier still! You need to add a reference to System.DirectoryServices.AccountManagement, and then you can use code something like this using a sort of "query-by-example" approach:
// create a domain context for the current domain
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
// create a principal object decsribing what to search for
UserPrincipal user = new UserPrincipal(domain);
user.IsActive = true;
// create a principal searcher for running a search operation
PrincipalSearcher searcher = new PrincipalSearcher(user);
// run the query
PrincipalSearchResult<Principal> results = searcher.FindAll();
// iterate over all results
foreach (Principal result in results)
{
Console.WriteLine("name: {0}", result.Name);
}
And for searching for groups, just instantiate a GroupPrincipal, set any properties on it and then pass that into the PrincipalSearcher to search for groups.
i am building a csharp application and i would like a dropdown list of all users in my outlook global address book (the same one when i click on To: from outlook gui. is this possible to get this progrmaticall? what are the security requirements here?
Security ramifications, in addition to the Outlook dependency left me unable to use this approach, in the past. As a result, I ended up building this in the form of an LDAP query. Another plus is that, (in response to your other question) you will be able to extract contact information because this information is stored in the Active Directory.
DISCLAIMER: It has been almost five years since I have looked at this code, so I'm afraid I no longer fully understand the query. Hopefully it's enough to get you started, however.
DirectoryEntry adFolderObject = new DirectoryEntry();
DirectorySearcher adSearcher = new DirectorySearcher(adFolderObject);
adSearcher.SearchScope = SearchScope.Subtree;
adSearcher.Filter = "(& (mailnickname=*) (| (&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*))) ))";
foreach (SearchResult adObject in adSearcher.FindAll())
{
Console.WriteLine("CN={0}, Path={1}", adObject.Properties["CN"][0], adObject.Path);
}
Has anyone seen any solid libraries for working with active directory (mainly user related stuff) in C# and asp.net. Am I better off intergrating with asp membership or building something customised.
I took a look at LINQtoAD but it doesnt seem to be active anymore.
Is the System.DirectoryServices assembly and namespace insufficient?
If you're on .NET 3.5, also check out System.DirectoryServices.AccountManagement for much simpler interface when it comes to handling principals - users, groups, computers etc.
Check out this MSDN article as a great intro into S.DS.AD:
Managing Directory Security Principals in the .NET Framework 3.5
Cheers!
You can refer my OSS project which base on ActiveRecord pattern as following(Because it is open source you can find out how to operate the AD with DirectoryEntry, DirectoryEntry is not only support the LDAP protocol but also IIS, WIN and so on, so I develop this lib):
Eg: Update a user AD object.
using (var userObject = UserObject.FindOneByCN(this.ADOperator, “pangxiaoliang”))
{
if(userObject.Email == "example#landpy.com")
{
userObject.Email = "mv#live.cn";
userObject.Save();
}
}
Eg: Query user AD objects.
// 1. CN end with "liu", Mail conatains "live" (Eg: mv#live.cn), job title is "Dev" and AD object type is user.
// 2. CN start with "pang", Mail conatains "live" (Eg: mv#live.cn), job title is "Dev" and AD object type is user.
IFilter filter =
new And(
new IsUser(),
new Contains(PersonAttributeNames.Mail, "live"),
new Is(PersonAttributeNames.Title, "Dev"),
new Or(
new StartWith(AttributeNames.CN, "pang"),
new EndWith(AttributeNames.CN, "liu")
)
);
// Output the user object display name.
foreach (var userObject in UserObject.FindAll(this.ADOperator, filter))
{
using (userObject)
{
Console.WriteLine(userObject.DisplayName);
}
}
Eg: Custom query.
IFilter filter =
new And(
new IsUser(),
new Custom("(!userAccountControl:1.2.840.113556.1.4.803:=2)")
);
// Output the user object display name.
foreach (var userObject in UserObject.FindAll(this.ADOperator, filter))
{
using (userObject)
{
Console.WriteLine(userObject.DisplayName);
}
}
https://landpyactivedirectory.codeplex.com/documentation
And you will find it easy to operate the AD with it, if you have no interest with it please ignore my answer. Any question about AD please contact me :)
If I right-click and choose Properties on a service (like, say, Plug and Play) in the Services dialog, I get several pieces of information, including "Path to executable". For Plug and Play (in Vista) this is:
C:\Windows\system32\svchost.exe -k DcomLaunch
Is there some way I can get this same piece of information using .NET code if I know the service name (and/or the display name)?
(I can't use GetExecutingAssembly() because I'm not running the service from my project.)
Another option, without the interop, would be a WMI lookup (or registry - bit hacky!).
Here's a quick example, based on this code:
private static string GetServiceImagePathWMI(string serviceDisplayName)
{
string query = string.Format("SELECT PathName FROM Win32_Service WHERE DisplayName = '{0}'", serviceDisplayName);
using (ManagementObjectSearcher search = new ManagementObjectSearcher(query))
{
foreach(ManagementObject service in search.Get())
{
return service["PathName"].ToString();
}
}
return string.Empty;
}
This information is in the QUERY_SERVICE_CONFIG structure. You will need to use P/Invoke to get it out.
The basic process is:
Call OpenSCManager to get a handle to the services managed.
Call OpenService to get a handle to the service.
Call QueryServiceConfig to get the QUERY_SERVICE_CONFIG structure.
There's always the WMI class Win32_Service as described here, specifically the PathName.
This works:
ManagementClass mc = new ManagementClass("Win32_Service");
foreach(ManagementObject mo in mc.GetInstances())
{
if(mo.GetPropertyValue("Name").ToString() == "<Short name of your service>")
{
return mo.GetPropertyValue("PathName").ToString().Trim('"');
}
}
If you have any issue related to Reference then add a reference of System.Management in your project.