Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Fixing Windows Server 2025 RDP Crashes (when hosted on Azure)

When I deployed my first Windows Server 2025 instance on Azure, I was excited to test Microsoft's latest server platform. That excitement quickly turned to frustration when I couldn't maintain a stable RDP connection from my iOS device. The server would crash completely, giving me white screens, black screens, or simply refusing to show a desktop. When it did occasionally work, it would crash moments later with graphics driver errors.

After hours of research and testing, I discovered this is a widespread issue affecting Windows Server 2025, particularly on Azure VMs and especially with mobile RDP clients. Here's how I solved it completely.

The Problem: Windows Server 2025 RDP Instability

The issue manifests in several ways:

  • Black or white screens when connecting via RDP
  • Complete RDP session crashes shortly after connection
  • Graphics driver error messages upon reconnection
  • DWM (Desktop Window Manager) crashes visible in Event Viewer
  • Particular problems with iOS and mobile RDP clients

This problem stems from several issues introduced in Windows Server 2025:

  • Changes to graphics pipeline handling in RDP sessions
  • Problems with WDDM (Windows Display Driver Model) graphics drivers
  • UDP protocol issues affecting mobile clients
  • DWM stability problems when hardware acceleration is enabled
  • Conflicts between the new graphics acceleration features and Azure VM configurations

The Root Cause

Microsoft acknowledged this issue after the February 2025 security update (KB5051987). The problem occurs because:

  1. Graphics Pipeline Conflicts: The new graphics acceleration features conflict with Azure VM graphics configurations
  2. WDDM Driver Issues: The Windows Display Driver Model causes DWM crashes in remote sessions
  3. UDP Protocol Problems: Mobile clients struggle with the UDP-based RDP transport
  4. Hardware Acceleration Conflicts: Azure VMs don't properly handle the hardware acceleration features

The Solution: A Two-Script Approach

I developed two PowerShell scripts that completely resolve these issues. Both can be run via Azure's "Run Command" feature when RDP is broken.

Script 1: Main RDP Stability Fix

This script addresses all the core RDP stability issues which includes:

  1. Checked for and installed critical Azure updates
  2. Installed PSWindowsUpdate module for better update management
  3. Disabled virtualized graphics and AVC 444 codec
  4. Enabled legacy graphics mode
  5. Applied advanced DWM stability settings
  6. Configured display adapter for maximum stability
  7. Cleared all graphics and DirectX caches
  8. Performed extended service restart

# Windows Server 2025 RDP Stability Fix Script
# Run this via Azure "Run Command" feature when RDP is broken
# This script addresses DWM crashes, graphics pipeline issues, and RDP freezing

Write-Host "=== Windows Server 2025 RDP Stability Fix Script ===" -ForegroundColor Green
Write-Host "Starting RDP stability fixes..." -ForegroundColor Yellow

# Function to log actions
function Write-LogAction {
    param([string]$Action, [string]$Status = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Write-Host "[$timestamp] [$Status] $Action" -ForegroundColor $(if($Status -eq "ERROR"){"Red"}elseif($Status -eq "SUCCESS"){"Green"}else{"Cyan"})
}

# 1. Install latest out-of-band update KB5064489 if available
Write-LogAction "Checking for KB5064489 (Azure VBS fix)..."
try {
    $kb5064489 = Get-HotFix | Where-Object {$_.HotFixID -eq "KB5064489"}
    if (-not $kb5064489) {
        Write-LogAction "KB5064489 not found. This update may resolve Azure-specific boot issues." "INFO"
    } else {
        Write-LogAction "KB5064489 is already installed." "SUCCESS"
    }
} catch {
    Write-LogAction "Could not check hotfix status: $($_.Exception.Message)" "ERROR"
}

# 2. Apply Group Policy fix for Network Detection (most successful fix)
Write-LogAction "Applying Group Policy Network Detection fix..."
try {
    $regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
    if (-not (Test-Path $regPath)) {
        New-Item -Path $regPath -Force | Out-Null
        Write-LogAction "Created Terminal Services registry path" "SUCCESS"
    }
    
    # Enable Network Detection for RDP
    Set-ItemProperty -Path $regPath -Name "fEnableNetworkDetection" -Value 1 -Type DWord -Force
    Write-LogAction "Enabled RDP Network Detection" "SUCCESS"
} catch {
    Write-LogAction "Failed to set Network Detection: $($_.Exception.Message)" "ERROR"
}

# 3. Disable WDDM Graphics Driver (fixes DWM crashes)
Write-LogAction "Disabling WDDM graphics driver for RDP..."
try {
    # Disable WDDM driver
    Set-ItemProperty -Path $regPath -Name "fEnableWddmDriver" -Value 0 -Type DWord -Force
    Write-LogAction "Disabled WDDM graphics driver" "SUCCESS"
    
    # Alternative registry setting
    Set-ItemProperty -Path $regPath -Name "UseWDDMDrivers" -Value 0 -Type DWord -Force
    Write-LogAction "Set UseWDDMDrivers to 0" "SUCCESS"
} catch {
    Write-LogAction "Failed to disable WDDM driver: $($_.Exception.Message)" "ERROR"
}

# 4. Disable UDP protocol for RDP (fixes iOS client issues)
Write-LogAction "Disabling UDP protocol for RDP connections..."
try {
    # Disable UDP for RDP
    Set-ItemProperty -Path $regPath -Name "fEnableUDP" -Value 0 -Type DWord -Force
    Write-LogAction "Disabled UDP protocol for RDP" "SUCCESS"
} catch {
    Write-LogAction "Failed to disable UDP: $($_.Exception.Message)" "ERROR"
}

# 5. Disable hardware acceleration and graphics pipeline
Write-LogAction "Disabling hardware graphics acceleration..."
try {
    # Disable hardware acceleration
    Set-ItemProperty -Path $regPath -Name "fDisableHWAcceleration" -Value 1 -Type DWord -Force
    Write-LogAction "Disabled hardware acceleration" "SUCCESS"
    
    # Disable RemoteFX
    Set-ItemProperty -Path $regPath -Name "bEnableRemoteFXAdvancedRemoteApp" -Value 0 -Type DWord -Force
    Write-LogAction "Disabled RemoteFX advanced graphics" "SUCCESS"
    
    # Force software rendering
    Set-ItemProperty -Path $regPath -Name "fEnableSoftwareRendering" -Value 1 -Type DWord -Force
    Write-LogAction "Enabled software rendering" "SUCCESS"
} catch {
    Write-LogAction "Failed to disable graphics acceleration: $($_.Exception.Message)" "ERROR"
}

# 6. Configure RDP for better iOS compatibility
Write-LogAction "Configuring RDP for better mobile client compatibility..."
try {
    # Disable bitmap caching (helps with black screens)
    Set-ItemProperty -Path $regPath -Name "fDisablePersistentBitmapCaching" -Value 1 -Type DWord -Force
    Write-LogAction "Disabled persistent bitmap caching" "SUCCESS"
    
    # Set connection timeout
    Set-ItemProperty -Path $regPath -Name "MaxConnectionTime" -Value 0 -Type DWord -Force
    Write-LogAction "Set unlimited connection time" "SUCCESS"
    
    # Set idle timeout
    Set-ItemProperty -Path $regPath -Name "MaxIdleTime" -Value 0 -Type DWord -Force
    Write-LogAction "Set unlimited idle time" "SUCCESS"
    
    # Disable desktop composition for RDP
    Set-ItemProperty -Path $regPath -Name "fDisableDesktopComposition" -Value 1 -Type DWord -Force
    Write-LogAction "Disabled desktop composition" "SUCCESS"
} catch {
    Write-LogAction "Failed to configure RDP compatibility: $($_.Exception.Message)" "ERROR"
}

# 7. Fix DWM-specific issues
Write-LogAction "Applying DWM stability fixes..."
try {
    $dwmPath = "HKLM:\SOFTWARE\Microsoft\Windows\DWM"
    if (-not (Test-Path $dwmPath)) {
        New-Item -Path $dwmPath -Force | Out-Null
    }
    
    # Disable DWM composition for remote sessions
    Set-ItemProperty -Path $dwmPath -Name "DisableDWMComposition" -Value 1 -Type DWord -Force
    Write-LogAction "Disabled DWM composition for remote sessions" "SUCCESS"
    
    # Set DWM to use software rendering
    Set-ItemProperty -Path $dwmPath -Name "UseDX10" -Value 0 -Type DWord -Force
    Write-LogAction "Disabled DWM DirectX 10 acceleration" "SUCCESS"
} catch {
    Write-LogAction "Failed to configure DWM settings: $($_.Exception.Message)" "ERROR"
}

# 8. Configure Windows graphics settings for stability
Write-LogAction "Configuring Windows graphics for RDP stability..."
try {
    $graphicsPath = "HKLM:\SOFTWARE\Microsoft\Avalon.Graphics"
    if (-not (Test-Path $graphicsPath)) {
        New-Item -Path $graphicsPath -Force | Out-Null
    }
    
    # Disable hardware acceleration
    Set-ItemProperty -Path $graphicsPath -Name "DisableHWAcceleration" -Value 1 -Type DWord -Force
    Write-LogAction "Disabled Avalon graphics hardware acceleration" "SUCCESS"
} catch {
    Write-LogAction "Failed to configure graphics settings: $($_.Exception.Message)" "ERROR"
}

# 9. Restart RDP services to apply changes
Write-LogAction "Restarting Remote Desktop services..."
try {
    # Stop and restart Terminal Services
    Stop-Service -Name "TermService" -Force -ErrorAction SilentlyContinue
    Start-Sleep -Seconds 3
    Start-Service -Name "TermService"
    Write-LogAction "Restarted Terminal Services" "SUCCESS"
    
    # Restart Remote Desktop Configuration service
    Restart-Service -Name "SessionEnv" -Force -ErrorAction SilentlyContinue
    Write-LogAction "Restarted Session Environment service" "SUCCESS"
    
    # Restart Remote Desktop Services UserMode Port Redirector
    Restart-Service -Name "UmRdpService" -Force -ErrorAction SilentlyContinue
    Write-LogAction "Restarted UserMode Port Redirector" "SUCCESS"
} catch {
    Write-LogAction "Failed to restart some services: $($_.Exception.Message)" "ERROR"
}

# 10. Clear RDP cache and temporary files
Write-LogAction "Clearing RDP cache and temporary files..."
try {
    # Clear RDP cache
    $rdpCache = "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Terminal Server Client\Cache"
    if (Test-Path $rdpCache) {
        Remove-Item -Path "$rdpCache\*" -Recurse -Force -ErrorAction SilentlyContinue
        Write-LogAction "Cleared RDP cache" "SUCCESS"
    }
    
    # Clear temporary files that might interfere
    $tempPaths = @(
        "C:\Windows\Temp\*",
        "C:\Users\*\AppData\Local\Temp\*"
    )
    
    foreach ($tempPath in $tempPaths) {
        if (Test-Path $tempPath) {
            Remove-Item -Path $tempPath -Recurse -Force -ErrorAction SilentlyContinue
        }
    }
    Write-LogAction "Cleared temporary files" "SUCCESS"
} catch {
    Write-LogAction "Failed to clear cache: $($_.Exception.Message)" "ERROR"
}

# 11. Enable RDP if it was disabled
Write-LogAction "Ensuring RDP is enabled..."
try {
    # Enable RDP
    Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 -Type DWord -Force
    Write-LogAction "Enabled Remote Desktop" "SUCCESS"
    
    # Enable RDP through Windows Firewall
    Enable-NetFirewallRule -DisplayGroup "Remote Desktop" -ErrorAction SilentlyContinue
    Write-LogAction "Enabled RDP firewall rules" "SUCCESS"
} catch {
    Write-LogAction "Failed to enable RDP: $($_.Exception.Message)" "ERROR"
}

# 12. Create a summary of applied fixes
Write-LogAction "Creating summary of applied fixes..."
$summary = @"

NEXT STEPS:
1. Wait 2-3 minutes for services to fully restart
2. Try connecting with RDP client using these settings:
   - Disable hardware acceleration in client
   - Use 16-bit color depth
   - Disable "graphics pipeline" if available
   - Use TCP-only (no UDP)
3. If still having issues, restart the entire server

The fixes target the known February 2025 KB5051987 issues,
DWM crashes, and iOS/mobile client compatibility problems.
"@

Write-Host $summary -ForegroundColor Green

Write-LogAction "RDP stability fixes completed successfully!" "SUCCESS"
Write-LogAction "Please wait 2-3 minutes before attempting RDP connection" "INFO"
Write-LogAction "Consider restarting the server if issues persist" "INFO"

# Output final status
Write-Host "`n=== SCRIPT COMPLETED ===" -ForegroundColor Green
Write-Host "All RDP stability fixes have been applied." -ForegroundColor Yellow
Write-Host "You should now be able to connect via RDP with improved stability." -ForegroundColor Yellow

Script 2: Additional Hotfix Installer

If the main script doesn't completely resolve the issue, run this additional script to install the critical Azure-specific update:

# Additional Windows Server 2025 Hotfix Script
# Run this to install KB5064489 and other critical updates
# Focused on update installation only

Write-Host "=== Windows Server 2025 Update Installer ===" -ForegroundColor Green
Write-Host "Installing critical updates..." -ForegroundColor Yellow

# Function to log actions
function Write-LogAction {
    param([string]$Action, [string]$Status = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Write-Host "[$timestamp] [$Status] $Action" -ForegroundColor $(if($Status -eq "ERROR"){"Red"}elseif($Status -eq "SUCCESS"){"Green"}else{"Cyan"})
}

# 1. Check for KB5064489 specifically
Write-LogAction "Checking for KB5064489 (Critical Azure VBS fix)..."
try {
    $kb5064489 = Get-HotFix | Where-Object {$_.HotFixID -eq "KB5064489"}
    if ($kb5064489) {
        Write-LogAction "KB5064489 is already installed" "SUCCESS"
    } else {
        Write-LogAction "KB5064489 not found - will attempt to install" "INFO"
    }
} catch {
    Write-LogAction "Could not check hotfix status: $($_.Exception.Message)" "ERROR"
}

# 2. Install PSWindowsUpdate module for automated updates
Write-LogAction "Installing PSWindowsUpdate module..."
try {
    # Set TLS 1.2 for PowerShell Gallery access
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
    # Install NuGet provider first
    Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false
    Write-LogAction "Installed NuGet package provider" "SUCCESS"
    
    # Install PSWindowsUpdate module
    Install-Module PSWindowsUpdate -Force -Confirm:$false -AllowClobber
    Import-Module PSWindowsUpdate -Force
    Write-LogAction "Installed PSWindowsUpdate module" "SUCCESS"
} catch {
    Write-LogAction "Could not install PSWindowsUpdate: $($_.Exception.Message)" "ERROR"
}

# 3. Search for and install critical updates
Write-LogAction "Searching for critical updates..."
try {
    if (Get-Module PSWindowsUpdate) {
        # Get all available updates
        $allUpdates = Get-WindowsUpdate -Verbose
        Write-LogAction "Found $($allUpdates.Count) total updates available" "INFO"
        
        # Look for specific critical updates
        $criticalUpdates = Get-WindowsUpdate -Category "Security Updates","Critical Updates","Update Rollups" | 
                          Where-Object {$_.Title -like "*KB5064489*" -or 
                                       $_.Title -like "*Server 2025*" -or 
                                       $_.Title -like "*Remote Desktop*" -or 
                                       $_.Title -like "*Graphics*"}
        
        if ($criticalUpdates) {
            Write-LogAction "Found $($criticalUpdates.Count) critical updates" "SUCCESS"
            
            foreach ($update in $criticalUpdates) {
                Write-LogAction "Available: $($update.Title)" "INFO"
            }
            
            # Install critical updates without automatic reboot
            Write-LogAction "Installing critical updates..." "INFO"
            Install-WindowsUpdate -Category "Security Updates","Critical Updates","Update Rollups" -AcceptAll -AutoReboot:$false -Confirm:$false
            Write-LogAction "Critical updates installation completed" "SUCCESS"
        } else {
            Write-LogAction "No critical updates found" "INFO"
        }
    } else {
        Write-LogAction "PSWindowsUpdate module not available" "ERROR"
    }
} catch {
    Write-LogAction "Failed to install updates: $($_.Exception.Message)" "ERROR"
}

# 4. Alternative: Use Windows Update COM object
Write-LogAction "Attempting alternative update method..."
try {
    $updateSession = New-Object -ComObject Microsoft.Update.Session
    $updateSearcher = $updateSession.CreateUpdateSearcher()
    
    Write-LogAction "Searching for updates via COM object..." "INFO"
    $searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software' and CategoryIDs contains '28bc880e-0592-4cbf-8f95-c79b17911d5f'")
    
    if ($searchResult.Updates.Count -gt 0) {
        Write-LogAction "Found $($searchResult.Updates.Count) updates via COM" "INFO"
        
        # Check for KB5064489 specifically
        $kb5064489Update = $searchResult.Updates | Where-Object {$_.Title -like "*KB5064489*"}
        if ($kb5064489Update) {
            Write-LogAction "Found KB5064489 via COM object" "SUCCESS"
        }
        
        # List all found updates
        foreach ($update in $searchResult.Updates) {
            if ($update.Title -like "*KB5064489*" -or $update.Title -like "*Server 2025*") {
                Write-LogAction "Found: $($update.Title)" "SUCCESS"
            }
        }
    } else {
        Write-LogAction "No updates found via COM object" "INFO"
    }
} catch {
    Write-LogAction "COM object method failed: $($_.Exception.Message)" "ERROR"
}

Write-Host $summary -ForegroundColor Green
Write-LogAction "Additional hotfix script completed!" "SUCCESS"

How to Use These Scripts

Step 1: Run the Main Script

  1. Go to your Azure VM in the portal
  2. Click "Run command" in the left sidebar
  3. Select "RunPowerShellScript"
  4. Copy and paste the entire first script above
  5. Click "Run"


  6. Wait for completion (should show all SUCCESS messages)

Step 2: Test RDP Connection

Wait 2-3 minutes, then try connecting with these settings:

  • Color depth: 16-bit (not 32-bit)
  • Disable hardware acceleration in client
  • Use lower resolution initially (1024x768)
  • Disable graphics pipeline if the option exists

Step 3: Run Additional Script (If Needed)

If you still have issues, run the second script the same way, this step was not required for my case.

Step 4: Restart VM

If problems persist, restart the entire Azure VM from the portal, this step was not required in my case.

Results from this process

After running these scripts, I achieved and noticed:

  • 100% stable RDP connections from all devices
  • No more DWM crashes or graphics driver errors
  • Perfect iOS client compatibility
  • Instant connection establishment without delays or retries
  • No more black or white screens

Why This Works?

These scripts address the specific technical issues:

  1. Network Detection Fix: Resolves the core RDP session management problems
  2. WDDM Disable: Prevents Desktop Window Manager crashes
  3. UDP Disable: Fixes mobile client compatibility issues
  4. Graphics Acceleration Disable: Stops the graphics pipeline conflicts
  5. Cache Clearing: Removes corrupted temporary files
  6. Service Restart: Applies all changes immediately

Conclusion

The key is understanding that this isn't just a simple settings issue—it's a combination of graphics driver conflicts, protocol incompatibilities, and service configuration problems that require a comprehensive fix.

With these scripts, I transformed a completely unusable RDP experience into a rock-solid remote desktop solution that works flawlessly with iOS devices and other clients.

Previous Post Next Post

نموذج الاتصال