Skip to content

Instantly share code, notes, and snippets.

@fahadysf
Created December 11, 2025 15:56
Show Gist options
  • Select an option

  • Save fahadysf/bbe9a722594f9c081c9c1fae574ffd28 to your computer and use it in GitHub Desktop.

Select an option

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.
#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 ""
@fahadysf
Copy link
Author

fahadysf commented Dec 11, 2025

To re-create the TCP listener via Registry:

# Requires elevated (Administrator) PowerShell session

# --- Configuration ---
$RdpKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$BackupPath = "C:\Temp\RdpTcp_Backup.reg"
$DefaultPort = 3389

# --- Backup Existing Key ---
Write-Host "Backing up RDP-Tcp registry key to $BackupPath..."
# Ensure the backup directory exists
if (-not (Test-Path (Split-Path $BackupPath -Parent))) {
    New-Item -Path (Split-Path $BackupPath -Parent) -ItemType Directory -Force | Out-Null
}
# Export the key using reg.exe
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" $BackupPath /y

# --- Delete Key ---
Write-Host "Deleting the RDP-Tcp registry key..."
if (Test-Path $RdpKeyPath) {
    # Use -Force to delete the key and all its subkeys/properties without prompting
    Remove-Item -Path $RdpKeyPath -Recurse -Force
    Write-Host "RDP-Tcp registry key deleted successfully."
} else {
    Write-Host "RDP-Tcp registry key does not exist. Proceeding to create default key."
}

# --- Re-Create Key and Default Properties ---
Write-Host "Explicitly re-creating the RDP-Tcp registry key with default settings..."

# 1. Create the main key
New-Item -Path $RdpKeyPath -Force | Out-Null

# 2. Set the necessary default properties
# This is the Listener port (default 3389)
New-ItemProperty -Path $RdpKeyPath -Name "PortNumber" -Value $DefaultPort -Type DWORD -Force | Out-Null
# This value determines the listener is active (0=Active, 1=Inactive)
New-ItemProperty -Path $RdpKeyPath -Name "fDisableListener" -Value 0 -Type DWORD -Force | Out-Null
# This value controls the RDP security layer (0=Negotiate, 1=RDP Security Layer, 2=SSL)
New-ItemProperty -Path $RdpKeyPath -Name "SecurityLayer" -Value 1 -Type DWORD -Force | Out-Null
# This value controls the encryption level (2=Low, 3=Client Compatible, 4=High, 9=FIPS)
New-ItemProperty -Path $RdpKeyPath -Name "MinEncryptionLevel" -Value 2 -Type DWORD -Force | Out-Null
# This value controls Transport Type (Default TCP)
New-ItemProperty -Path $RdpKeyPath -Name "Transport" -Value 1 -Type DWORD -Force | Out-Null


Write-Host "Default RDP-Tcp registry key and properties re-created."

# --- Restart Services ---
Write-Host "Restarting Remote Desktop Services to apply changes..."
# Restart the relevant services to force recreation and re-initialization
Restart-Service TermService -Force
Restart-Service SessionEnv -Force

Write-Host "Script finished. RDP should now be listening on port $DefaultPort."

# Optional check:
Write-Host "Checking RDP listener status (Netstat -ano | findstr 3389):"
netstat -ano | findstr $DefaultPort

@fahadysf
Copy link
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