Executive Summary
- Winget deploys the new RealVNC Server (7.x) but leaves the legacy MSI install (6.x) behind — two "VNC Server" entries in Add/Remove Programs
- Intune Proactive Remediation cleans this up automatically: detects when 2+ server installs coexist, removes the older one via msiexec, keeps the highest version
- Critical: run in 64-bit PowerShell as SYSTEM — anything else and the native HKLM Uninstall hive is hidden behind WOW6432Node, making the old install invisible
- The viewer product (RealVNC Connect / VNC Viewer) is never touched — server cleanup only
- Scripts on GitHub: Hal-Scriptville/PowerShell
The Problem
Winget is great at deploying software. It is less great at cleaning up after itself when the thing you're deploying is an MSI that installs alongside — rather than over — an existing version.
RealVNC Server is one of those products. When winget deploys the new "RealVNC Server" (7.17.0 at time of writing), the upgrade path doesn't always uninstall the legacy "VNC Server" (6.x). You end up with two entries in Add/Remove Programs on the same machine:
VNC Server 6.x.x ← old, left behind
RealVNC Server 7.17.0 ← new, just deployed by winget
Two server processes, potential port conflicts, and a mess in your inventory. Multiply across a fleet and it becomes noise you have to manually chase.
Intune Proactive Remediation is the right tool: detect the dual-install state, remediate once, confirm clean. Set it, forget it.
Why the Scripts Are Structured the Way They Are
The Registry Scope Problem
RealVNC Server is a 64-bit application that registers under the native HKLM Uninstall hive:
HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
If your PowerShell session is 32-bit (or running under WOW64), Windows transparently redirects that path to:
HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
The 64-bit RealVNC install is not visible from that redirected path. Your detection script exits 0 (compliant) on every machine, and you have no idea why remediation never fires.
The scripts query both hives explicitly and deduplicate by the registry subkey name (the PSChildName — typically the MSI ProductCode GUID). That's what makes the detection reliable regardless of how the PowerShell host was launched — though you should still set "Run script in 64-bit PowerShell" to Yes in Intune to be safe.
$uninstallKeys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)
Viewer Exclusion
RealVNC ships two separate products:
| Product | Registry DisplayName |
|---|---|
| Server | "VNC Server" (old) / "RealVNC Server" (new) |
| Viewer | "RealVNC Connect" / "VNC Viewer" |
The scripts match on *VNC Server* and explicitly exclude *Viewer* and *Connect*. The viewer is never flagged, never touched. A machine with only a viewer installed is compliant.
Version Comparison
The remediation needs to know which install to keep. It sorts on DisplayVersion using a safe parser that strips non-numeric characters before handing it to [version]::TryParse() — because RealVNC's version strings are normally clean (e.g. 7.17.0), but the parser guards against anything malformed without throwing.
Highest version wins. Everything else is removed.
Uninstall Method Priority
RealVNC is MSI-packaged, so the registry PSChildName is a ProductCode GUID like {A1B2C3D4-...}. The remediation script prefers:
- Direct msiexec
/x {ProductCode} /qn /norestart— silent, no UI, fully synchronous with-Wait QuietUninstallString— if present and msiexec path isn't detectedUninstallStringwith/Sappended — last resort
It stops the VNC Server service and related processes before running the uninstall, so the MSI isn't blocked by a running executable. After a 5-second settle, it re-queries the registry and verifies only one server install remains.
Deployment Settings
| Setting | Value |
|---|---|
| Run script in 64-bit PowerShell | Yes |
| Run this script using logged-on credentials | No (SYSTEM) |
| Detection exit 0 | Compliant (0 or 1 server install) |
| Detection exit 1 | Non-compliant (2+ server installs) |
| Remediation exit 0 | Cleanup succeeded |
| Remediation exit 1 | Old server still present after attempt |
SYSTEM context matters for two reasons: service management (Stop-Service -Force) and registry write access to Uninstall keys.
Three Things to Verify in Your Environment
The scripts were written against RealVNC 6.x → 7.17.0. Before deploying wide, confirm on a machine that has both versions installed:
1. Old version DisplayName
The detection matches *VNC Server*. This covers both "VNC Server" (old) and "RealVNC Server" (new). If RealVNC renamed the old product in your version, check Get-ItemProperty against your actual 6.x install and adjust the filter if needed.
2. Service name
The remediation stops the service named vncserver. Confirm with Get-Service *vnc* on a machine with the old version installed. If the service name differs in 6.x, update the Get-Service call.
3. MSI ProductCode
The msiexec path fires when PSChildName matches ^\{[0-9A-Fa-f-]+\}$. RealVNC 6.x is MSI-based, so this should match — but confirm the registry PSChildName is a GUID on one affected machine before trusting it at scale.
The Scripts
Both scripts are in the Hal-Scriptville/PowerShell repo under ProactiveRemediation/:
- Detection:
ProactiveRemediation/Detection/Detect-VNCServerOldVersion.ps1 - Remediation:
ProactiveRemediation/Remediation/Remediate-VNCServerOldVersion.ps1
The Broader Pattern
This pattern — winget deploys, old MSI lingers — isn't unique to RealVNC. Any product where the installer doesn't call MsiExec /x on the previous version before laying down the new one is susceptible. The general shape of this remediation (query both Uninstall hives, deduplicate by PSChildName, sort by version, remove everything below the highest) applies to any similar dual-install cleanup.
If you're doing broad winget deployments without an uninstall-prior-version step baked in, it's worth auditing your fleet for other products in this state. A quick Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* | Group-Object {$_.DisplayName -replace '\s+\d.*'} | Where-Object Count -gt 1 will surface anything with multiple versions in inventory.
Scripts tested against RealVNC Server 6.x / 7.17.0 on Windows 10/11. Adjust DisplayName filters and service names if your version strings differ. Pilot on a test group before broad deployment.
Want more patterns like this?
Get the full 6-part guide — what Intune doesn't tell you, but you'll hit in production.
Running into this in production? 30 minutes — I will tell you directly if it is worth fixing.
Book a Fit Call →