- rename host/group/folder everywhere to match the server's actual hostname and physical label - share-analysis.ps1: sanitize the output filename prefix — '-Paths "D:"' produced 'D:-file-types.csv' and Export-Csv failed with 'path format not supported', so no CSVs were written - share-analysis.ps1: new -FolderDepth so folders can be aggregated at D:\Abteilungen\<Share> level, which matches the share layout - list-shares.ps1: -WithSize walks local paths instead of UNC when run on the server itself (UNC was orders of magnitude slower and looked stuck) and prints progress every 50k files Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
128 lines
4.9 KiB
PowerShell
128 lines
4.9 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Lists all SMB shares of a Windows file server incl. local path, size and
|
|
permissions — the basis for the include/exclude decision (projektplan §2.6).
|
|
|
|
.DESCRIPTION
|
|
Run ON the file server (or against a remote one via -ComputerName). Reports
|
|
per share: name, local path, description, share-level permissions, NTFS
|
|
groups, plus optional size/file count. Admin shares (C$, ADMIN$, IPC$) are
|
|
skipped unless -IncludeAdminShares is given.
|
|
|
|
Purely read-only. Requires local admin for Get-SmbShare (part of Windows
|
|
Server, no extra module needed).
|
|
|
|
.EXAMPLE
|
|
.\list-shares.ps1
|
|
|
|
.EXAMPLE
|
|
# with size per share — run this ON the file server, it then walks the local
|
|
# paths instead of the UNC paths (far faster). Still minutes to hours on
|
|
# millions of files; progress is printed every 50k files.
|
|
.\list-shares.ps1 -WithSize
|
|
|
|
.NOTES
|
|
Faster alternative when all shares live under one drive (as on Z-FILESERVER,
|
|
where everything sits under D:): run share-analysis.ps1 once with a matching
|
|
folder depth instead of measuring every share separately, e.g.
|
|
|
|
.\share-analysis.ps1 -Paths "D:" -FolderDepth 2
|
|
|
|
That produces size/count per D:\Abteilungen\<Share> in a single pass over
|
|
the disk, which is what the share list here maps onto.
|
|
#>
|
|
param(
|
|
[string]$ComputerName = $env:COMPUTERNAME,
|
|
[switch]$IncludeAdminShares,
|
|
[switch]$WithSize,
|
|
[string]$OutDir = (Join-Path (Get-Location) "share-list")
|
|
)
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
|
|
|
|
Write-Host "=== SMB shares on $ComputerName ===" -ForegroundColor Cyan
|
|
|
|
$shares = Get-SmbShare -CimSession $ComputerName -ErrorAction Stop
|
|
if (-not $IncludeAdminShares) {
|
|
$shares = $shares | Where-Object { -not $_.Name.EndsWith('$') }
|
|
}
|
|
|
|
$result = foreach ($sh in $shares) {
|
|
Write-Host (" {0,-25} {1}" -f $sh.Name, $sh.Path)
|
|
|
|
# share-level permissions
|
|
$sharePerms = try {
|
|
(Get-SmbShareAccess -Name $sh.Name -CimSession $ComputerName -ErrorAction Stop |
|
|
ForEach-Object { "$($_.AccountName)=$($_.AccessRight)" }) -join '; '
|
|
} catch { 'n/a' }
|
|
|
|
# NTFS permissions (top level only) — who actually has access
|
|
$ntfsPerms = try {
|
|
$uncPath = "\\$ComputerName\$($sh.Name)"
|
|
((Get-Acl -LiteralPath $uncPath -ErrorAction Stop).Access |
|
|
ForEach-Object { "$($_.IdentityReference)=$($_.FileSystemRights)" } |
|
|
Select-Object -Unique | Select-Object -First 10) -join '; '
|
|
} catch { 'n/a' }
|
|
|
|
$sizeGB = $null; $fileCount = $null
|
|
if ($WithSize) {
|
|
# Walk the LOCAL path when we are on the server itself — going through
|
|
# the UNC path (\\server\share) routes every single file through the SMB
|
|
# stack and is slower by orders of magnitude on large shares.
|
|
$scanPath = if ($ComputerName -eq $env:COMPUTERNAME -and $sh.Path) {
|
|
$sh.Path
|
|
} else {
|
|
"\\$ComputerName\$($sh.Name)"
|
|
}
|
|
|
|
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
|
$n = 0L; $bytes = 0L
|
|
Get-ChildItem -LiteralPath $scanPath -Recurse -File -Force -ErrorAction SilentlyContinue |
|
|
ForEach-Object {
|
|
$n++; $bytes += $_.Length
|
|
if ($n % 50000 -eq 0) {
|
|
Write-Host (" ... {0:N0} files, {1:N1} GB ({2:N0}s)" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
|
|
}
|
|
}
|
|
$sw.Stop()
|
|
$sizeGB = [Math]::Round($bytes / 1GB, 2)
|
|
$fileCount = $n
|
|
Write-Host (" {0:N0} files, {1:N1} GB in {2:N0}s" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
|
|
}
|
|
|
|
[PSCustomObject]@{
|
|
Name = $sh.Name
|
|
Path = $sh.Path
|
|
Description = $sh.Description
|
|
SharePermissions = $sharePerms
|
|
NtfsPermissions = $ntfsPerms
|
|
SizeGB = $sizeGB
|
|
FileCount = $fileCount
|
|
}
|
|
}
|
|
|
|
$csv = Join-Path $OutDir "smb-shares.csv"
|
|
$result | Export-Csv $csv -NoTypeInformation -Encoding UTF8
|
|
$result | Format-Table Name, Path, SizeGB, FileCount -AutoSize
|
|
|
|
# DFS namespaces (if used) — often the path users actually see
|
|
Write-Host "`n=== DFS namespaces ===" -ForegroundColor Cyan
|
|
try {
|
|
$roots = Get-DfsnRoot -ErrorAction Stop
|
|
if ($roots) {
|
|
$dfs = foreach ($r in $roots) {
|
|
Get-DfsnFolder -Path "$($r.Path)\*" -ErrorAction SilentlyContinue | ForEach-Object {
|
|
$t = Get-DfsnFolderTarget -Path $_.Path -ErrorAction SilentlyContinue
|
|
[PSCustomObject]@{ DfsPath = $_.Path; Targets = ($t.TargetPath -join '; ') }
|
|
}
|
|
}
|
|
$dfs | Export-Csv (Join-Path $OutDir "dfs-namespaces.csv") -NoTypeInformation -Encoding UTF8
|
|
$dfs | Format-Table -AutoSize
|
|
} else { Write-Host " none" }
|
|
} catch {
|
|
Write-Host " no DFS role / not available on this host"
|
|
}
|
|
|
|
Write-Host "`nResults in: $OutDir" -ForegroundColor Green
|