Search code examples
windowspowershellfile-permissionsacl

PowerShell: Display the differences in permissions between folders and their parents


I'm hoping to come up with a script that can walk a directory tree on a Windows server and show me a tree that only includes directories whose permissions are different from it's parent (or sub) directory. I want to produce an easily understandable report that can help me quickly audit the permissions of a folder structure.

Here's what I've got so far:

DIR "Z:\FileShare" -directory -recurse | GET-ACL | where {$_.AreAccessRulesProtected -eq $true} | select path, accessToString | format-list  |out-file c:\permissions.txt

This produces a usable set of data as is but it's a bit bulky.

What I don't know is how to have it filter out redundant text, namely lines like "BUILTIN\Administrators Allow FullControl" and instead only show me the delta. Human readable psudo-code might be "if this ACL line can be found in the immediate parent directory, don't show it here."


Solution

  • I tested this with a few folders setting up different ACLs with my own user and it seem to be working but I haven't tested enough to be sure. Basically, the script will loop through directories and add the ACLs to a dictionary where the Keys are each IdentityReference and the Values are the properties from the ACLs which you're interested on (FileSystemRights and AccessControlType) in addition to the folder's absolute path. While enumerating the directories, each object will be compared against the stored values using the Compare-Acl function which only returns $true if the object is different.

    using namespace System.Collections
    using namespace System.Collections.Generic
    
    $map = [Dictionary[string, ArrayList]]::new()
    
    $outObj = {
        [pscustomobject]@{
            AbsolutePath      = $dir.FullName
            FileSystemRights  = $acl.FileSystemRights
            IdentityReference = $acl.IdentityReference
            AccessControlType = $acl.AccessControlType
        }
    }
    
    function Compare-Acl {
        param(
            [object[]] $Reference,
            [object] $Difference
        )
    
        foreach ($ref in $Reference) {
            $fsRights = $ref.FileSystemRights -eq $Difference.FileSystemRights
            $actRef = $ref.AccessControlType -eq $Difference.AccessControlType
            if ($fsRights -and $actRef) {
                return $false
            }
        }
    
        $true
    }
    
    foreach ($dir in Get-ChildItem Z:\FileShare -Directory -Recurse) {
        foreach ($acl in (Get-Acl $dir.FullName | Where-Object AreAccessRulesProtected).Access) {
            if ($thisKey = $map[$acl.IdentityReference]) {
                $obj = & $outObj
                if (Compare-Acl -Reference $thisKey -Difference $obj) {
                    $null = $thisKey.Add($obj)
                }
                continue
            }
    
            $obj = & $outObj
            $map.Add($acl.IdentityReference, [object[]] $obj)
        }
    }
    
    $map.Keys.ForEach({ $map[$_] }) | Export-Csv path\to\acls.csv -NoTypeInformation