Advertisement
joedigital

Folder Group Permissions with exceptions

May 9th, 2025
185
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. .SYNOPSIS
  3.     Lists groups and users with permissions on folders, excluding specified default identities.
  4. .DESCRIPTION
  5.     This script recursively scans a specified folder path, identifies all security principals (users or groups)
  6.     that have permissions, and lists those whose names are not in a predefined exclusion list.
  7.     The exclusion list targets common administrative, system, and broad access accounts.
  8. .PARAMETER Path
  9.     The root folder path to scan for permissions. This parameter is mandatory.
  10. .EXAMPLE
  11.     .\Get-FolderPermissionsExcludingDefaults.ps1 -Path "C:\Shares\DepartmentX"
  12.  
  13.     This command will scan the "C:\Shares\DepartmentX" folder and its subfolders,
  14.     displaying security principals (groups or users) with their permissions,
  15.     excluding the predefined system/admin/common groups.
  16. .EXAMPLE
  17.     Get-ChildItem -Path "C:\Projects" -Directory | .\Get-FolderPermissionsExcludingDefaults.ps1
  18.  
  19.     This command gets all subdirectories of "C:\Projects" and pipes them to the script
  20.     to analyze permissions for each.
  21. .OUTPUTS
  22.     PSCustomObject
  23.     Outputs objects with the following properties:
  24.     - FolderPath: The full path to the folder.
  25.     - Identity: The security principal (e.g., "DOMAIN\GroupName" or "BUILTIN\Users").
  26.     - Permissions: The file system rights granted to the security principal (e.g., "ReadAndExecute, Synchronize").
  27.     - AccessControlType: Specifies whether the rights are "Allow" or "Deny".
  28.     - IsInherited: Indicates if the permission is inherited from a parent folder (True/False).
  29. .NOTES
  30.     Author: Gemini
  31.     Date: 2025-05-09
  32.     The script filters identities based on the name part of the IdentityReference (e.g., "GroupName" from "DOMAIN\GroupName").
  33.     The exclusion list matching is case-insensitive.
  34.     If you need to exclude "Backup Operators" (the built-in group), ensure "Backup Operators" is in the $ExcludedIdentities list.
  35.     The script requires appropriate permissions to read ACLs of the target folders.
  36. #>
  37. [CmdletBinding()]
  38. param (
  39.     [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
  40.     [string]$Path
  41. )
  42.  
  43. begin {
  44.     # List of identity names to exclude (case-insensitive matching will be used)
  45.     # These are the "name parts" after any domain or "BUILTIN\" prefix.
  46.     $Global:ExcludedIdentities = @(
  47.         "Domain Admins",
  48.         "Backup_OPs",      # This is per user request. The built-in group is "Backup Operators".
  49.                            # Add "Backup Operators" to this list if you want to exclude the built-in group.
  50.         "Administrators",  # Matches BUILTIN\Administrators
  51.         "SYSTEM",          # Matches NT AUTHORITY\SYSTEM
  52.         "Domain Users",
  53.         "CREATOR OWNER",   # Matches special identity
  54.         "Authenticated Users", # Matches well-known group
  55.         "Users"            # Matches BUILTIN\Users
  56.     )
  57.  
  58.     Write-Verbose "Script initialized. Excluded identities (case-insensitive): $($Global:ExcludedIdentities -join ', ')"
  59.     $Global:Results = [System.Collections.Generic.List[PSCustomObject]]::new()
  60. }
  61.  
  62. process {
  63.     # Validate the path
  64.     if (-not (Test-Path -Path $Path -PathType Container)) {
  65.         Write-Error "The path '$Path' does not exist or is not a folder. Please provide a valid folder path. Skipping this item."
  66.         return # Changed from 'continue' to 'return' as process block handles one item from pipeline
  67.     }
  68.  
  69.     Write-Verbose "Starting permission scan for base folder: $Path"
  70.  
  71.     # Create a list of folders to process: the root path itself and all its subdirectories
  72.     $FoldersToProcess = [System.Collections.Generic.List[System.IO.DirectoryInfo]]::new()
  73.     try {
  74.         $RootFolderItem = Get-Item -Path $Path -ErrorAction Stop
  75.         $FoldersToProcess.Add($RootFolderItem)
  76.  
  77.         $SubFolders = Get-ChildItem -Path $Path -Directory -Recurse -ErrorAction SilentlyContinue # Continue if some subfolders are inaccessible
  78.         if ($null -ne $SubFolders) {
  79.             $FoldersToProcess.AddRange($SubFolders)
  80.         }
  81.     }
  82.     catch {
  83.         Write-Error "Error accessing folder structure for '$Path': $($_.Exception.Message)"
  84.         return
  85.     }
  86.  
  87.     foreach ($Folder in $FoldersToProcess) {
  88.         Write-Verbose "Processing folder: $($Folder.FullName)"
  89.         try {
  90.             # Get-Acl can be slow; ensure errors don't stop the whole script for one folder.
  91.             $Acl = Get-Acl -Path $Folder.FullName -ErrorAction SilentlyContinue
  92.             if (-not $Acl) {
  93.                 Write-Warning "Could not retrieve ACL for $($Folder.FullName). Skipping."
  94.                 continue
  95.             }
  96.  
  97.             foreach ($Ace in $Acl.Access) {
  98.                 $IdentityName = $Ace.IdentityReference.Value
  99.                 # Extract the name part, e.g., "Administrators" from "BUILTIN\Administrators" or "SalesUsers" from "CONTOSO\SalesUsers"
  100.                 $IdentityNamePart = $IdentityName.Split('\')[-1]
  101.  
  102.                 # Check if the identity name part is in the exclusion list (PowerShell -eq is case-insensitive for strings)
  103.                 $IsExcluded = $false
  104.                 foreach ($ExcludedItem in $Global:ExcludedIdentities) {
  105.                     if ($IdentityNamePart -eq $ExcludedItem) {
  106.                         $IsExcluded = $true
  107.                         Write-Verbose "Excluding '$IdentityName' because '$IdentityNamePart' is in the exclusion list."
  108.                         break
  109.                     }
  110.                 }
  111.  
  112.                 if (-not $IsExcluded) {
  113.                     $OutputObject = [PSCustomObject]@{
  114.                         FolderPath        = $Folder.FullName
  115.                         Identity          = $IdentityName
  116.                         Permissions       = $Ace.FileSystemRights.ToString() # Convert FileSystemRights enum to string
  117.                         AccessControlType = $Ace.AccessControlType.ToString()
  118.                         IsInherited       = $Ace.IsInherited
  119.                     }
  120.                     $Global:Results.Add($OutputObject)
  121.                 }
  122.             }
  123.         }
  124.         catch {
  125.             # Catch errors specific to processing a single folder's ACL
  126.             Write-Warning "Error processing ACL for folder '$($Folder.FullName)': $($_.Exception.Message)"
  127.         }
  128.     }
  129. }
  130.  
  131. end {
  132.     if ($Global:Results.Count -eq 0) {
  133.         Write-Host "No non-excluded identities with permissions found in the processed path(s)."
  134.     } else {
  135.         Write-Output $Global:Results
  136.     }
  137.     Write-Verbose "Script finished. Total matching ACEs found: $($Global:Results.Count)"
  138. }
  139.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement