New Year, New Segment

Hello everyone, I’ve decided to decouple my stuff from RMM specific scripts where possible. This starts a new blog series for me, just pure PowerShell. We have several clients that need regular reports on who has access to what files, but making those reports is either a lot of manual work, or at the very least, tedious amounts of documentation that will likely fall out of sync when a tech doesn’t bother updating it when they make a change.

So I sat down with the task of creating the reports in an automated way. As generic as possible so it could hit multiple clients with minimal or no modifications. What I’ve made is a script that will loop through all network shares, and grab the file permissions of them, and write each to a file. It, by default, only goes 2 folders deep, to prevent scanning millions of folders all of which have the same permissions as their parent. It only reports on folders that have permissions that do not match their parent, so the reports are no longer than they need to be. It prints them to text files that are pretty easy for humans to read. A crafty person might even write them to more user friendly reports.

Ideally you would parameterize the output path and recursedepth for easy changes without having to change the code, but for an easy quick run, I have left them not.

Small warning, -depth parameter of Get-ChildObject is only valid for PowerShell 5.1 and up. So that may create issues. You can exclude that parameter, but doing so will significantly increase scan times. You can get the same limits by removing -Depth and -Recurse, but changing the path to be something like “-Path $RootPath\*, $RootPath\*\*”

#Edit these to meet your liking.
$OutputPath = "."
$OutputFileName = "Shares"
$RecurseDepth = 2

#Automatically gets the list of shares. Excludes system shares and non-file shares like printers.
$Shares = Get-SmbShare | Where-Object { $_.Name -NotLike "*$" -and $_.Name -ne "SYSVOL" -and $_.Name -ne "NETLOGON" -and $_.ShareType -eq "FileSystemDirectory" }
#Gets NTDomain to help with name filters
$Domain = (Get-WmiObject Win32_NTDomain).DomainName

#List of names to filter out. Great for client facing reports, might not be best for change monitoring.
$NameFilter = "$Domain\MSPAdmin", 'BUILTIN\Administrators', 'NT AUTHORITY\SYSTEM', "$Domain\Domain Admins"


#Converts the Propagation and Inheritance flags into human readable output
Function Get-HumanReadablePropagation($PropagationFlags, $InheritanceFlags) {
    switch ($PropagationFlags) {
        "None" {
            switch ($InheritanceFlags) {
                "None" { return "This Folder Only" }
                "Container" { return "This Folder and Sub-folders" }
                "Object" { return "This Folder and Files" }
                Default { return "This Folder, Sub-folders and Files" }
            }
        }
        Default {
            switch ($InheritanceFlags) {
                "Container" { return"Sub-Folders" }
                "Object" { return "Files" }
                Default { return "Sub-folders and Files" }
            }
        }
    }
}
$Output = foreach ($Share in $Shares) {  
    $RootPath = $Share.Path
    $FolderPath = Get-ChildItem -Directory -Path $RootPath -Recurse -Depth $RecurseDepth -Force | Where-Object { $_.PSPath -notlike "*DfsrPrivate*" }
    $MainFolderACL = Get-Acl -Path $RootPath
    ForEach ($Access in $MainFolderACL.Access) {
        $Propagation = Get-HumanReadablePropagation($Access.PropagationFlags, $Access.InheritanceFlags)
        [PSCustomObject]@{
            'Share' = $Share.Name
            'Folder Name' = $RootPath
            'Group/User' = $Access.IdentityReference
            'Permissions' = $Access.FileSystemRights
            'Inherited' = $Access.IsInherited
            'Propagation' = $Propagation
        }
    }
    ForEach ($Folder in $FolderPath) {
        $Acl = Get-Acl -Path $Folder.FullName
        if ($Acl.Access.IsInherited -contains $false) {
            ForEach ($Access in $Acl.Access) {
                $Propagation = Get-HumanReadablePropagation($Access.PropagationFlags, $Access.InheritanceFlags)
                [PSCustomObject]@{
                    'Share' = $Share.Name
                    'Folder Name' = $Folder.FullName
                    'Group/User' = $Access.IdentityReference;
                    'Permissions' = $Access.FileSystemRights
                    'Inherited' = $Access.IsInherited
                    'Propagation' = $Propagation
                }
            }
         }
    }   
}

#Output Phase

$Output | Group-Object "Share" | ForEach-Object {
    $ShareOutput = $_.Group
    $FilteredOutput = $ShareOutput | Where-Object { $_."Group/User" -notin $NameFilter } 
    $FormattedOutput = $FilteredOutput | Format-Table "Group/User", "Permissions", "Inherited", "Propagation" -GroupBy "Folder Name"
    $FormattedOutput | Out-File "$OutputPath\$OutputFileName-$($_.Name).txt" -Width 120
    Write-Host "================`n$($_.Name)`n================"
    $FormattedOutput
}


$FilteredOutput = $Output | Where-Object { $_."Group/User" -notin $NameFilter } 
$FilteredOutput | Export-Csv -Path "$OutputPath\$OutputFileName.csv"