다음을 통해 공유


Copying ACEs in the .NET Framework (PowerShell)

A PowerShell script to copy access entries from one user to another user might look something like this:


$oldUser = [System.Security.Principal.NTAccount] 'DOMAIN\User01'
$newUser = [System.Security.Principal.NTAccount] 'DOMAIN\User02'

$acl = Get-Acl -Path 'C:\Program Files'
$accessList = $acl.Access | Where-Object { $_.IsInherited -eq $false }

foreach ($ace in $accessList) {
    if ($ace.IdentityReference.Translate([System.Security.Principal.NTAccount]) -eq $oldUser) {
        $newRule = New-Object 'System.Security.AccessControl.FileSystemAccessRule' (
            $newUser,
            $ace.FileSystemRights,
            $ace.InheritanceFlags,
            $ace.PropagationFlags,
            $ace.AccessControlType 
        )
        
        $acl.AddAccessRule($newRule)
    }
}

$acl | Set-Acl


The problem with this script is that you will eventually run into this type of error:

New-Object : Exception calling ".ctor" with "5" argument(s): "The value '268435456' is not valid for this usage of the type FileSystemRights.
Parameter name: fileSystemRights"

The reason for this is that the Win32 API's "Access Mask" definitions and the .NET Framework FileSystemRights enumerated type don't contain all of the same flags.  Specifically, the GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE and GENERIC_ALL values from the Win32 API cannot be cast to a [System.Security.AccessControl.FileSystemRights] value.  See http://msdn.microsoft.com/en-us/library/aa374896(v=vs.85).aspx for details of the Win32 API access masks.

The way to get around this is to use the AccessRuleFactory method on the FileSystemSecurity class.  This method accepts an integer "bitmask" parameter, rather than only accepting values that will cast to a FileSystemRights value:  http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemsecurity.accessrulefactory.aspx

Because you have no control over what the ACLs of objects might be (and whether they will contain the GENERIC_* values from the Win32 API), you should use the AccessRuleFactory method any time you're trying to create a new access rule based on the rights defined in an ACE that you read from an object.  If you are hard-coding the rights in a new ACE, using the FileSystemAccessRule constructor with a valid FileSystemRights value is the preferred approach.

This information applies to the SACL and to other securable objects as well (ActiveDirectorySecurity, RegistrySecurity, etc);  each has an AccessRuleFactory and AuditRuleFactory method in the .NET Framework.

The revised sample script looks like this:


$oldUser = [System.Security.Principal.NTAccount] 'DOMAIN\User01'
$newUser = [System.Security.Principal.NTAccount] 'DOMAIN\User02'

$acl = Get-Acl -Path 'C:\Program Files'
$accessList = $acl.Access | Where-Object { $_.IsInherited -eq $false }

foreach ($ace in $accessList) {
    if ($ace.IdentityReference.Translate([System.Security.Principal.NTAccount]) -eq $oldUser) {
        $newRule = $acl.AccessRuleFactory(
            $newUser,
            $ace.FileSystemRights,
            $ace.IsInherited,
            $ace.InheritanceFlags,
            $ace.PropagationFlags,
            $ace.AccessControlType 
        )
        
        $acl.AddAccessRule($newRule)
    }
}

$acl | Set-Acl