Shore Up
A magnifying glass moving over a wall of stacked folders, some folders glowing red where too many keys hang on their hooks.
WindowsSecurity

Automate NTFS Permission Audits and Detect Oversharing on File Servers

Ketan Aagja9 min read
No ratings yet

Before you run this

This guide gives you a PowerShell script that walks a share tree, reads the NTFS access control list (ACL) on each folder, and reports every place where a broad identity — Everyone, Authenticated Users, Domain Users, BUILTIN\Users — has been granted Write, Modify, or Full Control. That combination is the classic definition of "oversharing," and this script's only job is to find it and write it to a CSV. It reads permissions. It does not change a single ACE.

A few things to be honest about before you run it:

  • Privileges. To read ACLs on every subfolder you generally need to be a local administrator on the file server (or at least hold read access, including bypass-traverse, everywhere the script walks). Run it from an elevated PowerShell session. It does not need Domain Admin, and it makes no changes, so there is nothing to undo on the file system.
  • It is read-only, but not free. A -Recurse walk of a large volume is real disk I/O and can take a long time. Run the first pass against a single test subtree, off-hours if the volume is busy, before you point it at an entire D:\Shares root.
  • Read the script first. Understand what it enumerates and where it writes the CSV. Don't paste-and-run against production storage blind.
  • No firewall or destructive action here, so there is no config to back up and no rollback to plan — the only artifact this creates is a CSV report you can delete. If you later schedule it (covered at the end), the only thing to remove is the scheduled task.

I'm assuming Windows Server 2019 or 2022, a domain-joined file server, and Windows PowerShell 5.1 (the version shipped in the box). Everything here uses built-in cmdlets — no external modules.

What "oversharing" means here, concretely

I'm flagging an access control entry (ACE) when both of these are true:

  1. The identity is one of the broad, everyone-in-the-building groups, and
  2. The rights include Write, Modify, or Full Control (i.e. the ability to change or delete data), not just Read/Execute.

Read-only access for Domain Users on a company-wide reference share is often fine and intentional. Write access for Everyone three folders deep in Finance almost never is. The script separates those cases by testing the rights, not just the identity.

The audit script

Save this as Audit-NtfsOversharing.ps1. Change the four values at the top to match your environment — they're the only things you edit.

# --- Settings you edit ---------------------------------------------------
$Root    = 'D:\Shares'                 # share root to audit
$Depth   = 3                           # folder levels below $Root to inspect
$OutFile = 'C:\Temp\ntfs-audit.csv'    # where the report is written

# Broad identities that usually indicate oversharing when they can write.
# Replace DOMAIN with your NetBIOS domain name.
$RiskyIdentities = @(
    'Everyone',
    'NT AUTHORITY\Authenticated Users',
    'BUILTIN\Users',
    'DOMAIN\Domain Users'
)
# -------------------------------------------------------------------------

# Rights that mean "can change or delete data". FileSystemRights is a
# [Flags] enum, so we test with a bitwise AND against this mask.
$WriteMask = [System.Security.AccessControl.FileSystemRights]'Write, Modify, FullControl'

# Build the list of paths: the root itself, plus subfolders to $Depth.
$paths = @($Root) + (Get-ChildItem -LiteralPath $Root -Directory -Recurse `
                        -Depth $Depth -ErrorAction SilentlyContinue).FullName

$results = foreach ($path in $paths) {
    try {
        $acl = Get-Acl -LiteralPath $path -ErrorAction Stop
    } catch {
        # Unreadable path (permissions, long path, reparse point) — record and move on
        [pscustomobject]@{
            Path = $path; Identity = '(could not read ACL)'; Rights = $_.Exception.Message
            Inherited = ''; AccessType = ''
        }
        continue
    }

    foreach ($ace in $acl.Access) {
        if ($ace.AccessControlType -ne 'Allow') { continue }   # ignore Deny entries

        $identity = $ace.IdentityReference.Value
        $isRisky  = $RiskyIdentities -contains $identity
        $canWrite = ($ace.FileSystemRights -band $WriteMask) -ne 0

        if ($isRisky -and $canWrite) {
            [pscustomobject]@{
                Path       = $path
                Identity   = $identity
                Rights     = $ace.FileSystemRights   # named rights, e.g. "Modify, Synchronize"
                Inherited  = $ace.IsInherited        # $true = came from a parent folder
                AccessType = $ace.AccessControlType
            }
        }
    }
}

$results | Sort-Object Path | Export-Csv -LiteralPath $OutFile -NoTypeInformation -Encoding UTF8
Write-Host "Wrote $($results.Count) flagged entries to $OutFile"

Run it elevated:

.\Audit-NtfsOversharing.ps1

Reading the output

Open the CSV in Excel or with Import-Csv. Each row is one risky ACE. The Inherited column matters: True means the permission is flowing down from a parent folder, so you fix it at the parent, not on that specific child. False means it was set explicitly on that folder — those are the ones that usually accumulate from ad-hoc "just give them access" requests.

Two things to know about the rights column

FileSystemRights is a flags enum, and I want you to trust the output, so here are its honest edges:

  • The bitwise test can over-count in one direction. Because Modify and Full Control include the Write bits, the -band against $WriteMask correctly catches all three. It will not flag plain ReadAndExecute or ListDirectory — those don't share bits with Write. That's the behaviour you want.
  • Occasionally the value shows as a large number instead of a name (e.g. -1610612736). That happens when an ACE was written with generic access rights that Windows maps at runtime. It's uncommon on normal file shares, but if you see numeric values in the Rights column, inspect those paths by hand with icacls "<path>" rather than trusting the label. For the exact FileSystemRights values and what they map to, see Microsoft Learn for System.Security.AccessControl.FileSystemRights.

If you'd rather work from the raw ACL text, icacls "D:\Shares" /T /C dumps the same information in a different format; it's the mainstream alternative and worth knowing, but the PowerShell object output is far easier to filter and diff.

Scheduling it as a recurring audit

Once the report looks right, run it weekly so oversharing gets caught as it appears. Use Task Scheduler with a service account that has read access to the tree:

# Run from an elevated prompt. Adjust the path and account.
$action  = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Audit-NtfsOversharing.ps1"'
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 2am

Register-ScheduledTask -TaskName 'NTFS Oversharing Audit' `
    -Action $action -Trigger $trigger -RunLevel Highest -User 'DOMAIN\svc-audit'

Register-ScheduledTask will prompt for the account's password. To catch new oversharing over time, have the task write to a dated filename (append Get-Date -Format 'yyyyMMdd' to $OutFile) and compare each week's CSV against the last with Compare-Object.

Verify it did what it should

  • Confirm the report exists and has content:

    Import-Csv 'C:\Temp\ntfs-audit.csv' | Format-Table -AutoSize
    
  • Spot-check one flagged path by hand, so you trust the whole file:

    (Get-Acl -LiteralPath 'D:\Shares\SomeFlaggedFolder').Access |
        Where-Object AccessControlType -eq 'Allow' |
        Format-Table IdentityReference, FileSystemRights, IsInherited -AutoSize
    

    The identity and rights you see here should match the row in the CSV.

  • Confirm the scheduled task registered:

    Get-ScheduledTask -TaskName 'NTFS Oversharing Audit'
    

Undo

The audit itself changes nothing, so there is nothing to reverse — delete the CSV if you don't want it. The only persistent artifact is the scheduled task; remove it with:

Unregister-ScheduledTask -TaskName 'NTFS Oversharing Audit' -Confirm:$false

Remediating the oversharing this report finds — pulling Everyone off a folder, breaking inheritance — is a separate, deliberate act. Do that one path at a time, with a record of the original ACL (Get-Acl | Export-Clixml before you touch it, Set-Acl to restore), and never in bulk from the audit output. Finding the problem and fixing it are two different jobs, and this script is only the first one.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.

Audit Windows Firewall Rules and Report Drift From a Baseline

This guide builds two small PowerShell scripts. The first captures the current Windows Firewall ruleset to a CSV baseline . The second re-reads the live rules and reports which ones were added, removed, or changed since that baseline. Both scripts are read-only — they use Get-* cmdlets only and change no firewall rules, so running the audit cannot break connectivity.

10 min read

Automate a Network Share Permissions Audit with PowerShell

This guide builds a read-only report . The script enumerates the SMB shares on a Windows file server, then lists two things for each one: the share-level permissions (the "who can connect" layer) and the NTFS permissions on the folder behind it (the "who can touch the files" layer). It writes both to CSV so you can review access in a spreadsheet instead of clicking through the Security tab share by share.

9 min read

Audit Local Administrators Across Many Windows Machines

This is a read-only audit. It pulls a list of computers from Active Directory, connects to each one over PowerShell Remoting (WinRM), reads the membership of the local Administrators group, and writes everything to a single CSV you can open in Excel. It creates nothing and changes nothing on the target machines, so there is no destructive step and nothing to roll back — the only thing produced is the report file on your own workstation.

9 min read