Pietro Ottati
Pietro Ottati

Reputation: 39

Automatic translate Generic Access Rights within a report script

I'm trying to obtain a simple report about Folder Security that I have to use in official documentation that need to be easy and clear for non-technical people.

I have found and adapt some code, but generic access rights are displayed as number so completely not-easy to explain and report

Function Get-Folder($initialDirectory)

{
    [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|Out-Null

    $foldername = New-Object System.Windows.Forms.FolderBrowserDialog
    $foldername.Description = "Select a folder"
    $foldername.rootfolder = "MyComputer"

    if($foldername.ShowDialog() -eq "OK")
    {
        $folder += $foldername.SelectedPath
    }
    return $folder
}

$IntPath = Get-Folder
$FolderPath = dir -Directory -Path $IntPath -Recurse -Force
$FolderName = Get-Item -Path $IntPath | Select-Object -ExpandProperty Name
$date = Get-Date -UFormat %Y%m%d_%H%M%S
$Report = @()

Foreach ($Folder in $FolderPath) {
    $Acl = Get-Acl -Path $Folder.FullName
    foreach ($Access in $acl.Access)
        {
            $Properties = [ordered]@{'FolderName'=$Folder.FullName;'AD Group or User'=$Access.IdentityReference;'Permissions'=$Access.FileSystemRights;'Inherited'=$Access.IsInherited}
            $Report += New-Object -TypeName PSObject -Property $Properties
        }
}

$Report | Export-Csv -path $env:USERPROFILE\Desktop\"$FolderName"_"$date".csv

# 268435456 --> FullControl
# -536805376 --> Modify, Synchronize
# -1610612736 --> ReadAndExecute, Synchronize

I would find a solution that will translate in the final CSV the number in the commments.

Upvotes: 2

Views: 800

Answers (1)

Theo
Theo

Reputation: 61028

Changing your code like below works for me (PS version 5.1 of Windows 10 Pro):

function Get-Folder($initialDirectory) {
    # [System.Reflection.Assembly]::LoadWithPartialName() is deprecated
    Add-Type -AssemblyName System.Windows.Forms

    $dialog = New-Object System.Windows.Forms.FolderBrowserDialog
    $dialog.Description = "Select a folder"
    $dialog.rootfolder = "MyComputer"

    if($dialog.ShowDialog() -eq "OK") {
        $folder += $dialog.SelectedPath
    }
    $dialog.Dispose()

    return $folder
}

$SelectedPath = Get-Folder
$FolderName   = Get-Item -Path $SelectedPath | Select-Object -ExpandProperty Name
$FolderPath   = Get-ChildItem -Directory -Path $SelectedPath -Recurse -Force
$date         = '{0:yyyyMMdd_HHmmss}' -f (Get-Date)
$outputFile   = "$env:USERPROFILE\Desktop\$FolderName_$date.csv"

# collect the objects returned
$Report = foreach ($Folder in $FolderPath) {
    $Acl = Get-Acl -Path $Folder.FullName
    foreach ($Access in $acl.Access) {
        [PSCustomObject]@{
            'FolderName'       = $Folder.FullName
            'AD Group or User' = $Access.IdentityReference
            'Permissions'      = $Access.FileSystemRights.ToString()
            'Inherited'        = $Access.IsInherited
        }
    }
}

# output on screen
$Report | Format-Table -AutoSize

# export to csv file
$Report | Export-Csv -Path $outputFile -NoTypeInformation

If for some reason you still get numeric values instead of strings for the FileSystemRights, you can use a switch like this to convert the numbers to descriptive strings:

# collect the objects returned
$Report = foreach ($Folder in $FolderPath) {
    $Acl = Get-Acl -Path $Folder.FullName
    foreach ($Access in $acl.Access) {
        # see https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesystemrights
        $permissions = switch($Access.FileSystemRights.value__) {
            2032127     { 'FullControl '; break}
            1179785     { 'Read '; break}
            1180063     { 'Read, Write '; break}
            1179817     { 'ReadAndExecute '; break}
            1245631     { 'ReadAndExecute, Modify, Write '; break}
            1180095     { 'ReadAndExecute, Write '; break}
            268435456   { 'FullControl (Sub Only) '; break}
            -1610612736 { 'ReadAndExecute, Synchronize '; break}
            -536805376  { 'Modify, Synchronize '; break}
            default     { $Access.FileSystemRights.ToString()}
        }
        [PSCustomObject]@{
            'FolderName'       = $Folder.FullName
            'AD Group or User' = $Access.IdentityReference
            'Permissions'      = $permissions
            'Inherited'        = $Access.IsInherited
        }
    }
}


Update

The above is a listing of the most common file permissions. File objects can have a lot more permission values set in the Access Mask Format flags value.

The System.Security.AccessControl.FileSystemRights enumeration stores these values, mapping the most common values to human readable words:

Name                           Value
----                           -----
ListDirectory                      1
ReadData                           1
WriteData                          2
CreateFiles                        2
CreateDirectories                  4
AppendData                         4
ReadExtendedAttributes             8
WriteExtendedAttributes           16
Traverse                          32
ExecuteFile                       32
DeleteSubdirectoriesAndFiles      64
ReadAttributes                   128
WriteAttributes                  256
Write                            278
Delete                         65536
ReadPermissions               131072
Read                          131209
ReadAndExecute                131241
Modify                        197055
ChangePermissions             262144
TakeOwnership                 524288
Synchronize                  1048576
FullControl                  2032127

Looking at the AccessMask in above link, you can encounter values that aren't in this enumeration and in order to translate all of this into words, you could try something like below.
It uses the System.Security.AccessControl.FileSystemRights enumeration sorted in reverse order by Value and stored in a variable $FileSystemRights. Variable $value is the numeric value you obtained from $Access.FileSystemRights.value__.

$PermsList = [System.Collections.Generic.List[string]]::new()
#if ($value -band 0xF1000000) {
    # Generic access rights (bits 24..31)
    if ($value -band 0x80000000) { [void]$PermsList.AddRange([string[]]('Read', 'ReadAttributes', 'ReadExtendedAttributes', 'Synchronize'))}     # GENERIC_READ
    if ($value -band 0x40000000) { [void]$PermsList.AddRange([string[]]('Write', 'WriteAttributes', 'WriteExtendedAttributes', 'Synchronize'))}  # GENERIC_WRITE
    if ($value -band 0x20000000) { [void]$PermsList.AddRange([string[]]('ReadAndExecute', 'ReadAttributes', 'ReadPermissions'))}                 # GENERIC_EXECUTE
    if ($value -band 0x10000000) { [void]$PermsList.Add('FullControl')}                                                                          # GENERIC_All
    if ($value -band 0x1000000)  { [void]$PermsList.Add('ChangePermissions')}                                                                    # Right to access SACL
#}

# Standard access and Object specific rights (bits 0..23)
$value = $value -band 0xFFFFFF
$FileSystemRights | ForEach-Object {
    if (($value -band $_.Value)) { [void]$PermsList.Add($_.Name) }
}

($PermsList.ToArray() | Select-Object -Unique) -join ', '

The values you mention in the comment would return:

-2147483648 --> 'Read, ReadAttributes, ReadExtendedAttributes, Synchronize'
-268435456  --> 'Read, ReadAttributes, ReadExtendedAttributes, Synchronize, Write, WriteAttributes, WriteExtendedAttributes, ReadAndExecute, ReadPermissions, FullControl'
-1073741760 --> 'Read, ReadAttributes, ReadExtendedAttributes, Synchronize, Write, WriteAttributes, WriteExtendedAttributes, FullControl, DeleteSubdirectoriesAndFiles'

Hope this explains more about the different file object permissions.

Upvotes: 2

Related Questions