Created
December 11, 2025 15:56
-
-
Save fahadysf/bbe9a722594f9c081c9c1fae574ffd28 to your computer and use it in GitHub Desktop.
Powershell Script to Diagnose any issues in Remote Desktop (Incoming) Connectivity for the host.
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
| #Requires -RunAsAdministrator | |
| <# | |
| .SYNOPSIS | |
| RDP Health Check - Diagnoses Remote Desktop Service connectivity issues | |
| .DESCRIPTION | |
| Checks service status, listening ports, firewall rules, NLA settings, | |
| user permissions, and network accessibility for RDP connections. | |
| #> | |
| param( | |
| [int]$RDPPort = 3389 | |
| ) | |
| $ErrorActionPreference = "Continue" | |
| function Write-Status { | |
| param([string]$Message, [string]$Status, [string]$Details = "") | |
| $color = switch ($Status) { | |
| "OK" { "Green" } | |
| "WARNING" { "Yellow" } | |
| "ERROR" { "Red" } | |
| "INFO" { "Cyan" } | |
| default { "White" } | |
| } | |
| Write-Host "[$Status] " -ForegroundColor $color -NoNewline | |
| Write-Host $Message | |
| if ($Details) { Write-Host " $Details" -ForegroundColor DarkGray } | |
| } | |
| Write-Host "`n========== RDP HEALTH CHECK ==========" -ForegroundColor Cyan | |
| Write-Host "Hostname: $env:COMPUTERNAME" | |
| Write-Host "Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n" | |
| # 1. Check Terminal Services (RDP) Service | |
| Write-Host "`n[1] SERVICE STATUS" -ForegroundColor Yellow | |
| $rdpService = Get-Service -Name TermService -ErrorAction SilentlyContinue | |
| if ($rdpService) { | |
| if ($rdpService.Status -eq "Running") { | |
| Write-Status "Remote Desktop Services (TermService)" "OK" "Status: Running" | |
| } else { | |
| Write-Status "Remote Desktop Services (TermService)" "ERROR" "Status: $($rdpService.Status) - Service not running!" | |
| } | |
| $startType = (Get-WmiObject Win32_Service -Filter "Name='TermService'").StartMode | |
| Write-Status "Service Start Type" "INFO" $startType | |
| } else { | |
| Write-Status "Remote Desktop Services" "ERROR" "Service not found!" | |
| } | |
| # Check RDP-related services | |
| $rdpDependencies = @("SessionEnv", "UmRdpService") | |
| foreach ($svc in $rdpDependencies) { | |
| $service = Get-Service -Name $svc -ErrorAction SilentlyContinue | |
| if ($service -and $service.Status -eq "Running") { | |
| Write-Status "$($service.DisplayName)" "OK" "Running" | |
| } elseif ($service) { | |
| Write-Status "$($service.DisplayName)" "WARNING" "Status: $($service.Status)" | |
| } | |
| } | |
| # 2. Check if RDP is Enabled in Registry | |
| Write-Host "`n[2] RDP CONFIGURATION" -ForegroundColor Yellow | |
| $rdpEnabled = (Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -ErrorAction SilentlyContinue).fDenyTSConnections | |
| if ($rdpEnabled -eq 0) { | |
| Write-Status "RDP Enabled (Registry)" "OK" "Remote connections are allowed" | |
| } else { | |
| Write-Status "RDP Enabled (Registry)" "ERROR" "Remote connections are DENIED (fDenyTSConnections = $rdpEnabled)" | |
| } | |
| # Check NLA requirement | |
| $nlaRequired = (Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -ErrorAction SilentlyContinue).UserAuthentication | |
| if ($nlaRequired -eq 1) { | |
| Write-Status "Network Level Authentication (NLA)" "OK" "Required (more secure)" | |
| } else { | |
| Write-Status "Network Level Authentication (NLA)" "WARNING" "Not required (less secure, but allows older clients)" | |
| } | |
| # Check security layer | |
| $securityLayer = (Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "SecurityLayer" -ErrorAction SilentlyContinue).SecurityLayer | |
| $secLayerDesc = switch ($securityLayer) { | |
| 0 { "Native RDP (least secure)" } | |
| 1 { "Negotiate" } | |
| 2 { "TLS/SSL (most secure)" } | |
| default { "Unknown" } | |
| } | |
| Write-Status "Security Layer" "INFO" $secLayerDesc | |
| # 3. Check Listening Port | |
| Write-Host "`n[3] NETWORK LISTENER" -ForegroundColor Yellow | |
| $portNumber = (Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "PortNumber" -ErrorAction SilentlyContinue).PortNumber | |
| if ($portNumber) { | |
| Write-Status "Configured RDP Port" "INFO" $portNumber | |
| $RDPPort = $portNumber | |
| } | |
| $listener = Get-NetTCPConnection -LocalPort $RDPPort -State Listen -ErrorAction SilentlyContinue | |
| if ($listener) { | |
| Write-Status "Port $RDPPort Listening" "OK" "Process: $((Get-Process -Id $listener.OwningProcess -ErrorAction SilentlyContinue).ProcessName)" | |
| $bindAddress = $listener.LocalAddress | |
| if ($bindAddress -eq "0.0.0.0" -or $bindAddress -eq "::") { | |
| Write-Status "Binding" "OK" "Listening on all interfaces" | |
| } else { | |
| Write-Status "Binding" "WARNING" "Only listening on: $bindAddress" | |
| } | |
| } else { | |
| Write-Status "Port $RDPPort Listening" "ERROR" "RDP is NOT listening on port $RDPPort!" | |
| } | |
| # Show active RDP connections | |
| $activeConnections = Get-NetTCPConnection -LocalPort $RDPPort -State Established -ErrorAction SilentlyContinue | |
| if ($activeConnections) { | |
| Write-Status "Active RDP Connections" "INFO" "$($activeConnections.Count) connection(s)" | |
| $activeConnections | ForEach-Object { | |
| Write-Host " -> $($_.RemoteAddress):$($_.RemotePort)" -ForegroundColor DarkGray | |
| } | |
| } else { | |
| Write-Status "Active RDP Connections" "INFO" "None" | |
| } | |
| # 4. Windows Firewall Rules | |
| Write-Host "`n[4] FIREWALL RULES" -ForegroundColor Yellow | |
| $firewallProfiles = Get-NetFirewallProfile | |
| foreach ($profile in $firewallProfiles) { | |
| $status = if ($profile.Enabled) { "Enabled" } else { "Disabled" } | |
| $color = if ($profile.Enabled) { "INFO" } else { "WARNING" } | |
| Write-Status "Firewall Profile: $($profile.Name)" $color $status | |
| } | |
| $rdpRules = Get-NetFirewallRule -DisplayGroup "Remote Desktop" -ErrorAction SilentlyContinue | |
| if ($rdpRules) { | |
| $enabledRules = $rdpRules | Where-Object { $_.Enabled -eq "True" -and $_.Direction -eq "Inbound" } | |
| if ($enabledRules) { | |
| Write-Status "RDP Firewall Rules" "OK" "$($enabledRules.Count) inbound rule(s) enabled" | |
| foreach ($rule in $enabledRules) { | |
| $ruleProfiles = $rule.Profile | |
| $portFilter = $rule | Get-NetFirewallPortFilter | |
| $addressFilter = $rule | Get-NetFirewallAddressFilter | |
| Write-Host " -> $($rule.DisplayName)" -ForegroundColor DarkGray | |
| Write-Host " Profiles: $ruleProfiles | RemoteAddr: $($addressFilter.RemoteAddress)" -ForegroundColor DarkGray | |
| } | |
| } else { | |
| Write-Status "RDP Firewall Rules" "ERROR" "No enabled inbound RDP rules found!" | |
| } | |
| } else { | |
| Write-Status "RDP Firewall Rules" "ERROR" "No Remote Desktop firewall rules exist!" | |
| } | |
| # Check for any BLOCK rules | |
| $blockRules = Get-NetFirewallRule -Action Block -Direction Inbound -Enabled True -ErrorAction SilentlyContinue | | |
| Where-Object { ($_ | Get-NetFirewallPortFilter).LocalPort -eq $RDPPort } | |
| if ($blockRules) { | |
| Write-Status "Blocking Rules Detected" "ERROR" "$($blockRules.Count) rule(s) blocking port $RDPPort" | |
| $blockRules | ForEach-Object { Write-Host " -> $($_.DisplayName)" -ForegroundColor Red } | |
| } | |
| # 5. Check RDP User Permissions | |
| Write-Host "`n[5] USER PERMISSIONS" -ForegroundColor Yellow | |
| try { | |
| $rdpGroup = [ADSI]"WinNT://./Remote Desktop Users,group" | |
| $members = @($rdpGroup.Invoke("Members")) | ForEach-Object { | |
| $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) | |
| } | |
| if ($members.Count -gt 0) { | |
| Write-Status "Remote Desktop Users Group" "OK" "$($members.Count) member(s)" | |
| $members | ForEach-Object { Write-Host " -> $_" -ForegroundColor DarkGray } | |
| } else { | |
| Write-Status "Remote Desktop Users Group" "WARNING" "No members (only Administrators can connect)" | |
| } | |
| } catch { | |
| Write-Status "Remote Desktop Users Group" "WARNING" "Could not enumerate members" | |
| } | |
| # Check if local Administrators can RDP | |
| Write-Status "Local Administrators" "INFO" "Always allowed to connect via RDP" | |
| # 6. Check RDP Certificate | |
| Write-Host "`n[6] RDP CERTIFICATE" -ForegroundColor Yellow | |
| try { | |
| $rdpCertThumb = (Get-WmiObject -Class "Win32_TSGeneralSetting" -Namespace "root\cimv2\terminalservices" -ErrorAction SilentlyContinue).SSLCertificateSHA1Hash | |
| if ($rdpCertThumb) { | |
| $cert = Get-ChildItem -Path "Cert:\LocalMachine\My\$rdpCertThumb" -ErrorAction SilentlyContinue | |
| if ($cert) { | |
| if ($cert.NotAfter -gt (Get-Date)) { | |
| Write-Status "RDP Certificate" "OK" "Valid until $($cert.NotAfter.ToString('yyyy-MM-dd'))" | |
| } else { | |
| Write-Status "RDP Certificate" "ERROR" "EXPIRED on $($cert.NotAfter.ToString('yyyy-MM-dd'))" | |
| } | |
| Write-Status "Certificate Subject" "INFO" $cert.Subject | |
| } else { | |
| Write-Status "RDP Certificate" "WARNING" "Certificate not found in store" | |
| } | |
| } else { | |
| Write-Status "RDP Certificate" "INFO" "Using self-signed certificate (default)" | |
| } | |
| } catch { | |
| Write-Status "RDP Certificate" "WARNING" "Could not check certificate status" | |
| } | |
| # 7. Local Network Accessibility Test | |
| Write-Host "`n[7] LOCAL NETWORK ACCESSIBILITY" -ForegroundColor Yellow | |
| $localIPs = Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -notlike "127.*" } | |
| Write-Status "Local IP Addresses" "INFO" "" | |
| foreach ($ip in $localIPs) { | |
| $ifAlias = $ip.InterfaceAlias | |
| $ipAddr = $ip.IPAddress | |
| Write-Host " -> $ipAddr ($ifAlias)" -ForegroundColor DarkGray | |
| } | |
| # Check if Windows Remote Management might interfere | |
| $winrmListener = Get-WSManInstance -ResourceURI winrm/config/listener -Enumerate -ErrorAction SilentlyContinue 2>$null | |
| if ($winrmListener) { | |
| Write-Status "WinRM Listener" "INFO" "Active (separate from RDP)" | |
| } | |
| # 8. Test local loopback connection | |
| Write-Host "`n[8] CONNECTIVITY TEST" -ForegroundColor Yellow | |
| try { | |
| $tcpTest = New-Object System.Net.Sockets.TcpClient | |
| $tcpTest.Connect("127.0.0.1", $RDPPort) | |
| if ($tcpTest.Connected) { | |
| Write-Status "Loopback Test (127.0.0.1:$RDPPort)" "OK" "Connection successful" | |
| $tcpTest.Close() | |
| } | |
| } catch { | |
| Write-Status "Loopback Test (127.0.0.1:$RDPPort)" "ERROR" "Cannot connect: $($_.Exception.Message)" | |
| } | |
| # Test on each local IP | |
| foreach ($ip in $localIPs) { | |
| try { | |
| $tcpTest = New-Object System.Net.Sockets.TcpClient | |
| $asyncResult = $tcpTest.BeginConnect($ip.IPAddress, $RDPPort, $null, $null) | |
| $wait = $asyncResult.AsyncWaitHandle.WaitOne(2000, $false) | |
| if ($wait -and $tcpTest.Connected) { | |
| Write-Status "Local Test ($($ip.IPAddress):$RDPPort)" "OK" "Reachable" | |
| $tcpTest.Close() | |
| } else { | |
| Write-Status "Local Test ($($ip.IPAddress):$RDPPort)" "WARNING" "Connection timeout" | |
| } | |
| } catch { | |
| Write-Status "Local Test ($($ip.IPAddress):$RDPPort)" "ERROR" $_.Exception.Message | |
| } | |
| } | |
| # 9. Event Log Check (recent RDP-related events) | |
| Write-Host "`n[9] RECENT RDP EVENTS (Last 24h)" -ForegroundColor Yellow | |
| try { | |
| $rdpEvents = Get-WinEvent -FilterHashtable @{ | |
| LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational' | |
| StartTime = (Get-Date).AddHours(-24) | |
| } -MaxEvents 5 -ErrorAction SilentlyContinue | |
| if ($rdpEvents) { | |
| Write-Status "Recent Session Events" "INFO" "$($rdpEvents.Count) event(s) found" | |
| $rdpEvents | ForEach-Object { | |
| Write-Host " -> $($_.TimeCreated.ToString('MM-dd HH:mm')) [ID:$($_.Id)] $($_.Message.Split("`n")[0].Substring(0, [Math]::Min(60, $_.Message.Length)))..." -ForegroundColor DarkGray | |
| } | |
| } else { | |
| Write-Status "Recent Session Events" "INFO" "No events in last 24 hours" | |
| } | |
| } catch { | |
| Write-Status "Event Log Check" "WARNING" "Could not read event log" | |
| } | |
| # Summary | |
| Write-Host "`n========== SUMMARY ==========" -ForegroundColor Cyan | |
| $issues = @() | |
| if ($rdpService.Status -ne "Running") { $issues += "TermService not running" } | |
| if ($rdpEnabled -ne 0) { $issues += "RDP disabled in registry" } | |
| if (-not $listener) { $issues += "Not listening on port $RDPPort" } | |
| if (-not $enabledRules) { $issues += "No firewall rules enabled" } | |
| if ($blockRules) { $issues += "Blocking firewall rules detected" } | |
| if ($issues.Count -eq 0) { | |
| Write-Host "RDP appears to be configured correctly and ready for connections." -ForegroundColor Green | |
| } else { | |
| Write-Host "Issues detected:" -ForegroundColor Red | |
| $issues | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } | |
| } | |
| Write-Host "`nClients should connect to: $($localIPs[0].IPAddress):$RDPPort" -ForegroundColor Cyan | |
| Write-Host "" |
Author
Author
Fix for Windows 11 not listening on 3389
# Requires elevated (Administrator) PowerShell session
# --- Configuration ---
$RdpControlPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
$RdpControlValue = "fDenyTSConnections"
$EnabledValue = 0 # 0 means RDP is ENABLED (Not Denied)
Write-Host "--- Applying RDP Global Enable Fix ---"
# 1. Check and Set the fDenyTSConnections value to 0 (Enabled)
Write-Host "1. Setting $RdpControlValue to $EnabledValue (Enabled) in $RdpControlPath..."
try {
# Ensure the registry path exists before attempting to set the value
if (-not (Test-Path $RdpControlPath)) {
Write-Error "Registry path $RdpControlPath does not exist. Creating it."
New-Item -Path $RdpControlPath -Force | Out-Null
}
# Set the key to 0 to ensure RDP is not denied
Set-ItemProperty -Path $RdpControlPath -Name $RdpControlValue -Value $EnabledValue -Type DWORD -Force
Write-Host "Successfully set $RdpControlValue to $EnabledValue."
} catch {
Write-Error "An error occurred while setting the registry key: $($_.Exception.Message)"
exit 1
}
# 2. Restart Services
Write-Host "2. Restarting Remote Desktop Services to apply changes..."
try {
# Restart the relevant services to force the listener to initialize with the new setting
Restart-Service TermService -Force
Restart-Service SessionEnv -Force
Write-Host "Remote Desktop Services restarted successfully."
} catch {
Write-Warning "Could not restart services. You may need to manually restart them or reboot the system."
}
# 3. Verification
Write-Host "--- Script Finished. ---"
Write-Host "Checking RDP listener status on default port 3389 (Netstat -ano | findstr 3389):"
netstat -ano | findstr 3389
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To re-create the TCP listener via Registry: