I need to remove certain accounts (like the "Everyone" trustee or some global group that is giving everyone access to a folder) from an ACL on a particular subfolder of a share. I get a DirectorySecurity object, get and loop through the AuthorizationRuleCollection, remove the AccessRule in question from the ACL, and then I call SetAccessControl to apply the changes. Everything works fine if the target folder is small, but if it has a lot of child folders and files, it can take forever to apply the changes (much longer than to just do it manually). I only want to deal with the ACL on the target folder. Is there a way to do this using the .NET DirectorySecurity classes? Or do I have to resort to the Win32 API or some other solution?

Here is a snippet of code. The call to dirInfo.SetAccessControl(dirSec) is where it hangs when the folder size is very large.

DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectorySecurity dirSec = dirInfo.GetAccessControl();
AuthorizationRuleCollection acl = dirSec.GetAccessRules(true, true,     
                                  typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);
        dirInfo.SetAccessControl(dirSec);
    }
}
2

There are 2 best solutions below

1
On BEST ANSWER
DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectorySecurity dirSec = dirInfo.GetAccessControl();
AuthorizationRuleCollection acl = dirSec.GetAccessRules(true, true,     
                                  typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);
        dirInfo.SetAccessControl(dirSec);
    }
}

In your code, you are applying the updates to the ACL every interation of your loop, this gets very expensive. Have you tried moving dirInfo.SetAccessControl(dirSec); outside the foreach ? This should invoke the SetAccessControl method on your DirectoryInfo object once, applying all the changes in one pass, like this:

foreach (FileSystemAccessRule ace in acl)
{
    if (groupsToRemove.Contains(ace.IdentityReference.Value))
    {
        dirSec.RemoveAccessRuleSpecific(ace);        
    }
}
dirInfo.SetAccessControl(dirSec);
5
On

You have to set the SE_DACL_PROTECTED flag in order to "prevent ACEs set on the DACL of the parent container, and any objects above the parent container in the directory hierarchy, from being applied to the object DACL." This would likely speed up your operation because it would not have to apply it all child objects. Also from MSDN,

Be aware that the SE_DACL_PRESENT flag must be present to set SE_DACL_PROTECTED and SE_SACL_PRESENT must be present to set SE_SACL_PROTECTED.

You then have to use the IADsSecurityDescriptor.Control property to control whether DACLs and SACLs are inherited by the object from its parent container.

More information is available at MSDN for the IADsSecurityDescriptor Interface.