Skip to content

Instantly share code, notes, and snippets.

@caasig
Forked from aaronpmiller/Get-FileShareReport.ps1
Created March 15, 2021 03:40
Show Gist options
  • Save caasig/778bac710d6a468d721fca08e7042474 to your computer and use it in GitHub Desktop.
Save caasig/778bac710d6a468d721fca08e7042474 to your computer and use it in GitHub Desktop.
PowerShell script to create a CSV file containing a list of ACEs (access control entries) for all file paths at and under the root path specified.
<#
.SYNOPSIS
Create a CSV file containing a list of ACEs (access control entries) for all file paths at and under the root path specified.
.DESCRIPTION
This script will pull a list of all directory at and under the root path specified.
In a parallel process it will pull the ACL for each directory and list out each ACE that is not inherited, the ACEs in the root ACL will be listed as is inherited or not.
This script uses the NTFSSecurity module (http://ntfssecurity.codeplex.com/) to work around long path issues via an embeded AlphaFS .NET class. Please ensure this module in in the PSModulePath. As an added benefity this class appears to be faster at enumerating directories as well.
.PARAMETER rootPath
The root path you want the report to start on.
.PARAMETER CSVExportPath
The full file path you want to save the report to.
.PARAMETER threads
How many threads do you want to run in parallel when evaluating ACLs?
.LINK
http://ntfssecurity.codeplex.com
#>
#Requires -Modules NTFSSecurity
PARAM (
[Parameter(Mandatory=$true)][string]$rootPath,
[Parameter(Mandatory=$true)][string]$CSVExportPath,
[Parameter(Mandatory=$false)][int]$threads=25
)
# The NTFSSecurity module is not signed so we need to momentarily workaround the execution policy.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
Workflow Get-FileShareReport {
PARAM (
[string]$rootPath,
[int]$threads
)
# Initialze an empty array to hold our report items
[array]$report = @()
# Get the ACL for our root level path pulling all properties.
$rootAccess = [array](Get-Access -Path $rootPath | Select-Object -Property *)
# We want each ace repot item to have a property 'isInheritedDuplicate' that indicates if it is not-inherited is it a duplicate of a inherited ace. (indicator that it's unnecessary).
# Lastly we want the account attribute to be a simple string instead of an identity reference.
foreach ($rootAce in $rootAccess) {
Add-Member -InputObject $rootAce -MemberType NoteProperty -Name 'isInheritedDuplicate' -Value 'root'
Add-Member -InputObject $rootAce -MemberType NoteProperty -Name 'Account' -Value $rootAce.Account.AccountName -Force
$report += $rootAce
}
# So that we can easily compare our ace objects we define the properties we want to evaluate.
$aceCompareProperties = @('Account','InheritanceFlags','PropagationFlags','AccessRights','AccessControlType')
# Let's get an array of all child paths by fullname. Note the use of Get-ChildItem2 from NTFSSecurity / AlphaFS.
$folderPaths = [array](Get-ChildItem2 -Path $rootPath -Directory -IncludeSystem -IncludeHidden -SkipMountPoints -SkipSymbolicLinks -Recurse | ForEach-Object {$_.FullName})
# Processing each path in parallel to speed things up (primary reason for using workflows), throttleing it to 25 threads for the time-being.
ForEach -parallel -throttlelimit $threads ($folderPath in $folderPaths) {
# Get the acl for the path, pulling all properties.
$folderAccess = [array](Get-Access -Path $folderPath | Select-Object -Property *)
# Again we want the account attribute to be a simple string (works better for the Compare-Object cmdlet).
foreach ($folderACE in $folderAccess) {
Add-Member -InputObject $folderACE -MemberType NoteProperty -Name 'Account' -Value $folderACE.Account.AccountName -Force
}
# Seperate the inherited aces into their own array that we can compare against.
$inheritedFolderAccess = [array]($folderAccess | Where-Object -FilterScript {$_.IsInherited -eq $true})
# Seperate the non-inherited aces into their own array to process.
$nonInheritedFolderAccess = [array]($folderAccess | Where-Object -FilterScript {$_.IsInherited -ne $true})
# Process each non-inherited ace
foreach ($ace in $nonInheritedFolderAccess) {
# Create an empty array to stare (using an array so we can use a simple count comparison for the isInheritedDuplicateValue check.
$CompareResults = @()
# Ensure we have inherited aces to compare against first
if ($inheritedFolderAccess) {
# Compare our ace against inherited aces based on the choosen ace properties. Only show objects that are identical.
$CompareResults = [array](Compare-Object -ReferenceObject $ace -DifferenceObject $inheritedFolderAccess -Property $aceCompareProperties -ExcludeDifferent -IncludeEqual)
}
# The CompareResults.count should be greater than 0 if there was a duplicate, evaluating to $true otherwise false.
$isInheritedDuplicateValue = $CompareResults.Count -gt 0
# Add the [bool] value of $isInheritedDuplicateValue for the property isInheritedDuplicate
Add-Member -InputObject $ace -MemberType NoteProperty -Name 'isInheritedDuplicate' -Value $isInheritedDuplicateValue -Force
# Add the ace item to our report! Prefixing the workflow scope since we are in the foreach parrallel loop.
$workflow:report += $ace
}
}
# Woot! We're done let's shoot back the report array!
Return $report
}
# Starting to get share report data
$aclReport = Get-FileShareReport -rootPath $rootPath -threads $threads
# Finished getting share report data, choose the values we want and export to .csv ...
$aclReport | Select-Object -Property *,@{Name='PathLength';Expression={$_.FullName.Length}} -ExcludeProperty Name,InheritedFrom,PSComputerName,PSShowComputerName,PSSourceJobInstanceId | Export-Csv -Path $CSVExportPath -NoTypeInformation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment