Public/healthcheck/Get-FileServerHealth.ps1
|
#Requires -Version 5.1 function Get-FileServerHealth { <# .SYNOPSIS Retrieves file server health metrics from local or remote servers .DESCRIPTION Collects comprehensive file server health data including the LanmanServer service status, non-administrative share count, open SMB sessions and files, FSRM quota statistics, and minimum free disk space across shared drives. Returns a single typed object per server with an overall health assessment. .PARAMETER ComputerName One or more computer names to query. Defaults to the local computer. Accepts pipeline input by value and by property name. .PARAMETER Credential Optional PSCredential for authenticating to remote computers. Not used for local queries. .EXAMPLE Get-FileServerHealth Queries the local file server and returns health metrics. .EXAMPLE Get-FileServerHealth -ComputerName 'FS01' Queries the remote file server FS01 and returns its health metrics. .EXAMPLE 'FS01', 'FS02' | Get-FileServerHealth -Credential (Get-Credential) Queries multiple remote file servers via pipeline with explicit credentials. .OUTPUTS PSWinOps.FileServerHealth Returns one object per server containing service status, share counts, SMB sessions, FSRM quotas, disk space, and overall health assessment. .NOTES Author: Franck SALLET Version: 1.0.0 Last Modified: 2026-03-26 Requires: PowerShell 5.1+ / Windows only Requires: Run As Administrator for SMB and FSRM cmdlets .LINK https://github.com/k9fr4n/PSWinOps .LINK https://learn.microsoft.com/en-us/powershell/module/smbshare/ #> [CmdletBinding()] [OutputType('PSWinOps.FileServerHealth')] param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('CN', 'Name', 'DNSHostName')] [string[]]$ComputerName = $env:COMPUTERNAME, [Parameter(Mandatory = $false)] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Starting" $localNames = @($env:COMPUTERNAME, 'localhost', '.') $scriptBlock = { $svcStatus = 'Unknown' $roleAvailable = $true $totalShares = 0 $openSessions = 0 $openFiles = 0 $fsrmAvailable = $false $totalQuotas = 0 $quotasNearLimit = 0 $minShareDiskFreeGB = $null try { $svc = Get-Service -Name 'LanmanServer' -ErrorAction Stop $svcStatus = $svc.Status.ToString() } catch { $roleAvailable = $false } if ($roleAvailable) { $userShareList = @() try { $allShares = @(Get-SmbShare -ErrorAction Stop) $userShareList = @($allShares | Where-Object -FilterScript { -not $_.Special }) $totalShares = $userShareList.Count } catch { Write-Verbose -Message "SMB share enumeration failed: $_" } try { $openSessions = @(Get-SmbSession -ErrorAction Stop).Count } catch { Write-Verbose -Message "SMB session query failed: $_" } try { $openFiles = @(Get-SmbOpenFile -ErrorAction Stop).Count } catch { Write-Verbose -Message "SMB open file query failed: $_" } $fsrmModule = Get-Module -Name 'FileServerResourceManager' -ListAvailable -ErrorAction SilentlyContinue if ($fsrmModule) { $fsrmAvailable = $true try { Import-Module -Name 'FileServerResourceManager' -ErrorAction Stop $quotaList = @(Get-FsrmQuota -ErrorAction Stop) $totalQuotas = $quotaList.Count foreach ($quota in $quotaList) { if ($quota.Size -gt 0 -and ($quota.Usage / $quota.Size) -gt 0.9) { $quotasNearLimit++ } } } catch { Write-Verbose -Message "FSRM quota query failed: $_" } } if ($userShareList.Count -gt 0) { $driveIndex = @{} foreach ($share in $userShareList) { $sharePath = $share.Path if ($sharePath.Length -ge 2 -and $sharePath.Substring(1, 1) -eq ':') { $driveLetter = $sharePath.Substring(0, 1).ToUpper() $driveIndex[$driveLetter] = $true } } if ($driveIndex.Count -gt 0) { try { $filterParts = foreach ($dl in $driveIndex.Keys) { "DeviceID='${dl}:'" } $wmiFilter = $filterParts -join ' OR ' $diskList = @(Get-CimInstance -ClassName 'Win32_LogicalDisk' -Filter $wmiFilter -ErrorAction Stop) $lowestFree = $null foreach ($disk in $diskList) { $freeGB = [decimal]([math]::Round(($disk.FreeSpace / 1GB), 2)) if ($null -eq $lowestFree -or $freeGB -lt $lowestFree) { $lowestFree = $freeGB } } $minShareDiskFreeGB = $lowestFree } catch { Write-Verbose -Message "Disk space query failed: $_" } } } } @{ ServiceStatus = $svcStatus RoleAvailable = $roleAvailable TotalShares = $totalShares OpenSessions = $openSessions OpenFiles = $openFiles FSRMAvailable = $fsrmAvailable TotalQuotas = $totalQuotas QuotasNearLimit = $quotasNearLimit MinShareDiskFreeGB = $minShareDiskFreeGB } } } process { foreach ($machine in $ComputerName) { $displayName = $machine.ToUpper() Write-Verbose -Message "[$($MyInvocation.MyCommand)] Querying '${machine}'" try { $isLocal = $localNames -contains $machine if ($isLocal) { $data = & $scriptBlock } else { $invokeParams = @{ ComputerName = $machine ScriptBlock = $scriptBlock ErrorAction = 'Stop' } if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $invokeParams['Credential'] = $Credential } $data = Invoke-Command @invokeParams } $overallHealth = if (-not $data.RoleAvailable) { 'RoleUnavailable' } elseif ($data.ServiceStatus -ne 'Running' -or ($null -ne $data.MinShareDiskFreeGB -and $data.MinShareDiskFreeGB -lt 5)) { 'Critical' } elseif ($data.QuotasNearLimit -gt 0 -or ($null -ne $data.MinShareDiskFreeGB -and $data.MinShareDiskFreeGB -lt 20)) { 'Degraded' } else { 'Healthy' } [PSCustomObject]@{ PSTypeName = 'PSWinOps.FileServerHealth' ComputerName = $displayName ServiceName = 'LanmanServer' ServiceStatus = [string]$data.ServiceStatus TotalShares = [int]$data.TotalShares OpenSessions = [int]$data.OpenSessions OpenFiles = [int]$data.OpenFiles FSRMAvailable = [bool]$data.FSRMAvailable TotalQuotas = [int]$data.TotalQuotas QuotasNearLimit = [int]$data.QuotasNearLimit MinShareDiskFreeGB = if ($null -ne $data.MinShareDiskFreeGB) { [decimal]$data.MinShareDiskFreeGB } else { $null } OverallHealth = $overallHealth Timestamp = Get-Date -Format 'o' } } catch { Write-Error -Message "[$($MyInvocation.MyCommand)] Failed on '${machine}': $_" continue } } } end { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Completed" } } |