Public/07_Printing_Shares/Get-VBSharePermission.ps1

# ============================================================
# FUNCTION : Get-VBSmbSharePermission
# MODULE : VBTools
# VERSION : 1.0.2
# CHANGED : 29-05-2026 -- Initial release
# AUTHOR : Vibhu
# PURPOSE : Returns SMB share-level and NTFS top-level ACEs as objects
# ENCODING : UTF-8 with BOM -- do not re-save without BOM
# ============================================================

function Get-VBSmbSharePermission {
<#
.SYNOPSIS
    Returns SMB share-level and NTFS top-level ACEs for non-administrative
    file shares on the local machine as structured objects.
 
.DESCRIPTION
    Queries Get-SmbShare and filters out administrative shares (ADMIN$, IPC$,
    drive-letter shares, SYSVOL, NETLOGON, PRINT$) and system paths under
    C:\Windows. For every qualifying share, two rows are emitted per ACE --
    one for share-level permissions and one for NTFS top-level permissions.
 
    Output is pipeline-friendly: pipe to Export-VBSmbSharePermissionReport
    for a grouped text report, or to Export-Csv for spreadsheet analysis.
 
    Errors on individual shares are captured per-row and do not stop
    processing of remaining shares.
 
.PARAMETER ShareName
    One or more share names to query. Accepts pipeline input by value and
    by property name. Defaults to all qualifying shares when omitted.
 
.EXAMPLE
    Get-VBSmbSharePermission
 
    Returns all share and NTFS ACEs for every non-administrative SMB share
    on the local machine.
 
.EXAMPLE
    Get-VBSmbSharePermission -ShareName 'Data', 'Finance'
 
    Returns ACEs only for the shares named Data and Finance.
 
.EXAMPLE
    Get-VBSmbSharePermission | Export-Csv -Path C:\Reports\SharePerms.csv -NoTypeInformation -Encoding UTF8
 
    Exports all ACEs to a CSV file for further analysis.
 
.EXAMPLE
    Get-VBSmbSharePermission | Where-Object { $_.PermissionLayer -eq 'NTFS' -and -not $_.IsInherited }
 
    Filters to explicit (non-inherited) NTFS ACEs only.
 
.EXAMPLE
    $OutputPath = Join-Path 'C:\Realtime' "SharePermissions_$(Get-Date -Format 'yyyyMMdd').txt"
    Get-VBSmbSharePermission | Export-VBSmbSharePermissionReport -OutputPath $OutputPath
 
    Produces a grouped, human-readable text report of all share permissions.
 
.OUTPUTS
    [PSCustomObject]
 
    Each object represents a single ACE with the following properties:
 
        ComputerName [string] -- Machine the share resides on
        Status [string] -- 'Success' or 'Failed'
        ShareName [string] -- SMB share name
        SharePath [string] -- Local file system path
        ShareDescription [string] -- Share description (may be empty)
        PermissionLayer [string] -- 'Share' or 'NTFS'
        Identity [string] -- Account or group name
        AccessControlType [string] -- 'Allow' or 'Deny'
        Rights [string] -- Permission level or FileSystemRights value
        IsInherited [bool] -- True if ACE is inherited; $null for Share-layer rows
        Error [string] -- $null on success; exception message on failure
        CollectionTime [string] -- Timestamp in dd-MM-yyyy HH:mm:ss format
 
.NOTES
    Version : 1.0.0
    Author : Vibhu
    Modified : 29-05-2026
    Category : Windows Server Administration
 
    This function is local-only and does not use WinRM or remoting.
    Run directly on the target machine.
 
    Requires the SmbShare module (available by default on Windows Server 2012+
    and Windows 8+).
#>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$ShareName
    )

    begin {
        $CollectedNames = [System.Collections.Generic.List[string]]::new()
    }

    process {
        if ($ShareName) {
            foreach ($name in $ShareName) { $CollectedNames.Add($name) }
        }
    }

    end {
        # Step 1 -- Resolve qualifying shares
        Write-Verbose '[Get-VBSmbSharePermission] Querying SMB shares'

        $allShares = Get-SmbShare | Where-Object {
            $_.ShareType -eq 'FileSystemDirectory' -and
            $_.Path -and
            $_.Path -notmatch '^C:\\($|Windows)' -and
            $_.Name -notmatch '^(ADMIN\$|IPC\$|PRINT\$|SYSVOL|NETLOGON|[A-Z]\$)$'
        }

        if ($CollectedNames.Count -gt 0) {
            $allShares = $allShares | Where-Object { $CollectedNames -contains $_.Name }
        }

        if ($null -eq $allShares -or @($allShares).Count -eq 0) {
            Write-Warning '[Get-VBSmbSharePermission] No qualifying shares found'
            return
        }

        $collectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss')

        # Step 2 -- Emit one object per ACE for each share
        foreach ($share in $allShares) {
            Write-Verbose "[Get-VBSmbSharePermission] Processing share: $($share.Name)"

            # Share-level ACEs
            try {
                $smbAces = Get-SmbShareAccess -Name $share.Name
                if ($null -eq $smbAces -or @($smbAces).Count -eq 0) {
                    Write-Warning "[Get-VBSmbSharePermission] No share-level ACEs found for: $($share.Name)"
                } else {
                    foreach ($ace in $smbAces) {
                        [PSCustomObject]@{
                            ComputerName      = $env:COMPUTERNAME
                            Status            = 'Success'
                            ShareName         = $share.Name
                            SharePath         = $share.Path
                            ShareDescription  = $share.Description
                            PermissionLayer   = 'Share'
                            Identity          = $ace.AccountName
                            AccessControlType = $ace.AccessControlType.ToString()
                            Rights            = $ace.AccessRight.ToString()
                            IsInherited       = $null
                            Error             = $null
                            CollectionTime    = $collectionTime
                        }
                    }
                }
            }
            catch {
                Write-Warning "[Get-VBSmbSharePermission] Share-level ACL query failed for: $($share.Name)"
                [PSCustomObject]@{
                    ComputerName      = $env:COMPUTERNAME
                    Status            = 'Failed'
                    ShareName         = $share.Name
                    SharePath         = $share.Path
                    ShareDescription  = $share.Description
                    PermissionLayer   = 'Share'
                    Identity          = $null
                    AccessControlType = $null
                    Rights            = $null
                    IsInherited       = $null
                    Error             = $_.Exception.Message
                    CollectionTime    = $collectionTime
                }
            }

            # NTFS top-level ACEs
            try {
                $ntfsAces = (Get-Acl -Path $share.Path).Access
                if ($null -eq $ntfsAces -or @($ntfsAces).Count -eq 0) {
                    Write-Warning "[Get-VBSmbSharePermission] No NTFS ACEs found at path: $($share.Path)"
                } else {
                    foreach ($ace in $ntfsAces) {
                        [PSCustomObject]@{
                            ComputerName      = $env:COMPUTERNAME
                            Status            = 'Success'
                            ShareName         = $share.Name
                            SharePath         = $share.Path
                            ShareDescription  = $share.Description
                            PermissionLayer   = 'NTFS'
                            Identity          = $ace.IdentityReference.ToString()
                            AccessControlType = $ace.AccessControlType.ToString()
                            Rights            = $ace.FileSystemRights.ToString()
                            IsInherited       = $ace.IsInherited
                            Error             = $null
                            CollectionTime    = $collectionTime
                        }
                    }
                }
            }
            catch {
                Write-Warning "[Get-VBSmbSharePermission] NTFS ACL query failed for path: $($share.Path)"
                [PSCustomObject]@{
                    ComputerName      = $env:COMPUTERNAME
                    Status            = 'Failed'
                    ShareName         = $share.Name
                    SharePath         = $share.Path
                    ShareDescription  = $share.Description
                    PermissionLayer   = 'NTFS'
                    Identity          = $null
                    AccessControlType = $null
                    Rights            = $null
                    IsInherited       = $null
                    Error             = $_.Exception.Message
                    CollectionTime    = $collectionTime
                }
            }
        }
    }
}


# ============================================================
# FUNCTION : Export-VBSmbSharePermissionReport
# MODULE : VBTools
# VERSION : 1.0.0
# CHANGED : 29-05-2026 -- Initial release
# AUTHOR : Vibhu
# PURPOSE : Writes Get-VBSmbSharePermission output to a grouped text report
# ENCODING : UTF-8 with BOM -- do not re-save without BOM
# ============================================================

function Export-VBSmbSharePermissionReport {
<#
.SYNOPSIS
    Writes output from Get-VBSmbSharePermission to a grouped, human-readable
    text report file.
 
.DESCRIPTION
    Consumes PSCustomObject rows from Get-VBSmbSharePermission and produces a
    text file grouped by share. Each share section shows share-level ACEs
    followed by NTFS top-level ACEs. Errors are surfaced inline under the
    relevant layer heading.
 
    This function buffers all pipeline input before writing -- required because
    grouping by share name is intrinsically blocking.
 
.PARAMETER Data
    Output from Get-VBSmbSharePermission. Accepts pipeline input.
 
.PARAMETER OutputPath
    Full path for the output text file. Parent directory must already exist.
 
.EXAMPLE
    Get-VBSmbSharePermission | Export-VBSmbSharePermissionReport -OutputPath 'C:\Reports\SharePerms.txt'
 
    Runs the query and writes a grouped report to the specified path.
 
.EXAMPLE
    $OutputPath = Join-Path 'C:\Realtime' "DSI-DH01-DC-003_SharePermissions_$(Get-Date -Format 'yyyyMMdd').txt"
    Get-VBSmbSharePermission | Export-VBSmbSharePermissionReport -OutputPath $OutputPath
    Write-Host "Saved to: $OutputPath"
 
    Writes the report with a date-stamped filename matching the original script convention.
 
.EXAMPLE
    $data = Get-VBSmbSharePermission
    $data | Export-VBSmbSharePermissionReport -OutputPath 'C:\Reports\SharePerms.txt'
    $data | Export-Csv -Path 'C:\Reports\SharePerms.csv' -NoTypeInformation -Encoding UTF8
 
    Reuse the collected objects for both a readable report and a CSV export.
 
.OUTPUTS
    None. This function writes a file and does not return objects to the pipeline.
 
.NOTES
    Version : 1.0.0
    Author : Vibhu
    Modified : 29-05-2026
    Category : Windows Server Administration
 
    Output file is written as UTF-8. Parent directory must exist before calling.
    This function intentionally buffers all input -- grouping requires it.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSCustomObject[]]$Data,

        [Parameter(Mandatory = $true)]
        [string]$OutputPath
    )

    begin {
        $all = [System.Collections.Generic.List[PSCustomObject]]::new()
    }

    process {
        foreach ($row in $Data) { $all.Add($row) }
    }

    end {
        if ($all.Count -eq 0) {
            Write-Warning '[Export-VBSmbSharePermissionReport] No data received -- report not written'
            return
        }

        Write-Verbose "[Export-VBSmbSharePermissionReport] Building report for $($all.Count) ACE rows"

        $lines  = [System.Collections.Generic.List[string]]::new()
        $server = ($all | Select-Object -First 1).ComputerName
        $ts     = ($all | Select-Object -First 1).CollectionTime

        $lines.Add("Share Permission Report")
        $lines.Add("Server : $server")
        $lines.Add("Generated : $ts")
        $lines.Add("Total ACE rows : $($all.Count)")
        $lines.Add("=" * 60)

        $shareNames = $all | Select-Object -ExpandProperty ShareName -Unique

        foreach ($shareName in $shareNames) {
            $shareRows = $all | Where-Object { $_.ShareName -eq $shareName }
            $first     = $shareRows | Select-Object -First 1

            $lines.Add("")
            $lines.Add("=== Share: $shareName ===")
            $lines.Add("Path : $($first.SharePath)")
            $lines.Add("Description : $($first.ShareDescription)")

            # Share-level ACEs
            $lines.Add("")
            $lines.Add("-- Share-Level Permissions --")
            $shareAces = $shareRows | Where-Object { $_.PermissionLayer -eq 'Share' }

            if (@($shareAces).Count -eq 0) {
                $lines.Add(" (none)")
            } else {
                foreach ($ace in $shareAces) {
                    if ($ace.Error) {
                        $lines.Add(" [ERROR] $($ace.Error)")
                    } else {
                        $lines.Add(" $($ace.Identity) | $($ace.AccessControlType) | $($ace.Rights)")
                    }
                }
            }

            # NTFS ACEs
            $lines.Add("")
            $lines.Add("-- NTFS Permissions (top level) --")
            $ntfsAces = $shareRows | Where-Object { $_.PermissionLayer -eq 'NTFS' }

            if (@($ntfsAces).Count -eq 0) {
                $lines.Add(" (none)")
            } else {
                foreach ($ace in $ntfsAces) {
                    if ($ace.Error) {
                        $lines.Add(" [ERROR] $($ace.Error)")
                    } else {
                        $inherited = if ($ace.IsInherited) { 'Inherited' } else { 'Explicit' }
                        $lines.Add(" $($ace.Identity) | $($ace.AccessControlType) | $($ace.Rights) | $inherited")
                    }
                }
            }
        }

        $lines | Set-Content -Path $OutputPath -Encoding UTF8
        Write-Verbose "[Export-VBSmbSharePermissionReport] Report written to: $OutputPath"
    }
}