Skip to content

Instantly share code, notes, and snippets.

@Rapidhands
Last active April 7, 2025 12:32
Show Gist options
  • Save Rapidhands/58fe8bfe6acdafefe8664719f3ecfdf9 to your computer and use it in GitHub Desktop.
Save Rapidhands/58fe8bfe6acdafefe8664719f3ecfdf9 to your computer and use it in GitHub Desktop.
How to chek NTFS permissions on subfolders. Example with large comments for explanation
<#
.SYNOPSIS
Analyzes NTFS permissions on folders and exports results to CSV or XLSX format.
.DESCRIPTION
This script recursively analyzes NTFS permissions on all subfolders of a specified path
and exports the results to either CSV or Excel format. It uses the NTFSSecurity module
for accurate permission analysis and ImportExcel for Excel export capabilities.
The name of the export file is NTFS_Permissions followed by the current date and time.
The script also provides progress updates during the analysis process.
The script is amply (and perhaps too much and unnecessarily) commented,
but it is intended to be a script providing explanations of the why and how to beginners.
The script was also run through the PSScriptAnalyzer module (using its Invoke-ScriptAnalyzer cmdlet) to verify that it complies with best practices.
.PARAMETER RootPath
The root folder path to analyze. Must be a valid directory path.
.PARAMETER ExportPath
The directory where the export file will be created. Must be a valid directory path.
.PARAMETER ExportFormat
The export format, either 'CSV', 'XLSX', or 'Both'
Default is XLSX.
Valid values are 'CSV', 'XLSX', 'Both'.
.EXAMPLE
.\Check-NTFS.ps1 -RootPath "C:\Data" -ExportPath "C:\Reports"
Analyzes all folders under C:\Data and exports results to a CSV file (default) in C:\Reports
.EXAMPLE
.\Check-NTFS.ps1 -RootPath "C:\Data" -ExportPath "C:\Reports" -ExportFormat XLSX
Analyzes all folders under C:\Data and exports results to an Excel file in C:\Reports
.EXAMPLE
.\Check-NTFS.ps1 -RootPath "D:\Shared" -ExportPath "D:\Audit" -ExportFormat CSV -Verbose
Analyzes permissions with verbose output and exports to CSV format
.EXAMPLE
.\Check-NTFS.ps1 -RootPath "D:\Shared" -ExportPath "D:\Audit" -ExportFormat Both -Verbose
Analyzes permissions with verbose output and exports to both CSV and XLSX formats
.EXAMPLE
Get-Help .\Check-NTFS.ps1 -showWindow
Displays the help information for the script in a window
.NOTES
Requires modules: NTFSSecurity, ImportExcel
Author: O. FERRIERE
Version: 1.0
Last Modified: 2024/04/07
#>
[CmdletBinding()]
[OutputType([System.Void])]
param(
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Path of the folder to be analyzed')]
[ValidateScript({
if (-Not (Test-Path $_) )
{
throw 'Folder does not exist'
}
if (-Not (Test-Path $_ -PathType Container) )
{
throw 'The Path argument must be a Directory. Files are not allowed.'
}
return $true
})]
[System.IO.DirectoryInfo]$RootPath,
[Parameter(Mandatory = $true,
Position = 1,
HelpMessage = 'Results export path')]
[ValidateScript({
if (-Not (Test-Path $_) )
{
throw 'Export path does not exist'
}
if (-Not (Test-Path $_ -PathType Container) )
{
throw 'The ExportPath argument must be a Directory.'
}
return $true
})]
[System.IO.DirectoryInfo]$ExportPath,
[Parameter(Position = 2,
HelpMessage = 'Results export format')]
[ValidateSet('CSV', 'XLSX', 'Both')]
[string]$ExportFormat = 'XLSX'
)
BEGIN
{
Write-Verbose "Script started at $(Get-Date)"
#region Parameters
<#
using region to group parameters and their description
This is a common practice in PowerShell scripts to improve readability and organization.
#>
# Initialization of time counter
$StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
$StartTime = Get-Date
$ExportFiles = @()
# Initialize result collection
<#
The use of a generic list allows for efficient dynamic resizing and avoids the overhead of using an array.
The final export will be done in a single operation, which is more efficient than exporting each item individually.
The final result will be a collection of PSCustomObject, which is suitable for export to CSV or Excel.
This approach is more efficient than using an array, as it avoids the need to resize the array dynamically.
ref. : https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/using-collections-in-powershell?view=powershell-7.3#using-generic-collections
#>
$Result = [System.Collections.Generic.List[PSCustomObject]]::new()
#endregion Parameters
#region Checking and installing required modules if necessary
$RequiredModules = @('NTFSSecurity', 'ImportExcel')
foreach ($Module in $RequiredModules)
{
try
{
if (-not (Get-Module -ListAvailable -Name $Module))
{
Write-Verbose "Module $Module not found. Attempting to install..."
Install-Module -Name $Module -Scope CurrentUser -Force -AllowClobber
Write-Verbose "Module $Module installed successfully"
}
Import-Module $Module -ErrorAction Stop
Write-Verbose "Module $Module loaded successfully"
}
catch
{
throw "Failed to install or load module $Module : $_"
}
}
#endregion Checking and installing required modules if necessary
}
PROCESS
{
try
{
Write-Verbose "Starting permissions analysis on $RootPath"
# Gathering folders list in recursive way
$AllFolders = Get-ChildItem -Path $RootPath.FullName -Directory -Recurse -ErrorAction Stop
$TotalFolders = $AllFolders.Count
$Current = 0
foreach ($Folder in $AllFolders)
{
$Current++
Write-Progress -Activity 'Permissions Analysis' -Status "$Current / $TotalFolders folders" -PercentComplete (($Current / $TotalFolders) * 100)
Write-Verbose "Directory Analysis : $($Folder.FullName)"
# Gathering ACLs with NTFSSecurity
$AllAcls = Get-NTFSAccess -Path $Folder.FullName
foreach ($Acl in $AllAcls)
{
$Result.Add([PSCustomObject]@{
Path = $Folder.FullName
Account = $Acl.Account
AccessType = $Acl.AccessRights
TypePermission = $Acl.AccessControlType
Herite = $Acl.IsInherited
Source = if ($Acl.IsInherited)
{
'Inherited'
}
else
{
'Direct'
}
AnalysisDate = Get-Date
})
}
}
}
catch
{
Write-Error "Error during processing : $_"
throw
}
}
END
{
try
{
Write-Verbose "Exporting results to $ExportPath"
$exportFiles = @()
# Export results according to the chosen format
$ExcelParams = @{
Path = Join-Path -Path $ExportPath.FullName -ChildPath $('NTFS_Permissions_At_' + $(Get-Date -Format yyyy-MM-dd) + '.xlsx')
AutoSize = $true
TableName = 'NTFS_Permissions'
Worksheet = 'Permissions'
FreezeTopRow = $true
AutoFilter = $true
}
$CsvParams = @{
Path = Join-Path -Path $ExportPath.FullName -ChildPath $('NTFS_Permissions_At_' + $(Get-Date -Format yyyy-MM-dd) + '.csv')
NoTypeInformation = $true
Encoding = 'UTF8'
}
switch ($ExportFormat)
{
'XLSX'
{
$Result | Export-Excel @ExcelParams
$exportFiles += $ExcelParams.Path
}
'CSV'
{
$Result | Export-Csv @CsvParams
$exportFiles += $CsvParams.Path
}
'Both'
{
$Result | Export-Excel @ExcelParams
$Result | Export-Csv @CsvParams
$exportFiles += $ExcelParams.Path, $CsvParams.Path
Write-Verbose 'Exported in both formats successfully'
}
}
}
catch
{
Write-Error "Error during export : $_"
throw
}
finally
{
# Stopping the stopwatch and displaying the execution summary
$stopWatch.Stop()
<#
There are different ways to calculate the exectution time in PowerShell.
The method used here is the most efficient and accurate.
The use of the StopWatch class allows for precise measurement of elapsed time.
The StopWatch class is part of the System.Diagnostics namespace and provides a high-resolution timer.
It is more efficient than using the Get-Date cmdlet for measuring elapsed time.
#>
<#
there are different ways to display the execution summary in PowerShell.
The method used here is the most efficient and user-friendly.
The use of Write-Host allows for colored output, which is more readable and visually appealing.
The use of Write-Progress allows for a progress bar to be displayed, which is more informative and user-friendly.
The use of Write-Verbose allows for detailed output, which is more informative and useful for debugging.
The Best Practices recommendation are to avoid using Write-Host for outputting information.
"Avoid using Write-Host because it might not work in all hosts,
does not work when there is no host, and (prior to PS 5.0) cannot be suppressed, captured, or redirected.
Instead, use Write-Output, Write-Verbose, or Write-Information."
However, in this case, the use of Write-Host is justified because the script is intended to be run interactively and the output is meant to be displayed on the console.
The use of Write-Host allows for colored output, which is more readable and visually appealing.
#>
Write-Host "`n=== Execution Summary ===" -ForegroundColor Cyan
Write-Host "Start of analysis : $StartTime"
Write-Host "End of analysis : $(Get-Date)"
Write-Host "Elapsed time : $($StopWatch.Elapsed.Hours)h $($StopWatch.Elapsed.Minutes)m $($StopWatch.Elapsed.Seconds)s"
Write-Host "Total folders analyzed : $($Result.Count)"
Write-Host "`n Export Files created :" -ForegroundColor Cyan
foreach ($file in $exportFiles)
{
Write-Host "- $file"
}
Write-Host "========================`n"
Write-Progress -Activity 'Permissions Analysis' -Completed
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment