Shore Up
An inspector walking a long corridor of labelled storage lockers, clipboard in hand, ticking off a list of which keys open which locker — nothing is being opened or changed, only recorded.
WindowsSecurity

Automate a Network Share Permissions Audit with PowerShell

Ketan Aagja9 min read
No ratings yet

Before you run this

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.

It does not change anything. It creates no shares, grants no rights, deletes nothing. There is no undo section at the end because there is nothing to undo — the only thing written is a CSV file you choose the path for.

That said, treat it with normal care:

  • It needs an elevated session. Get-SmbShare and Get-SmbShareAccess return data on a stock server, but to see every share and every ACL reliably you want to run this in a PowerShell console started as Administrator, ideally logged in as an account with local admin on the file server. Reading NTFS ACLs on folders your account can't traverse will otherwise throw access-denied errors and leave gaps in the report.
  • Read the script before you run it, and run it once against a test share on a non-production server so you can see the shape of the output before you point it at a live file server with hundreds of ACEs.
  • The SmbShare module used here ships with Windows Server 2012 and later and with Windows 8/10/11. If you are on something older, these cmdlets won't be present.

There is no firewall, routing, or console-lockout risk here — this is a local query, not a config change — so the usual "keep an out-of-band connection open" warning doesn't apply. Just don't confuse this read-only audit script with the ones that set permissions; those deserve far more caution.

What I'm assuming

  • A domain-joined Windows Server 2019 or 2022 file server.
  • Windows PowerShell 5.1 (the version in the box). The script also runs on PowerShell 7, but 5.1 is what you'll have by default on a server.
  • You're running it locally on the file server, in an elevated console. Running against a remote server is covered briefly at the end.
  • An output folder exists — I use C:\Audit\ in the examples. Change it to whatever you like; just make sure the folder exists first.

Step 1: List the shares you care about

Start by seeing what's actually shared. This one line lists everything and lets you eyeball it:

Get-SmbShare

You'll see the administrative shares too — C$, ADMIN$, IPC$. Those are hidden shares Windows creates itself; you usually don't want them in a permissions audit. The convention is that their names end in $, so filter them out:

# Grab every non-administrative share (skip C$, ADMIN$, IPC$, etc.)
$shares = Get-SmbShare | Where-Object { $_.Name -notlike '*$' }
$shares | Select-Object Name, Path, Description

Everything below works off that $shares variable.

Step 2: Report the share-level permissions

Share-level permissions are the first gate: they cap what a user can do over the network, regardless of NTFS. Get-SmbShareAccess returns them per share — an account, an allow/deny, and a right of Full, Change, or Read.

$shareReport = foreach ($share in $shares) {
    Get-SmbShareAccess -Name $share.Name | ForEach-Object {
        [PSCustomObject]@{
            ShareName   = $_.Name
            Path        = $share.Path
            Account     = $_.AccountName
            AccessType  = $_.AccessControlType   # Allow or Deny
            AccessRight = $_.AccessRight          # Full / Change / Read
        }
    }
}

# Replace the path with your own audit folder
$shareReport | Export-Csv -Path 'C:\Audit\ShareLevel.csv' -NoTypeInformation -Encoding UTF8

Many shops leave share permissions at the default (Everyone / Full Control) and control access entirely through NTFS. That's a valid design, but the audit should still record it — a Full for Everyone at the share layer means NTFS is doing all the work, and that's worth knowing.

Step 3: Report the NTFS permissions behind each share

The NTFS ACL on the folder is where real access lives. Get-Acl returns it, and each entry in .Access is a rule with an identity, a set of rights, allow/deny, and whether it was inherited.

I wrap the Get-Acl call in a try/catch so one unreadable path doesn't kill the whole run — it records the failure and moves on:

$ntfsReport = foreach ($share in $shares) {
    try {
        $acl = Get-Acl -Path $share.Path -ErrorAction Stop
        foreach ($ace in $acl.Access) {
            [PSCustomObject]@{
                ShareName  = $share.Name
                Path       = $share.Path
                Account    = $ace.IdentityReference
                Rights     = $ace.FileSystemRights
                AccessType = $ace.AccessControlType   # Allow or Deny
                Inherited  = $ace.IsInherited
            }
        }
    }
    catch {
        # Record the share we couldn't read rather than silently skipping it
        [PSCustomObject]@{
            ShareName  = $share.Name
            Path       = $share.Path
            Account    = 'ERROR READING ACL'
            Rights     = $_.Exception.Message
            AccessType = ''
            Inherited  = ''
        }
    }
}

# Replace the path with your own audit folder
$ntfsReport | Export-Csv -Path 'C:\Audit\NtfsLevel.csv' -NoTypeInformation -Encoding UTF8

A couple of things to read carefully in the output:

  • FileSystemRights sometimes shows a plain word like Modify and sometimes a long numeric value or a comma list. That's normal — it reflects how the ACE was built. If you see a bare number, it's a combination of flags that didn't map to a friendly name; PowerShell isn't lying to you, it just can't name that exact bitmask.
  • Inherited = False entries are the ones set directly on this folder. Those are usually the interesting ones in an audit — deliberately broken inheritance is where surprises hide.

Step 4: A single combined report (optional)

If you'd rather have one file, keep the two CSVs but also drop a quick on-screen summary so you can spot the obvious problems — Everyone or Authenticated Users with broad rights:

$ntfsReport |
    Where-Object { $_.Account -match 'Everyone|Authenticated Users' } |
    Format-Table ShareName, Account, Rights, AccessType -AutoSize

This doesn't decide anything for you — it just surfaces the entries most worth a second look.

Running it against a remote file server

The clean, supported way to hit another server is to run the same code inside Invoke-Command, so both the SMB cmdlets and Get-Acl execute on that server against its local paths:

Invoke-Command -ComputerName FILESERVER01 -ScriptBlock {
    Get-SmbShare | Where-Object { $_.Name -notlike '*$' } |
        ForEach-Object { Get-SmbShareAccess -Name $_.Name }
}

You need PowerShell Remoting (WinRM) enabled and the right to administer that box. Mixing local Get-Acl with a remote UNC path can behave differently because of how Windows evaluates access remotely, so remoting into the server and reading its local paths is the reliable pattern. Check Microsoft Learn for Invoke-Command and for Get-SmbShareAccess if you want the full parameter set.

Verify it worked

The report is only useful if you trust it. Confirm it two ways:

  1. Open the CSVs. C:\Audit\ShareLevel.csv and C:\Audit\NtfsLevel.csv should have one row per permission entry, no empty account columns except any you flagged as ERROR READING ACL. If a whole share is missing, check that your account can read its path.
  2. Spot-check against the GUI. Pick one share. Open fsmgmt.msc (Shared Folders) and compare its Share Permissions tab to your ShareLevel.csv rows. Then right-click the folder in Explorer → Properties → Security and compare to NtfsLevel.csv. They should match line for line. If they do, the whole report is trustworthy.

Because nothing was modified, there's no rollback — delete the CSVs when you're done, or keep them as a dated baseline. Re-running this monthly and diffing the CSVs is a cheap way to catch permission drift before it becomes an incident.

For the underlying cmdlets, see Microsoft Learn for Get-SmbShare, Get-SmbShareAccess, and Get-Acl — each page lists every property these objects return, which is handy when you want to add columns to the report.

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.