Get a Clean NTFS Permissions Report Without Parsing Raw SDDL in PowerShell

PowerShell's Get-Acl is the standard approach to reading NTFS permissions programmatically. It works — technically. It outputs the Security Descriptor for any path, which contains every ACL entry in SDDL format.

SDDL looks like this: O:BAG:SYD:AI(A;OICIID;FA;;;BA)(A;OICIID;FA;;;SY)(A;OICIID;0x1200a9;;;BU)

That string says: Owner is Built-in Administrators, Group is SYSTEM, DACL has auto-inherit, and there are three ACL entries granting different rights to different principals. If you can read that fluently, you don't need this article. If you can't — and most sysadmins can't — you need a different approach to generating a permissions report.


What Get-Acl actually gives you

The Get-Acl cmdlet returns an object with .Access property containing FileSystemAccessRule objects. You can enumerate these:

(Get-Acl "C:\SharedFolder").Access | Format-Table IdentityReference, FileSystemRights, AccessControlType, IsInherited

This produces a table for one folder. To scan an entire directory tree, you wrap it in Get-ChildItem -Recurse:

Get-ChildItem -Recurse -Directory "C:\SharedFolder" | ForEach-Object {
    $acl = Get-Acl $_.FullName
    foreach ($ace in $acl.Access) {
        [PSCustomObject]@{
            Path = $_.FullName
            Principal = $ace.IdentityReference
            Rights = $ace.FileSystemRights
            Type = $ace.AccessControlType
            Inherited = $ace.IsInherited
        }
    }
} | Export-Csv permissions.csv -NoTypeInformation

This works on small, well-behaved directory trees. On a real file server, it starts falling apart:

Long paths. Paths over 260 characters cause Get-ChildItem to fail unless you use \\?\ prefix paths or the -LiteralPath parameter with specific handling. Many file servers have paths that exceed this limit in deeply nested departmental folders.

Access denied errors. Even running as administrator, some system paths or junction points produce access denied errors that terminate the script or produce incomplete results unless you wrap everything in try-catch blocks.

SID resolution failures. Orphaned SIDs that don't resolve to account names display as raw SID strings. The script output mixes names and SIDs without indicating which entries are orphaned.

FileSystemRights as integers. Some right combinations display as integer values (e.g., 268435456) instead of named rights because they use flag combinations that PowerShell's enum doesn't fully map. Your CSV contains a mix of readable names like "FullControl" and cryptic integers.

Performance on large trees. Get-Acl called per-folder on a tree with 10,000 folders is slow. There's no progress indication — the script either runs silently for 30 minutes or you add progress output and the script gets more complex.

No risk analysis. The script outputs raw data. Identifying which entries are dangerous — Everyone with Write, orphaned SIDs, broken inheritance, service accounts with FullControl — requires additional logic you have to write, test, and maintain.

Each of these issues is solvable. Each solution adds 10-30 lines of code. By the time you've handled them all, your "simple permissions script" is 200+ lines and specific to your environment.


The SDDL readability problem

Even with the .Access property approach that avoids raw SDDL, you sometimes need to read SDDL directly — for example, when examining SDDL on scheduled tasks (as covered in the Scheduled Task Auditor documentation) or when the .Access property doesn't fully decode a complex permission set.

SDDL encodes: - Owner (O:) — which principal owns the object - Group (G:) — the primary group (rarely used on files) - DACL (D:) — the discretionary access control list (who can access) - SACL (S:) — the system access control list (auditing rules)

Each ACE within the DACL follows the format: (AceType;AceFlags;Rights;ObjectGuid;InheritObjectGuid;SID)

The SID field uses abbreviations: BA = Built-in Administrators, SY = SYSTEM, BU = Built-in Users, WD = Everyone (World), AU = Authenticated Users. Any non-abbreviated SID is a long alphanumeric string that needs to be resolved against Active Directory.

The Rights field uses hex flags: FA = Full Access, FR = File Read, FW = File Write. Combined rights appear as hex integers that require a lookup table to decode.

This format was designed for machine parsing, not human reading. Asking sysadmins to audit file permissions by reading SDDL is like asking them to audit firewall rules by reading iptables hex — technically complete but practically useless for a review process.


What a useful permissions report looks like

A permissions report that an admin can actually review and an auditor can actually accept has these properties:

One row per ACL entry, not per folder. A folder with 8 ACL entries produces 8 rows. This makes it possible to sort by principal ("show me everywhere this user has access") or by rights ("show me everywhere someone has FullControl").

Human-readable principals. DOMAIN\JSmith not S-1-5-21-1234567890-1234567890-1234567890-1234. Orphaned SIDs that can't be resolved should be flagged as orphaned, not silently displayed as raw strings.

Human-readable rights. "FullControl" not 268435456. "Read & Execute" not 0x1200a9.

Inherited vs explicit clearly separated. This is critical for understanding why a permission exists. An inherited entry comes from a parent folder and will change automatically if the parent's permissions change. An explicit entry was set on this specific folder and overrides inheritance.

Risk flags on dangerous entries. The report should tell you which entries represent risk, not just list everything and expect you to figure it out. Everyone with Write access should be flagged as CRITICAL. An orphaned SID should be flagged as MEDIUM. A clean inherited entry from the parent should have no flag.

Exportable. CSV format that opens in Excel, sorts on any column, and can be filtered to show specific principals, specific risk levels, or specific paths.


When to use PowerShell vs a dedicated tool

PowerShell is the right choice when you need a quick permissions check on a single folder or a small set of paths, when you're integrating permissions data into a larger automation workflow, or when you have a maintained, tested script that handles your environment's specific edge cases.

A dedicated permissions auditor is the right choice when you need to scan an entire file server with thousands of folders, when you need risk scoring that identifies dangerous entries automatically, when you need a structured evidence pack for compliance, or when multiple team members need to run audits without PowerShell expertise.

The trade-off is the same as with every "script it yourself vs use a tool" decision: the script is free in dollars and costs time. The tool costs dollars and saves time. For an audit you run quarterly on multiple file servers, the tool pays for itself on the first use.


What to do next

If your permissions reporting process involves Get-Acl scripts, SDDL decoding, or manual Security tab inspection, you're spending time on the enumeration that should be spent on the review.

NTFS Permissions Auditor walks the tree, decodes every ACL entry into human-readable output, resolves SIDs, separates inherited from explicit, applies risk scoring, and produces a timestamped evidence pack. No scripting, no SDDL, no edge cases to handle.

The trial scans 500 paths. Run it on a file share and compare the output to what your PowerShell script produces. The difference is in readability, risk analysis, and what you have to show for it.

Skip the PowerShell scripting

Get a clean permissions report with risk scoring. No SDDL, no XML, no scripting required.

Download Free Trial Learn More