.netpowershellaclaccess-controlicacls

How do ICACL permissions map to FileSystemRights


I'm writing a script to copy permissions from one directory structure and reapply them to another. I can't simply use icacls \\my\path\* /save file.acl /T as I need to make a number of tweaks between the save and the restore, for which it's simpler to work with PowerShell's (Get-ACL $path).Access output.

In doing this I'm trying to map the output of this (i.e. FileSystemRights) to their equivalent icacls permissions; I'll then wrap the comma separated list of permissions in parenthesis to apply via icacls.

[string[]]$rights = $InputObject.FileSystemRights -split  '\s*,\s*' # note: FileSystemRights here is just a string rather than having been converted to ENUM, as I've just pulled it straight from CSV
switch ($rights) {
    'AppendData'                   {'AD'}
    #'ChangePermissions'           {'?'}
    #'CreateDirectories'           {'?'}
    'CreateFiles'                  {'WD'} # duplicate of WriteData
    'Delete'                       {'DE'} # or D?
    'DeleteSubdirectoriesAndFiles' {'DC'}
    'ExecuteFile'                  {'X'}
    'FullControl'                  {'F'}
    'ListDirectory'                {'RD'} #duplicate of read data
    'Modify'                       {'M'}
    'Read'                         {'R'} # or GR?
    'ReadAndExecute'               {'RX'}
    'ReadAttributes'               {'RA'}
    'ReadData'                     {'RD'}
    'ReadExtendedAttributes'       {'REA'}
    'ReadPermissions'              {'RC'}
    'Synchronize'                  {'S'}
    'TakeOwnership'                {'WO'}
    #'Traverse'                    {'?'} # or does this mean to specify the /T option?  Not really a permission
    'Write'                        {'W'} # or GW?
    'WriteAttributes'              {'WA'}
    'WriteData'                    {'WD'}
    'WriteExtendedAttributes'      {'WEA'}
    Default {Write-Warning "Could not find icacls permission for file system right: '$_'"} # Note: This may also occur if we get a numeric value for the rights instead of a comma separated list of enum names.  Once I've got the mapping I'll code a fix to handle that scenario too.
}

Question

I'm unsure of the mapping above; in some cases I couldn't work out what the equivalent value would be (e.g. ChangePermissions, CreateDirectories, Traverse) and in others there are multiple possibilities (e.g. should Read/Write map to R/W or GR/GW; or is there no difference)? I've checked documentation and searched the web, but most explanations of these permissions don't go into more detail than giving names for these abbreviations.

Additional Context

When I say I need to make a number of tweaks, I'm exporting permissions from an old file share on an old domain, and importing them to a new domain, so I need to map a number of the accounts from the old domain's user to the SID used in the new domain (we have a SIDHistory, but are aiming to do things cleanly, rather than relying on this long term). There are also some groups in the new domain which are equivalents of groups from the old domain, but have no relationship (i.e they're not migrated from the old domain; though they're serving essentially the same functional purpose. Editing the output of the ICACLS saved info is complex since the structure's quite minimal. It's not impossible, but more fiddly than I feel comfortable with.

The reason I need to map things back from the PS/.Net output to the icacls format is because the target share is on Azure Files (with AADDS enabled); so Set-ACL doesn't work, whilst icacls does.

I've created similar logic for the inheritance piece already:

[string[]]$if = $InputObject.InheritanceFlags -split '\s*,\s*'
[string[]]$pf = $InputObject.PropagationFlags -split '\s*,\s*'
switch ($if) {
    'ContainerInherit' {'(CI)'}
    'ObjectInherit' {'(OI)'}
}
switch ($pf) {
    'InheritOnly' {'(IO)'}
    'NoPropagateInherit' {'(NP)'}
}
if ($InputObject.IsInherited) {
    '(I)'
}

For anyone unfamiliar with PowerShell's switch statement; functionality is able to process each item in an array; hence there being no need for logic to loop through each right in $rights. More info here.

Update 2020-06-16 10:00

I just realised there's an easy way to see where FileSystemRights contains duplicates. The below code identifies these. This solved my issue with Traverse and CreateDirectories.

[Enum]::GetNames([System.Security.AccessControl.FileSystemRights]) | 
   sort | 
   %{[PSCustomObject]@{
        Name = $_ 
        FSR = ([System.Security.AccessControl.FileSystemRights]$_)
   }} | 
   ft -AutoSize

This shows that I can reuse some of the existing values

Name                                                  FSR
----                                                  ---
AppendData                                     AppendData
ChangePermissions                       ChangePermissions
CreateDirectories                              AppendData <-- i.e. AD
CreateFiles                                   CreateFiles
Delete                                             Delete
DeleteSubdirectoriesAndFiles DeleteSubdirectoriesAndFiles
ExecuteFile                                   ExecuteFile
FullControl                                   FullControl
ListDirectory                                    ReadData <-- i.e. RD
Modify                                             Modify
Read                                                 Read
ReadAndExecute                             ReadAndExecute
ReadAttributes                             ReadAttributes
ReadData                                         ReadData
ReadExtendedAttributes             ReadExtendedAttributes
ReadPermissions                           ReadPermissions
Synchronize                                   Synchronize
TakeOwnership                               TakeOwnership
Traverse                                      ExecuteFile <-- i.e. X
Write                                               Write
WriteAttributes                           WriteAttributes
WriteData                                     CreateFiles <-- i.e. WD
WriteExtendedAttributes           WriteExtendedAttributes

Solution

  • Whilst working out how to convert what I'd believe to be invalid numeric values (i.e. thinking they were made up of enum values; though for some reason not resolving when cast [System.Security.AccessControl.FileSystemRights]268435456) I came across this post, then from that found this one.

    Given the info there, and my findings on the duplicate values in my previous update, I've now written the mapping as this:

    [string[]]$rights = $InputObject.FileSystemRights -split  '\s*,\s*' # note: FileSystemRights here is just a string rather than having been converted to ENUM, as I've just pulled it straight from CSV
    switch ($rights) {
    
        '-2147483648'                  {'GR'}
        '268435456'                    {'GA'}
        '536870912'                    {'GE'}
        '1073741824'                   {'GW'}
        'AppendData'                   {'AD'}
        'ChangePermissions'            {'WDAC'}
        'CreateDirectories'            {'AD'} # duplicate of AppendData
        'CreateFiles'                  {'WD'} # duplicate of WriteData
        'Delete'                       {'DE'} # or D?
        'DeleteSubdirectoriesAndFiles' {'DC'}
        'ExecuteFile'                  {'X'}
        'FullControl'                  {'F'}
        'ListDirectory'                {'RD'} # duplicate of read data
        'Modify'                       {'M'}
        'Read'                         {'R'}
        'ReadAndExecute'               {'RX'}
        'ReadAttributes'               {'RA'}
        'ReadData'                     {'RD'}
        'ReadExtendedAttributes'       {'REA'}
        'ReadPermissions'              {'RC'}
        'Synchronize'                  {'S'}
        'TakeOwnership'                {'WO'}
        'Traverse'                     {'X'} # duplicate of ExecuteFile
        'Write'                        {'W'}
        'WriteAttributes'              {'WA'}
        'WriteData'                    {'WD'}
        'WriteExtendedAttributes'      {'WEA'}
        Default {Write-Warning "Could not find icacls permission for file system right: '$_'"} # Note: This may also occur if we get a numeric value for the rights instead of a comma separated list of enum names.  Once I've got the mapping I'll code a fix to handle that scenario too.
    }
    

    For resolving ChangePermissions to WDAC I just used Get-ACL and Set-ACL to assign only the ChangePermissions access to a unique user on a folder, then used icacls to query that folder's rights.