<# .SYNOPSIS #> # DISCLAIMER: # THIS SCRIPT IS WORK IN PROGRESS AND HAS NOT UNDERGONE VALIDATION TESTING. # FOR USE BY =S= SUPPORT PERSONNEL ONLY, NOT FOR DISTRIBUTION. # # To only check what needs to be cleaned up: # Copy this file to the desktop, then right-click it and select "Run with PowerShell". # # To really clean up: # Copy this file to the desktop, then copy/paste the following into an Admin PowerShell: # Set-ExecutionPolicy -Force -Scope Process Bypass; & "$home\Desktop\Release-PerUserDrivers.ps1" -NoWhatIf [CmdletBinding(PositionalBinding = $false)] Param ( # Process drivers installed for the currently logged-on user in addition to those installed for other users [Alias("u")] [switch]$IncludeCurrentUser, # Process 14.14+ drivers (installed per-machine) in addition to the older ones (installed per-user) [Alias("m")] [switch]$IncludePerMachine, # Turn on logging [switch]$Logging, # By default the script runs as if -WhatIf was set; -NoWhatIf overrides this default [switch]$NoWhatIf ) # Running a script from the file's "Run with PowerShell" context menu item executes this command (PS versions 5-7): # if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & '%1' $RunFromExplorer = $MyInvocation.Line -like "*'AllSigned'*" $CurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $Elevated = (New-Object Security.Principal.WindowsPrincipal $CurrentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) if ($RunFromExplorer) { # Include everything $IncludeCurrentUser = $true $IncludePerMachine = $true $Logging = $true } elseif (-not $Elevated) { # Force off for non-admins to prevent "access denied" exceptions $NoWhatIf = $false } # $Whatif is more idiomatic and also improves readability of conditions later in code $WhatIf = -not $NoWhatIf # Log to user's desktop if ($Logging) { $LogName = [IO.Path]::ChangeExtension($MyInvocation.MyCommand.Name, ".log") $LogPath = Join-Path $([Environment]::GetFolderPath("Desktop")) $LogName Start-Transcript -Path $LogPath } "Running `"$($MyInvocation.Line)`", started $(Get-Date -Format 'yyyy-MM-dd HH:mm K')" systeminfo /fo csv | ConvertFrom-Csv | Format-List # input: "ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ123456" # output: "HGFEDCBALKJIPONMRQTSVUXWZY214365" function ScrambleGuid { param ($guid) $scrambled = "" $parts = $guid.Split('-') # "ABCDEFGH-IJKL-MNOP" -> "HGFEDCBALKJIPONM" for ($i = 0; $i -lt 3; ++$i) { $charArray = $parts[$i].ToCharArray() [array]::Reverse($charArray) $scrambled += -join($charArray) } # "QRST-UVWXYZ123456" -> "RQTSVUXWZY214365" for ($i = 3; $i -lt 5; ++$i) { $part = $parts[$i] for ($j = 0; $j -lt $part.Length; $j += 2) { $scrambled += $part[$j + 1] + $part[$j] } } return $scrambled } # Product names (predefined - do not change) $DriverManager32 = "Driver Manager" $DriverManager64 = "DriverManager x64" $ModbusDriver32 = "Modbus Driver" $ModbusDriver64 = "Modbus Driver X64" $UnitelwayDriver32 = "Unitelway Driver" $UnitelwayDriver64 = "Unitelway Driver X64" $UsbDriver32 = "PLC Usb Driver" $UsbDriver64 = "PLCUsb Driver X64" $XipDriver32 = "Xip Driver" $XipDriver64 = "Xip Driver X64" # Setup reference files. These are used by the suite setup to determine # whether or not the installed version is older than the one to be installed. $ReferenceFiles = @{} $ReferenceFiles[$DriverManager32] = $ReferenceFiles[$DriverManager64] = "NA_Service.exe", "XWAYMgr.cpl" $ReferenceFiles[$ModbusDriver32] = $ReferenceFiles[$ModbusDriver64] = "ModbusDrvSys.exe" $ReferenceFiles[$UnitelwayDriver32] = $ReferenceFiles[$UnitelwayDriver64] = "Unitelw.dll" $ReferenceFiles[$UsbDriver32] = $ReferenceFiles[$UsbDriver64] = "UsbConnect.exe" $ReferenceFiles[$XipDriver32] = $ReferenceFiles[$XipDriver64] = "XipDrvSys.exe" $ProductNames = $ReferenceFiles.Keys # See ProductVersion in the respective .ism files $SuiteVersionsByDriverVersion = @{ $DriverManager32 = @{ "003.030.00000" = "14.15.1" # April 2021 "003.029.00000" = "14.15" # June 2020 "003.028.00000" = "14.14" # November 2019 "003.027.00000" = "14.13" # February 2019 "003.026.00000" = "14.12 or 14.12.1" # May 2018 "003.025.00000" = "14.11" # December 2017 "003.024.00000" = "14.10" # July 2017 "003.023.00000" = "14.9" # January 2017 "003.022.00000" = "14.7 or 14.8" # September/October 2016 "003.021.00000" = "14.6" # July 2016 "003.020.00000" = "14.5" # May 2016 "003.019.00000" = "14.0" # July 2013 } $DriverManager64 = @{ "004.030.00000" = "14.15.1" "004.029.00000" = "14.15" "004.028.00000" = "14.14" "004.027.00000" = "14.13" "004.026.00000" = "14.12 or 14.12.1" "004.025.00000" = "14.11" "004.024.00000" = "14.10" "004.023.00000" = "14.9" "004.022.00000" = "14.7 or 14.8" "004.021.00000" = "14.6" "004.020.00000" = "14.5" "004.019.00000" = "14.0" } $ModbusDriver32 = @{ "002.031.00000" = "14.15.1" "002.030.00000" = "14.15" "002.029.00000" = "14.14" "002.028.00000" = "14.13" "002.027.00000" = "14.12.1" "002.026.00000" = "14.12" "002.025.00000" = "14.11" "002.024.00000" = "14.10" "002.023.00000" = "14.9" "002.022.00000" = "14.6 to 14.8" "002.021.00000" = "14.5" "002.020.00000" = "14.0" } $ModbusDriver64 = @{ "003.031.00000" = "14.15.1" "003.030.00000" = "14.15" "003.029.00000" = "14.14" "003.028.00000" = "14.13" "003.027.00000" = "14.12.1" "003.026.00000" = "14.12" "003.025.00000" = "14.11" "003.024.00000" = "14.10" "003.023.00000" = "14.9" "003.022.00000" = "14.6 to 14.8" "003.021.00000" = "14.5" "003.020.00000" = "14.0" } $UnitelwayDriver32 = @{ "002.025.00000" = "14.15" "002.024.00000" = "14.14" "002.023.00000" = "14.13" "002.022.00000" = "14.12 or 14.12.1" "002.021.00000" = "14.11" "002.020.00000" = "14.10" "002.019.00000" = "14.9" "002.018.00000" = "14.6 to 14.8" "002.017.00000" = "14.5" "002.016.00000" = "14.0" } $UnitelwayDriver64 = @{ "003.025.00000" = "14.15" "003.024.00000" = "14.14" "003.023.00000" = "14.13" "003.022.00000" = "14.12 or 14.12.1" "003.021.00000" = "14.11" "003.020.00000" = "14.10" "003.019.00000" = "14.9" "003.018.00000" = "14.6 to 14.8" "003.017.00000" = "14.5" "003.016.00000" = "14.0" } $UsbDriver32 = @{ "002.028.00000" = "14.15" "002.027.00000" = "14.14" "002.026.00000" = "14.13" "002.025.00000" = "14.12" "002.024.00000" = "14.11" "002.023.00000" = "14.10" "002.022.00000" = "14.9" "002.021.00000" = "14.8" "002.020.00000" = "14.7" "002.019.00000" = "14.6" "002.018.00000" = "14.5" "002.017.00000" = "14.0" } $UsbDriver64 = @{ "003.028.00000" = "14.15" "003.027.00000" = "14.14" "003.026.00000" = "14.13" "003.025.00000" = "14.12" "003.024.00000" = "14.11" "003.023.00000" = "14.10" "003.022.00000" = "14.9" "003.021.00000" = "14.8" "003.020.00000" = "14.7" "003.019.00000" = "14.6" "003.018.00000" = "14.5" "003.017.00000" = "14.0" } $XipDriver32 = @{ "002.026.00000" = "14.15.1" "002.025.00000" = "14.15" "002.024.00000" = "14.14" "002.023.00000" = "14.13" "002.022.00000" = "14.12" "002.021.00000" = "14.10 or 14.11" "002.020.00000" = "14.9" "002.019.00000" = "14.6 to 14.8" "002.018.00000" = "14.5" "002.017.00000" = "14.0" } $XipDriver64 = @{ "003.026.00000" = "14.15.1" "003.025.00000" = "14.15" "003.024.00000" = "14.14" "003.023.00000" = "14.13" "003.022.00000" = "14.12" "003.021.00000" = "14.10 or 14.11" "003.020.00000" = "14.9" "003.019.00000" = "14.6 to 14.8" "003.018.00000" = "14.5" "003.017.00000" = "14.0" } } $ProductCodes = @{ $DriverManager32 = "F1365987-E481-441D-9AFF-2915325128B6" $DriverManager64 = "4201BA84-EC1B-4B75-A1EF-E77D3E958465" $ModbusDriver32 = "B01881D0-6709-4F46-A8AC-53CE8EED4C6A" $ModbusDriver64 = "708DB6DF-6949-46FA-AADD-752937B0FC72" $UnitelwayDriver32 = "3CEB7206-AFB6-4838-B8C9-012E7B67F708" $UnitelwayDriver64 = "889874D3-81AB-4728-ABA5-BF928B9A6963" $UsbDriver32 = "CA29A899-7713-4B15-801D-15C6833BC00B" $UsbDriver64 = "771C6F2F-BCE2-4773-BA33-21B31B14F57B" $XipDriver32 = "358BB4A7-3734-4173-AEB1-858BDEA892D3" $XipDriver64 = "2F6C6C4C-85CB-49B3-9AE2-974DFBC4145D" } $ScrambledProductCodes = @{} $ProductCodes.Keys | ForEach-Object { $ScrambledProductCodes[$_] = ScrambleGuid($ProductCodes[$_]) } $UpgradeCodes = @{ $DriverManager32 = "A20B01E2-0B8D-41FF-9C77-B22AE44634F6" $DriverManager64 = "6C11AB26-4709-4A03-A374-607963A81218" $ModbusDriver32 = "8EF6C4E0-002E-4334-BC81-FF7BD805F487" $ModbusDriver64 = "4AAA104C-4028-40D6-934E-DCA46E78EA48" $UnitelwayDriver32 = "554F8A8C-6106-4FBC-B861-A5144F98B66E" $UnitelwayDriver64 = "2D9B4BA1-475D-47B4-93A5-7910B9BA9824" $UsbDriver32 = "E7985CC1-8199-461D-B4D5-85B8FE4C75B7" $UsbDriver64 = "F75AFF09-85F7-4C89-ABDE-18C0D8EFE8FB" $XipDriver32 = "ACF7B986-BDFF-4845-86E6-CCA465E72A11" $XipDriver64 = "FFD5C1D6-7DA9-4D78-9A6D-C1EDD1079287" } $ScrambledUpgradeCodes = @{} $UpgradeCodes.Keys | ForEach-Object { $ScrambledUpgradeCodes[$_] = ScrambleGuid($UpgradeCodes[$_]) } $CurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $CurrentUserSidString = $CurrentUser.User.Value "Current user is $($CurrentUser.Name) ($CurrentUserSidString) on $env:COMPUTERNAME" # Stop services "`nChecking services" "NA_Service","UsbConnect" | ForEach-Object { Get-Service $_ -ErrorAction SilentlyContinue | ForEach-Object { if ( $_.Status -eq "Running") { if ($WhatIf) { " detected service '$($_.DisplayName)' ($($_.ServiceName)) is running" } else { " stopping service '$($_.DisplayName)' ($($_.ServiceName))" Stop-Service $_ } } else { " detected service '$($_.DisplayName)' ($($_.ServiceName)) in state $($_.Status)" } } } $NothingFound = $true # Delete Windows Installer registry keys # Get user keys to search, skipping LocalSystem and the user who runs the setup $UserDataKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData" $KeyPathLen = $UserDataKey.Length + 1 "`nChecking registry at $UserDataKey`n" $LocalSystemSidString = "S-1-5-18" if (Test-Path "Registry::\$UserDataKey\") { $UserKeys = Get-ChildItem "Registry::\$UserDataKey" -Name | Where-Object { (($_ -ne $CurrentUserSidString) -or $IncludeCurrentUser) -and (($_ -ne $LocalSystemSidString) -or $IncludePerMachine) } } # Search for installed product and delete respective keys (may be installed for more than one user) # and setup reference files $UserKeys | ForEach-Object { $SidString = $_ $ProductsKey = "$UserDataKey\$SidString\Products" if (Test-Path "Registry::\$ProductsKey") { # if there are any installed products at all # Check if InstallProperties subkey of any product has a DisplayName that matches our product name Get-ChildItem "Registry::\$ProductsKey" | ForEach-Object { $InstallProperties = $_.OpenSubKey("InstallProperties") if ($InstallProperties -ne $null) { $DisplayName = $InstallProperties.GetValue("DisplayName", $null) $DisplayVersion = $InstallProperties.GetValue("DisplayVersion", $null) $InstallDate = $InstallProperties.GetValue("InstallDate", $null) $InstallDate = $InstallDate.Substring(0, 4) + "-" + $InstallDate.Substring(4, 2) + "-" + $InstallDate.Substring(6, 2) $InstallProperties.Close() if ($DisplayName -in $ProductNames) { # if we've found one of our products $NothingFound = $false # Get owner and version for display if ($SidString -ne $LocalSystemSidString) { $ProductOwnerSid = New-Object Security.Principal.SecurityIdentifier("$SidString") try { $ProductOwnerName = "user " + $ProductOwnerSid.Translate([Security.Principal.NTAccount]).Value } catch [Security.Principal.IdentityNotMappedException] { $ProductOwnerName = "deleted user account $SidString" } } else { $ProductOwnerName = "all users" } $SuiteVersion = $SuiteVersionsByDriverVersion[$DisplayName][$DisplayVersion] if ([string]::IsNullOrEmpty($SuiteVersion)) { $SuiteVersion = "older than 14.0 from July 2013" } "$DisplayName $DisplayVersion ($SuiteVersion) was installed on $InstallDate for $ProductOwnerName" # Delete key $KeyName = ([string]$_).Remove(0, $KeyPathLen) if ($WhatIf) { " detected Windows Installer registry key $KeyName" } else { " deleting Windows Installer registry key $KeyName" Remove-Item "Registry::\$_" -Recurse } # Delete setup reference files $ReferenceFiles[$DisplayName] | ForEach-Object { "$env:SystemRoot\System32\$_" } | Where-Object { Test-Path $_ } | ForEach-Object { $file = Get-Item $_ $date = $file.LastWriteTime.ToString("yyyy-MM-dd") $version = "$($file.VersionInfo.ProductMajorPart).$($file.VersionInfo.ProductMinorPart) IE$($file.VersionInfo.ProductBuildPart)" if ($WhatIf) { " detected setup reference file $_ $version $date" } else { " deleting setup reference file $_ $version $date" Remove-Item $_ } } "" } } } } $ComponentsKey = "$UserDataKey\$SidString\Components" $KeyName = $ComponentsKey.Remove(0, $KeyPathLen) "Checking installed components at $KeyName" if (Test-Path "Registry::\$ComponentsKey") { # if there are any installed components at all Get-ChildItem "Registry::\$ComponentsKey" | ForEach-Object { $key = $_ $KeyName = $_.PSChildName $component = $_.Property[0] $ScrambledProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $component } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key $KeyName" } else { " deleting $($_.Name) key $KeyName" Remove-Item "Registry::\$key" -Recurse } } } } } $UpgradeCodesKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" "`nChecking upgrade codes at $UpgradeCodesKey" Get-ChildItem "Registry::\$UpgradeCodesKey" | ForEach-Object { $key = $_ $upgradeCode = $_.PSChildName $ScrambledUpgradeCodes.GetEnumerator() | Where-Object { $_.Value -eq $upgradeCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key $upgradeCode" } else { " deleting $($_.Name) key $upgradeCode" Remove-Item "Registry::\$key" -Recurse } } } $UninstallKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "`nChecking uninstall entries at $UninstallKey" Get-ChildItem "Registry::\$UninstallKey" | ForEach-Object { $key = $_ $productCode = $_.PSChildName $productCode = $productCode.Substring(1, $productCode.Length - 2) $ProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $productCode } | ForEach-Object { if ($WhatIf) { " detected $($_.Key) key {$productCode}" } else { " deleting $($_.Key) key {$productCode}" Remove-Item "Registry::\$key" -Recurse } } } if ($IncludePerMachine) { $MachineInstallerKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer" "`nChecking Windows Installer entries at $MachineInstallerKey" Get-ChildItem "Registry::\$MachineInstallerKey\Products" | ForEach-Object { $key = $_ $productCode = $_.PSChildName $ScrambledProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $productCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key Products\$productCode" } else { " deleting $($_.Name) key Products\$productCode" Remove-Item "Registry::\$key" -Recurse } } } Get-ChildItem "Registry::\$MachineInstallerKey\Features" | ForEach-Object { $key = $_ $featureCode = $_.PSChildName $ScrambledProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $featureCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key Features\$featureCode" } else { " deleting $($_.Name) key Features\$featureCode" Remove-Item "Registry::\$key" -Recurse } } } Get-ChildItem "Registry::\$MachineInstallerKey\UpgradeCodes" | ForEach-Object { $key = $_ $upgradeCode = $_.PSChildName $ScrambledUpgradeCodes.GetEnumerator() | Where-Object { $_.Value -eq $upgradeCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key UpgradeCodes\$upgradeCode" } else { " deleting $($_.Name) key UpgradeCodes\$upgradeCode" Remove-Item "Registry::\$key" -Recurse } } } } if ($IncludeCurrentUser) { $UserInstallerKey = "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Installer" if (Test-Path "Registry::\$UserInstallerKey\") { "`nChecking Windows Installer entries at $UserInstallerKey" Get-ChildItem "Registry::\$UserInstallerKey\Products" | ForEach-Object { $key = $_ $productCode = $_.PSChildName $ScrambledProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $productCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key Products\$productCode" } else { " deleting $($_.Name) key Products\$productCode" Remove-Item "Registry::\$key" -Recurse } } } Get-ChildItem "Registry::\$UserInstallerKey\Features" | ForEach-Object { $key = $_ $featureCode = $_.PSChildName $ScrambledProductCodes.GetEnumerator() | Where-Object { $_.Value -eq $featureCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key Features\$featureCode" } else { " deleting $($_.Name) key Features\$featureCode" Remove-Item "Registry::\$key" -Recurse } } } Get-ChildItem "Registry::\$UserInstallerKey\UpgradeCodes" | ForEach-Object { $key = $_ $upgradeCode = $_.PSChildName $ScrambledUpgradeCodes.GetEnumerator() | Where-Object { $_.Value -eq $upgradeCode } | ForEach-Object { $NothingFound = $false if ($WhatIf) { " detected $($_.Name) key UpgradeCodes\$upgradeCode" } else { " deleting $($_.Name) key UpgradeCodes\$upgradeCode" Remove-Item "Registry::\$key" -Recurse } } } } } if ($NothingFound) { "`nNo drivers installed by another user were detected." } elseif (!$WhatIf) { "`nPlease re-install ALL drivers listed above now." } "" if ($Logging) { Stop-Transcript } if ($RunFromExplorer) { Write-Host -NoNewline "`nPress Enter to close window" Read-Host }