Last active
August 25, 2026 21:13
-
-
Save robert-niestroj/1448b6d1cc0736625ee676ceae864960 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Diagnoses common Windows 11 SMB / network-sharing problems and prints a | |
| color-coded TUI report with pass/fail checks and recommendations. | |
| .DESCRIPTION | |
| Run this on the PC that is SHARING the folder (e.g. DELL-MICRO). | |
| Optionally run it again on the CLIENT PC that gets the error, using | |
| -TargetComputer to also test connectivity to the host. | |
| .PARAMETER TargetComputer | |
| Name or IP of the other PC. When run on the client, this tests | |
| reachability/port 445 to the sharing PC. When run on the host, this | |
| tests reachability to the client (mostly informational). | |
| .PARAMETER ShareName | |
| Optional. Limit the share/permissions check to one specific share name | |
| instead of listing all shares. | |
| .EXAMPLE | |
| # On DELL-MICRO (the PC sharing the folder), run elevated: | |
| .\Test-NetworkSharing.ps1 -TargetComputer "LIVINGROOM-PC" | |
| .EXAMPLE | |
| # On the client PC that can't open the share: | |
| .\Test-NetworkSharing.ps1 -TargetComputer "DELL-MICRO" | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [string]$TargetComputer, | |
| [string]$ShareName | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Setup | |
| # --------------------------------------------------------------------------- | |
| $Host.UI.RawUI.WindowTitle = "Network Sharing Diagnostics - $env:COMPUTERNAME" | |
| $script:Findings = New-Object System.Collections.Generic.List[object] | |
| function Add-Finding { | |
| param([ValidateSet('OK','WARN','FAIL','INFO')] [string]$Status, [string]$Message, [string]$Fix = "") | |
| $script:Findings.Add([pscustomobject]@{ Status = $Status; Message = $Message; Fix = $Fix }) | |
| } | |
| function Write-Banner { | |
| param([string]$Text) | |
| $w = 78 | |
| $line = ('=' * $w) | |
| Write-Host "" | |
| Write-Host $line -ForegroundColor Cyan | |
| $pad = [Math]::Max(0, [int](($w - $Text.Length) / 2)) | |
| Write-Host ((' ' * $pad) + $Text) -ForegroundColor Cyan | |
| Write-Host $line -ForegroundColor Cyan | |
| } | |
| function Write-Section { | |
| param([string]$Title) | |
| Write-Host "" | |
| Write-Host " >> $Title" -ForegroundColor Yellow | |
| Write-Host (" " + ('-' * 76)) -ForegroundColor DarkGray | |
| } | |
| function Write-Check { | |
| param( | |
| [ValidateSet('OK','WARN','FAIL','INFO')] [string]$Status, | |
| [string]$Message, | |
| [string]$Detail = "", | |
| [string]$Fix = "" | |
| ) | |
| switch ($Status) { | |
| 'OK' { $icon = "[ OK ]"; $color = "Green" } | |
| 'WARN' { $icon = "[WARN]"; $color = "DarkYellow" } | |
| 'FAIL' { $icon = "[FAIL]"; $color = "Red" } | |
| 'INFO' { $icon = "[INFO]"; $color = "Gray" } | |
| } | |
| Write-Host " $icon " -ForegroundColor $color -NoNewline | |
| Write-Host $Message | |
| if ($Detail) { Write-Host " $Detail" -ForegroundColor DarkGray } | |
| if ($Fix -and $Status -in @('WARN','FAIL')) { | |
| Write-Host " FIX: $Fix" -ForegroundColor Magenta | |
| } | |
| Add-Finding -Status $Status -Message $Message -Fix $Fix | |
| } | |
| $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| # --------------------------------------------------------------------------- | |
| Write-Banner "WINDOWS NETWORK SHARING DIAGNOSTICS" | |
| Write-Host " Host: $env:COMPUTERNAME User: $env:USERNAME Elevated: $isAdmin" -ForegroundColor Gray | |
| if (-not $isAdmin) { | |
| Write-Host "" | |
| Write-Host " NOTE: Not running as Administrator. Some checks (firewall, registry," -ForegroundColor DarkYellow | |
| Write-Host " SMB config) may be incomplete or inaccurate. Re-run elevated for full results." -ForegroundColor DarkYellow | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "1. Network Profile (Private vs Public)" | |
| try { | |
| $profiles = Get-NetConnectionProfile | |
| foreach ($p in $profiles) { | |
| if ($p.NetworkCategory -eq 'Public') { | |
| Write-Check -Status FAIL -Message "Interface '$($p.InterfaceAlias)' is on network category: Public" ` | |
| -Fix "Set-NetConnectionProfile -InterfaceAlias '$($p.InterfaceAlias)' -NetworkCategory Private (or Settings > Network > Properties > Private)" | |
| } elseif ($p.NetworkCategory -eq 'DomainAuthenticated') { | |
| Write-Check -Status OK -Message "Interface '$($p.InterfaceAlias)' is on a Domain network (sharing rules apply per domain profile)" | |
| } else { | |
| Write-Check -Status OK -Message "Interface '$($p.InterfaceAlias)' is on network category: Private" | |
| } | |
| } | |
| if (-not $profiles) { Write-Check -Status WARN -Message "No active network profiles found" } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not read network profile" -Detail $_.Exception.Message | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "2. Required Services" | |
| $services = @( | |
| @{ Name = "LanmanServer"; Desc = "Server (needed to HOST shares)" } | |
| @{ Name = "LanmanWorkstation"; Desc = "Workstation (needed to ACCESS shares)" } | |
| @{ Name = "FDResPub"; Desc = "Function Discovery Resource Publication (Network discovery)" } | |
| @{ Name = "fdPHost"; Desc = "Function Discovery Provider Host (Network discovery)" } | |
| @{ Name = "SSDPSRV"; Desc = "SSDP Discovery (Network discovery)" } | |
| @{ Name = "upnphost"; Desc = "UPnP Device Host (Network discovery)" } | |
| ) | |
| foreach ($s in $services) { | |
| try { | |
| $svc = Get-Service -Name $s.Name -ErrorAction Stop | |
| if ($svc.Status -eq 'Running') { | |
| Write-Check -Status OK -Message "$($s.Name) is running" -Detail $s.Desc | |
| } else { | |
| Write-Check -Status FAIL -Message "$($s.Name) is $($svc.Status)" -Detail $s.Desc ` | |
| -Fix "Start-Service $($s.Name); Set-Service $($s.Name) -StartupType Automatic" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "$($s.Name) service not found" -Detail $s.Desc | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "3. Firewall Rules" | |
| $fwGroups = @("File and Printer Sharing", "Network Discovery") | |
| foreach ($grp in $fwGroups) { | |
| try { | |
| $rules = Get-NetFirewallRule -DisplayGroup $grp -ErrorAction Stop | |
| $enabledPrivate = $rules | Where-Object { $_.Enabled -eq $true -and $_.Profile -match 'Private|Any|Domain' } | |
| if ($enabledPrivate) { | |
| Write-Check -Status OK -Message "'$grp' firewall rules are enabled" -Detail "$($enabledPrivate.Count) of $($rules.Count) rules enabled" | |
| } else { | |
| Write-Check -Status FAIL -Message "'$grp' firewall rules are disabled" ` | |
| -Fix "Enable-NetFirewallRule -DisplayGroup '$grp' (or Control Panel > Windows Defender Firewall > Allow an app)" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not read firewall rules for '$grp'" -Detail $_.Exception.Message | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "4. SMB Protocol Configuration" | |
| try { | |
| $smbCfg = Get-SmbServerConfiguration -ErrorAction Stop | |
| if ($smbCfg.EnableSMB2Protocol) { | |
| Write-Check -Status OK -Message "SMB2/3 protocol is enabled on the server" | |
| } else { | |
| Write-Check -Status FAIL -Message "SMB2/3 protocol is disabled" -Fix "Set-SmbServerConfiguration -EnableSMB2Protocol `$true" | |
| } | |
| if ($smbCfg.EnableSMB1Protocol) { | |
| Write-Check -Status WARN -Message "SMB1 is enabled (legacy, insecure)" -Fix "Disable unless required by very old devices: Set-SmbServerConfiguration -EnableSMB1Protocol `$false" | |
| } else { | |
| Write-Check -Status OK -Message "SMB1 is disabled (recommended)" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not read SMB server configuration" -Detail $_.Exception.Message | |
| } | |
| # Guest auth (matters most when run on the CLIENT PC connecting without a matching user account) | |
| try { | |
| $guestAuthPath = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" | |
| $guestAuth = Get-ItemProperty -Path $guestAuthPath -Name AllowInsecureGuestAuth -ErrorAction SilentlyContinue | |
| if ($null -ne $guestAuth) { | |
| if ($guestAuth.AllowInsecureGuestAuth -eq 1) { | |
| Write-Check -Status OK -Message "Insecure guest logons allowed on THIS PC as a client" -Detail "Relevant if connecting via Guest with no matching user account" | |
| } else { | |
| Write-Check -Status WARN -Message "Insecure guest logons are DISABLED on this PC as a client" ` | |
| -Detail "If the share requires Guest access (no matching username/password), this causes 'unable to connect' errors" ` | |
| -Fix "Only if truly needed: Set-ItemProperty -Path '$guestAuthPath' -Name AllowInsecureGuestAuth -Value 1 -Type DWord (or via gpedit: Computer Config > Admin Templates > Network > Lanman Workstation > Enable insecure guest logons)" | |
| } | |
| } else { | |
| Write-Check -Status INFO -Message "AllowInsecureGuestAuth not explicitly set (uses Windows default: disabled)" | |
| } | |
| } catch { | |
| Write-Check -Status INFO -Message "Could not read guest auth policy" -Detail $_.Exception.Message | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "5. Guest Account Status" | |
| try { | |
| $guest = Get-LocalUser -Name "Guest" -ErrorAction Stop | |
| if ($guest.Enabled) { | |
| Write-Check -Status INFO -Message "Local Guest account is Enabled" -Detail "Only matters if share is intentionally set up for anonymous/guest access" | |
| } else { | |
| Write-Check -Status OK -Message "Local Guest account is Disabled (normal/secure default)" | |
| } | |
| } catch { | |
| Write-Check -Status INFO -Message "Could not read local Guest account status" -Detail $_.Exception.Message | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "6. Shared Folders" | |
| try { | |
| $shares = Get-SmbShare -ErrorAction Stop | Where-Object { $_.Name -notmatch '\$$' -and $_.Name -ne 'IPC$' } | |
| if ($ShareName) { $shares = $shares | Where-Object { $_.Name -eq $ShareName } } | |
| if (-not $shares) { | |
| Write-Check -Status FAIL -Message "No (non-admin) shared folders found on this PC" -Fix "Create one with: New-SmbShare -Name 'ShareName' -Path 'C:\Path' -FullAccess 'Everyone' (or specific users)" | |
| } | |
| foreach ($sh in $shares) { | |
| Write-Host "" | |
| Write-Host " - Share: $($sh.Name) -> $($sh.Path)" -ForegroundColor White | |
| # Share-level permissions | |
| try { | |
| $access = Get-SmbShareAccess -Name $sh.Name -ErrorAction Stop | |
| foreach ($a in $access) { | |
| Write-Check -Status INFO -Message "Share permission: $($a.AccountName) = $($a.AccessControlType) $($a.AccessRight)" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not read share permissions for '$($sh.Name)'" -Detail $_.Exception.Message | |
| } | |
| # NTFS permissions on the underlying folder | |
| if (Test-Path $sh.Path) { | |
| try { | |
| $acl = Get-Acl -Path $sh.Path -ErrorAction Stop | |
| $everyoneOrUsers = $acl.Access | Where-Object { $_.IdentityReference -match 'Everyone|Users|Authenticated Users' } | |
| if ($everyoneOrUsers) { | |
| Write-Check -Status OK -Message "NTFS permissions include a broad group (Everyone/Users) on '$($sh.Path)'" | |
| } else { | |
| Write-Check -Status WARN -Message "NTFS permissions on '$($sh.Path)' don't include Everyone/Users/Authenticated Users" ` | |
| -Detail "Even if the SHARE permission allows access, restrictive NTFS permissions will still block it (most restrictive wins)" ` | |
| -Fix "Grant the specific user/group Read (or Modify) NTFS permission via folder Properties > Security, or: icacls ""$($sh.Path)"" /grant Users:(OI)(CI)RX" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not read NTFS ACL for '$($sh.Path)'" -Detail $_.Exception.Message | |
| } | |
| } else { | |
| Write-Check -Status FAIL -Message "Share path '$($sh.Path)' does not exist on disk" -Fix "Fix or recreate the share pointing to a valid folder path" | |
| } | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Could not enumerate SMB shares" -Detail $_.Exception.Message | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Section "7. Remote UAC / Token Filtering" | |
| try { | |
| $lsaPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" | |
| $tokenPolicy = Get-ItemProperty -Path $lsaPath -Name LocalAccountTokenFilterPolicy -ErrorAction SilentlyContinue | |
| if ($null -ne $tokenPolicy -and $tokenPolicy.LocalAccountTokenFilterPolicy -eq 1) { | |
| Write-Check -Status INFO -Message "LocalAccountTokenFilterPolicy = 1 (remote local-admin connections use full token)" | |
| } else { | |
| Write-Check -Status INFO -Message "LocalAccountTokenFilterPolicy not set to 1 (default)" ` | |
| -Detail "Only relevant if connecting with a LOCAL admin account remotely and getting access-denied despite correct permissions" ` | |
| -Fix "If needed: New-ItemProperty -Path '$lsaPath' -Name LocalAccountTokenFilterPolicy -Value 1 -PropertyType DWord -Force" | |
| } | |
| } catch { | |
| Write-Check -Status INFO -Message "Could not read LocalAccountTokenFilterPolicy" -Detail $_.Exception.Message | |
| } | |
| # --------------------------------------------------------------------------- | |
| if ($TargetComputer) { | |
| Write-Section "8. Connectivity Test to '$TargetComputer'" | |
| try { | |
| $ping = Test-Connection -ComputerName $TargetComputer -Count 2 -Quiet -ErrorAction SilentlyContinue | |
| if ($ping) { | |
| Write-Check -Status OK -Message "Ping to $TargetComputer succeeded" | |
| } else { | |
| Write-Check -Status WARN -Message "Ping to $TargetComputer failed" -Detail "ICMP may just be blocked by firewall; not always fatal for SMB" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Ping test error" -Detail $_.Exception.Message | |
| } | |
| try { | |
| $portTest = Test-NetConnection -ComputerName $TargetComputer -Port 445 -WarningAction SilentlyContinue | |
| if ($portTest.TcpTestSucceeded) { | |
| Write-Check -Status OK -Message "TCP port 445 (SMB) reachable on $TargetComputer" | |
| } else { | |
| Write-Check -Status FAIL -Message "TCP port 445 (SMB) NOT reachable on $TargetComputer" ` | |
| -Fix "Check firewall on $TargetComputer (allow File and Printer Sharing), confirm both PCs are on the same network/subnet, and that $TargetComputer is powered on and not asleep" | |
| } | |
| } catch { | |
| Write-Check -Status WARN -Message "Port 445 test error" -Detail $_.Exception.Message | |
| } | |
| try { | |
| Resolve-DnsName -Name $TargetComputer -ErrorAction Stop | Out-Null | |
| Write-Check -Status OK -Message "Name resolution for '$TargetComputer' succeeded" | |
| } catch { | |
| Write-Check -Status WARN -Message "Name resolution for '$TargetComputer' failed" ` | |
| -Detail "NetBIOS/mDNS name resolution can be flaky between PCs; try connecting by IP instead: \\<IP address>\ShareName" ` | |
| -Fix "Find IP with: Resolve-DnsName $TargetComputer or arp -a or check it on $TargetComputer with ipconfig" | |
| } | |
| } | |
| # --------------------------------------------------------------------------- | |
| Write-Banner "SUMMARY" | |
| $fails = $script:Findings | Where-Object { $_.Status -eq 'FAIL' } | |
| $warns = $script:Findings | Where-Object { $_.Status -eq 'WARN' } | |
| $oks = $script:Findings | Where-Object { $_.Status -eq 'OK' } | |
| Write-Host " OK: $($oks.Count) " -ForegroundColor Green -NoNewline | |
| Write-Host "WARN: $($warns.Count) " -ForegroundColor DarkYellow -NoNewline | |
| Write-Host "FAIL: $($fails.Count)" -ForegroundColor Red | |
| if ($fails.Count -eq 0 -and $warns.Count -eq 0) { | |
| Write-Host "" | |
| Write-Host " All checks passed. If the client still can't connect, try:" -ForegroundColor Green | |
| Write-Host " - Run this same script ON THE CLIENT PC with -TargetComputer '$env:COMPUTERNAME'" | |
| Write-Host " - Connect via IP instead of name: \\<IP-of-$env:COMPUTERNAME>\ShareName" | |
| Write-Host " - Note the EXACT error text/code on the client (e.g. 0x80070035, 0x80070005) for further diagnosis" | |
| } else { | |
| if ($fails.Count -gt 0) { | |
| Write-Host "" | |
| Write-Host " Top priority fixes (FAIL):" -ForegroundColor Red | |
| foreach ($f in $fails) { | |
| Write-Host " - $($f.Message)" -ForegroundColor Red | |
| if ($f.Fix) { Write-Host " -> $($f.Fix)" -ForegroundColor DarkGray } | |
| } | |
| } | |
| if ($warns.Count -gt 0) { | |
| Write-Host "" | |
| Write-Host " Worth checking (WARN):" -ForegroundColor DarkYellow | |
| foreach ($w in $warns) { | |
| Write-Host " - $($w.Message)" -ForegroundColor DarkYellow | |
| if ($w.Fix) { Write-Host " -> $($w.Fix)" -ForegroundColor DarkGray } | |
| } | |
| } | |
| } | |
| Write-Host "" | |
| Write-Host ("=" * 80) -ForegroundColor Cyan | |
| Write-Host " Tip: run this script (elevated) on BOTH PCs for the full picture:" -ForegroundColor Gray | |
| Write-Host " On DELL-MICRO (host): .\Test-NetworkSharing.ps1 -TargetComputer 'ClientPCName'" | |
| Write-Host " On the client PC: .\Test-NetworkSharing.ps1 -TargetComputer 'DELL-MICRO'" | |
| Write-Host ("=" * 80) -ForegroundColor Cyan | |
| Write-Host "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment