# Easy removal of preinstalled bloatware using PowerShell

Recently I came into a situation where I needed to provision new Dell Precision laptops for our employees. This laptop comes with a lot of preinstalled applications (bloatware) so I was looking for some **simple** but **efficient** way to **silently** remove them. And by **efficient** I mean avoiding the need to manually find an uninstall command for each preinstalled application.

As always PowerShell and registry property `QuietUninstallString` were my saviors 😉.

---

# TL;DR
Use function `Get-InstalledSoftware` and `Uninstall-ApplicationViaUninstallString` functions from my [CommonStuff](https://www.powershellgallery.com/packages/CommonStuff) PS module like 👇
```
# Uninstall every application that has 'Dell' in its name.
Get-InstalledSoftware -appName Dell | Uninstall-ApplicationViaUninstallString
```

# Find silent uninstall command the smart way
As you probably know, a list of installed applications is stored in two registry keys: `HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`, `HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall`.
What you might not know is that almost every application that is stored there has defined properties `QuietUninstallString` and/or `UninstallString`. Applications installed via EXE have typically both these properties but MSI have just `UninstallString`.

EXE installed application 👇
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1661778300693/lzuzbIzI5.png align="left")

MSI installed application 👇
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1661778502971/OVMRMWFfI.png align="left")

# Drawbacks of this solution
- Sometimes for MSI installed applications `UninstallString` property contains `/I` instead of `/X` parameter (`/I` means 'install' and `/X` means 'uninstall')
- Some EXE application (like "Dell Optimizer Service") has the `QuietUninstallString` property but when used, it normally runs uninstall wizard GUI. Hence **there is no guarantee that `QuietUninstallString` will really start silent uninstallation** 😞.
  - To help with that I've added the parameter `addArgument` to the `Uninstall-ApplicationViaUninstallString` function (in the case of the "Dell Optimizer Service" application mentioned before I've added `-silent` parameter to the uninstall command).

---

# Summary
With the information above in mind, I've created two functions that work together: `Uninstall-ApplicationViaUninstallString` and `Get-InstalledSoftware`. Both of them can be found in my [CommonStuff](https://www.powershellgallery.com/packages/CommonStuff) PS module and be used like this 👇.
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1661780105049/cEILv5pM6.png align="left")

Both also support TAB-completion, so it is super easy to get the app name.
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1661784248862/pqZ46tE5r.png align="left")


Just in case you are curious, my final solution for the Dell Precision laptops looks like this:
```
$SWName = Get-InstalledSoftware "Dell", "Microsoft Update Health Tools", "ExpressConnect Drivers & Services" | ? DisplayName -NotLike "Dell Command | Update for Windows*" | select -ExpandProperty DisplayName

if ($SWName) {
    try {
        $SWName | % {
            $param = @{
                Name        = $_
                ErrorAction = "Stop"
            }

            if ($_ -eq "Dell Optimizer Service") {
                # uninstallation isn't unattended without -silent switch
                $param.addArgument = "-silent"
            }

            Uninstall-ApplicationViaUninstallString @param
        }
    } catch {
        Write-Error "There was an error when uninstalling bloatware: $_"
    }
} else {
    "There is no bloatware detected"
}
```



