PowerShell can show you the NTFS permissions on any folder without opening a single Properties dialog. The cmdlet that does the work is Get-Acl, which reads a folder's security descriptor and exposes its owner and access control list (ACL) as objects you can filter, sort, and export.
This cheat sheet collects the commands administrators reach for most often: viewing a single folder's permissions, walking a whole directory tree, isolating a specific user or group, spotting explicit and inherited entries, and saving the results to CSV. Every snippet works in Windows PowerShell 5.1 and PowerShell 7, and none of them change anything on disk.
- The one-line answer
- Understanding Get-Acl output
- View permissions for one folder
- View permissions for every subfolder
- Filter by user or group
- Find explicit permissions and broken inheritance
- Find Everyone, Deny, and orphaned SID entries
- View and search by folder owner
- Network shares and remote servers
- Export folder permissions to CSV
- FileSystemRights reference
- icacls equivalents
- Where Get-Acl runs out of road
The One-Line Answer
To view the permissions on a folder, call Get-Acl and expand the Access property:
(Get-Acl -Path "C:\Data").Access |
Format-Table IdentityReference, FileSystemRights, AccessControlType, IsInherited -AutoSize
The output lists one row per access control entry (ACE):
IdentityReference FileSystemRights AccessControlType IsInherited
----------------- ---------------- ----------------- -----------
BUILTIN\Administrators FullControl Allow True
NT AUTHORITY\SYSTEM FullControl Allow True
CORP\Finance-RW Modify Allow False
CORP\Domain Users ReadAndExecute Allow True
That is usually enough to answer "who has access to this folder?" for a single path. The rest of this page covers the variations you need once the question becomes "which folders?", "which users?", or "show me everything in a spreadsheet".
Understanding Get-Acl Output
Get-Acl returns a System.Security.AccessControl.DirectorySecurity object.
Pipe it to Format-List to see every property:
Get-Acl -Path "C:\Data" | Format-List
| Property | What it tells you |
|---|---|
Path | The folder the descriptor belongs to, prefixed with the provider name |
Owner | The account that owns the folder (owners can always change its permissions) |
Group | The primary group, rarely meaningful on Windows |
Access | The list of access control entries, one FileSystemAccessRule per entry |
AreAccessRulesProtected | True when inheritance from the parent is disabled |
Sddl | The whole descriptor in Security Descriptor Definition Language, useful for comparisons |
Each entry in Access has its own set of properties:
| Property | Meaning | Typical values |
|---|---|---|
IdentityReference | The user or group the entry applies to | CORP\jsmith, BUILTIN\Users, or a raw SID if the account no longer exists |
FileSystemRights | The rights granted or denied | FullControl, Modify, ReadAndExecute, Write, or a number for generic masks |
AccessControlType | Whether the entry allows or denies | Allow, Deny |
IsInherited | Whether the entry came from a parent folder | True, False |
InheritanceFlags | What the entry passes down to children | ContainerInherit (subfolders), ObjectInherit (files), None |
PropagationFlags | How far inheritance propagates | None, InheritOnly, NoPropagateInherit |
Tip: Get-Acl needs Read Permissions on the folder, but not read access to its contents. Running an elevated session avoids most "access denied" errors on system folders.
View Permissions for One Folder
For a quick look, expand Access and pick the columns you care about:
Get-Acl -Path "C:\Data" |
Select-Object -ExpandProperty Access |
Format-Table IdentityReference, FileSystemRights, AccessControlType, IsInherited, InheritanceFlags -AutoSize
To include the folder path in each row (useful once you start combining multiple folders), add a calculated property:
$path = "C:\Data"
(Get-Acl -Path $path).Access |
Select-Object @{Name = "Path"; Expression = { $path }},
IdentityReference, FileSystemRights, AccessControlType, IsInherited
Paths containing square brackets or other wildcard characters need -LiteralPath
instead of -Path:
Get-Acl -LiteralPath "C:\Data\Reports [2025]"
View Permissions for Every Subfolder
Pipe a recursive directory listing into Get-Acl. The -Directory switch skips
files, which is what you want for a folder permissions report:
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
Get-Acl |
Select-Object Path, Owner, AreAccessRulesProtected
To list every access control entry for every subfolder, flatten the results so each entry becomes its own row:
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$folder = $_.FullName
(Get-Acl -LiteralPath $folder).Access | ForEach-Object {
[PSCustomObject]@{
Path = $folder
Identity = $_.IdentityReference.Value
Rights = $_.FileSystemRights
Type = $_.AccessControlType
IsInherited = $_.IsInherited
}
}
} | Format-Table -AutoSize
Folders you cannot read are skipped silently. To capture them for follow-up, collect the errors instead of discarding them:
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue -ErrorVariable scanErrors |
Get-Acl -ErrorAction SilentlyContinue -ErrorVariable +scanErrors
$scanErrors | Select-Object -ExpandProperty TargetObject
Performance note: Get-Acl queries each folder individually, so a tree with
100,000 folders means 100,000 security descriptor reads plus a name lookup for every SID.
Expect minutes to hours on large file servers. Limit the scan with -Depth
on Get-ChildItem, or see the alternatives below for large environments.
Filter by User or Group
Once entries are flattened, Where-Object narrows them to a specific account or
group. Match on IdentityReference, which is a DOMAIN\Name string:
# every folder under C:\Data where CORP\jsmith is named directly in the ACL
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$folder = $_.FullName
(Get-Acl -LiteralPath $folder).Access |
Where-Object { $_.IdentityReference.Value -eq "CORP\jsmith" } |
Select-Object @{Name = "Path"; Expression = { $folder }}, FileSystemRights, AccessControlType, IsInherited
}
Use -like with wildcards for partial matches, such as every group with
"Finance" in its name:
(Get-Acl -Path "C:\Data").Access |
Where-Object { $_.IdentityReference.Value -like "*Finance*" }
Important: this only finds accounts that are named directly in the ACL.
A user who has access because they belong to CORP\Finance-RW will not appear
when you search for their username. Resolving that requires expanding group membership,
which Get-Acl does not do (see limitations).
Find Explicit Permissions and Broken Inheritance
Explicit entries (those added directly to a folder rather than inherited) are where most
permission sprawl lives. Filter on IsInherited:
# folders that have at least one explicit (non-inherited) entry
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
Get-Acl |
Where-Object { $_.Access | Where-Object { -not $_.IsInherited } } |
Select-Object Path
Folders where inheritance has been disabled entirely report
AreAccessRulesProtected as True:
# folders that no longer inherit permissions from their parent
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
Get-Acl |
Where-Object { $_.AreAccessRulesProtected } |
Select-Object Path, Owner
Our guide to broken inheritance explains why these folders deserve attention and how to decide which breaks are legitimate.
Find Everyone, Deny, and Orphaned SID Entries
A few identity patterns are worth searching for on their own. Grants to Everyone or Authenticated Users expose data to the whole organization:
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
$folder = $_.FullName
(Get-Acl -LiteralPath $folder).Access |
Where-Object {
$_.IdentityReference.Value -in @("Everyone", "NT AUTHORITY\Authenticated Users", "BUILTIN\Users") -and
$_.AccessControlType -eq "Allow"
} |
Select-Object @{Name = "Path"; Expression = { $folder }}, IdentityReference, FileSystemRights, IsInherited
}
Deny entries override allows and are a frequent cause of confusing "access denied" errors:
(Get-Acl -Path "C:\Data").Access | Where-Object { $_.AccessControlType -eq "Deny" }
Orphaned SIDs belong to deleted accounts. Windows cannot translate them
to a name, so IdentityReference contains the raw SID string:
(Get-Acl -Path "C:\Data").Access |
Where-Object { $_.IdentityReference.Value -match "^S-1-5-21-" }
See the dedicated guides on Everyone Full Control and orphaned SIDs for remediation steps.
View and Search by Folder Owner
The owner is a property of the security descriptor rather than an access entry:
(Get-Acl -Path "C:\Data").Owner
# every subfolder owned by a specific account
Get-ChildItem -Path "C:\Data" -Directory -Recurse -ErrorAction SilentlyContinue |
Get-Acl |
Where-Object { $_.Owner -eq "CORP\jsmith" } |
Select-Object Path, Owner
Folders owned by individual users (rather than Administrators or a service group) are worth reviewing, because an owner can always regain Full Control regardless of the ACL.
Network Shares and Remote Servers
Get-Acl accepts UNC paths, so you can read the NTFS permissions on a share from any machine that can reach it:
(Get-Acl -Path "\\FileServer01\Finance").Access | Format-Table -AutoSize
Note that this returns the NTFS permissions of the folder behind the share, not the
share permissions themselves. Share-level permissions are a separate layer; read them on
the file server with Get-SmbShareAccess:
Invoke-Command -ComputerName FileServer01 -ScriptBlock {
Get-SmbShareAccess -Name "Finance"
}
For large remote trees, run the whole scan on the file server with
Invoke-Command so the security descriptor reads happen locally instead of
across the network:
Invoke-Command -ComputerName FileServer01 -ScriptBlock {
Get-ChildItem -Path "D:\Shares\Finance" -Directory -Recurse -ErrorAction SilentlyContinue |
Get-Acl |
Select-Object Path, Owner, AreAccessRulesProtected
}
Effective access over the network is the more restrictive of the share and NTFS layers. Our share permissions FAQ walks through how the two combine.
Export Folder Permissions to CSV
The flattened-entry pattern from earlier is exactly what you want in a spreadsheet. This script records every access entry for every folder in a tree and writes it to CSV:
$root = "C:\Data"
$output = "C:\Reports\folder-permissions.csv"
$folders = @(Get-Item -LiteralPath $root) +
(Get-ChildItem -Path $root -Directory -Recurse -ErrorAction SilentlyContinue)
$folders | ForEach-Object {
$folder = $_.FullName
$acl = Get-Acl -LiteralPath $folder -ErrorAction SilentlyContinue
if ($null -eq $acl) { return }
foreach ($ace in $acl.Access) {
[PSCustomObject]@{
Path = $folder
Owner = $acl.Owner
InheritanceOff = $acl.AreAccessRulesProtected
Identity = $ace.IdentityReference.Value
Rights = $ace.FileSystemRights.ToString()
Type = $ace.AccessControlType.ToString()
IsInherited = $ace.IsInherited
InheritanceFlags = $ace.InheritanceFlags.ToString()
PropagationFlags = $ace.PropagationFlags.ToString()
}
}
} | Export-Csv -Path $output -NoTypeInformation -Encoding UTF8
Open the result in Excel, filter the IsInherited column to
FALSE, and you have a list of every explicit permission in the tree.
Keep a dated copy each time you run it and you can diff two exports to see what changed.
Keep the root folder: Get-ChildItem returns only descendants, so the
script above adds the root with Get-Item first. Without it, the top-level
folder's own permissions are missing from the report.
FileSystemRights Reference
FileSystemRights is a flags enumeration. The named values you will see most
often, and what they correspond to in the Windows Security tab:
| Value | Security tab equivalent | Notes |
|---|---|---|
FullControl | Full control | Includes changing permissions and taking ownership |
Modify, Synchronize | Modify | Read, write, execute, and delete |
ReadAndExecute, Synchronize | Read & execute | The usual "read-only" grant for folders |
Read, Synchronize | Read | Read without traverse/execute |
Write, Synchronize | Write | Create and modify, but not read |
DeleteSubdirectoriesAndFiles | Delete subfolders and files | Appears in advanced (special) permissions |
268435456 | Full control (generic) | GENERIC_ALL on an inherit-only entry |
-1610612736 | Read & execute (generic) | GENERIC_READ plus GENERIC_EXECUTE on an inherit-only entry |
-536805376 | Modify (generic) | GENERIC_WRITE plus GENERIC_READ plus GENERIC_EXECUTE |
Synchronize is added automatically to most named rights and can be ignored.
The numeric values are generic access masks that Windows uses on inherit-only entries;
they are normal and not a sign of a damaged ACL. To test for a specific right regardless
of how it is expressed, use a bitwise comparison:
# entries that include write access, however it is expressed
(Get-Acl -Path "C:\Data").Access |
Where-Object { ($_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::Write) -ne 0 }
icacls Equivalents
If you prefer the classic command line, icacls covers the same ground with
terser output. It is also handy inside batch files and scheduled tasks where PowerShell
is unavailable.
| Task | PowerShell | icacls |
|---|---|---|
| View one folder | (Get-Acl "C:\Data").Access | icacls "C:\Data" |
| View a whole tree | Get-ChildItem -Recurse -Directory | Get-Acl | icacls "C:\Data" /t |
| Find one account | Where-Object IdentityReference -eq ... | icacls "C:\Data" /t /findsid CORP\jsmith |
| Save ACLs to a file | Export-Csv (see above) | icacls "C:\Data" /save acls.txt /t |
| Check for damaged ACLs | n/a | icacls "C:\Data" /verify /t |
icacls prints rights as letter codes: (F) full control, (M) modify,
(RX) read and execute, (R) read, (W) write, with
(I) marking inherited entries and (OI), (CI) marking
object and container inheritance.
Where Get-Acl Runs Out of Road
Get-Acl is the right tool for checking a folder or scripting a one-off export. It becomes awkward as soon as the question shifts from "what is on this ACL?" to "who can actually get in?" across a file server:
- No group expansion. Entries name groups, not the users inside them. Answering "does jsmith have access?" means resolving nested Active Directory membership yourself.
- No effective access. Combining allow and deny entries, nested groups, and share permissions into a single answer is left to you.
- Slow on large trees. One descriptor read and one SID lookup per folder adds up to hours on servers with hundreds of thousands of folders.
- Long paths. Paths beyond 260 characters need the
\\?\prefix or PowerShell 7, and deeply nested trees still trip up Get-ChildItem. - No shares. Share permissions require a separate cmdlet and a separate join in your script.
- Raw output. Turning a CSV into something a manager or auditor can read is another project.
Permissions Reporter was built for exactly that point. It scans local and network folders in parallel, expands direct and nested group membership, reports share permissions alongside NTFS permissions, ships with filter presets for explicit entries, Everyone grants, broken inheritance, and orphaned SIDs, and exports to Excel, PDF, HTML, XML, CSV, and JSON. The command-line interface slots into the same scheduled tasks and scripts you already use, so it complements PowerShell rather than replacing it. The Basic edition is free.
Related Resources
- Set Folder Permissions with PowerShell (Set-Acl Cheat Sheet) - The companion page for changing ACLs
- NTFS Permissions Overview - How allow, deny, and inheritance work
- NTFS Permissions Best Practices - Designing ACLs that stay manageable
- Auditing NTFS Permissions - Building a repeatable audit process
- Broken Inheritance - Finding folders that stopped inheriting
- Everyone Full Control - Finding and fixing over-exposed folders
- Orphaned SIDs - Cleaning up permissions for deleted accounts
- Share Permissions FAQ - How share and NTFS permissions combine
- NTFS Permissions Troubleshooting FAQ - Common problems and fixes