It is no secret that Windows 10 has a nasty habit of installing software that we do not want installed in our environments, and this seems to be something that Microsoft has no intention of changing in the near future. The only “easy” solution is to get your hands on Enterprise LTSB, but then you do not get feature updates. Anywho, presented with this issue and the fact that my customers only pay for Windows 10 Pro (shocker) I came up with a PowerShell script to only keep the apps that I dictate installed, with a little help from the Obi-Wan you all hear about so frequently.
A quick run-down for those who are not yet familiar with Windows 10 there are two types of packages that we are concerned with today; AppxPackages and AppxProvisionedPackages. AppxPackages are the packages of the currently installed Microsoft apps for a particular Windows User Profile on the machine. AppxProvisionedPackages are the pesky devils that install the apps upon new user profile creation on the machine. Naturally, the latter are the ones we are most concerned with, but we want to ditch any of the AppxPackages that may linger on.
Also, I have included a quick check to make sure the PowerShell script will only run on Windows 10 1703 or lower. This way I have time to test on the next version and make sure this script doesn’t screw anything up before I allow it to run on client machines.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
################## #Config Variables# ################## $strLogFile = "C:\Automated\Logs\NukeW10Apps.log" $boolWinVersionMajor = [environment]::OSVersion.Version.Major.Equals(10) #Calls Windows Major value. Windows 10 returns $true, any other returns $false $intWinVersionBuild = [environment]::OSVersion.Version.Build #Calls Windows build number (ex. Windows 10 version 1703 build number is 15063) ######## #Arrays# ######## $arrAppxPackages = Get-AppxPackage | Select Name #Array of all AppxPackage names $arrProvisionedPackages = Get-AppxProvisionedPackage -Online | Select PackageName #Array of all AppxProvisionedPackage names $arrAppxBlacklist = @("twitter", "candycrushsodasaga", "microsoftsolitairecollection") #Array of AppxPackages to be removed first #Array of AppxPackages to keep $arrAppxPackageWL = @("Microsoft.BioEnrollment", "Microsoft.AAD.BrokerPlugin", "Microsoft.Windows.CloudExperienceHost", "windows.immersivecontrolpanel", "Microsoft.LockApp", "Microsoft.MicrosoftEdge", "Microsoft.PPIProjection", "Microsoft.Windows.Apprep.ChxApp", "Microsoft.Windows.AssignedAccessLockApp", "Microsoft.Windows.ContentDeliveryManager", "Microsoft.Windows.ParentalControls", "Microsoft.Windows.SecondaryTileExperience", "Microsoft.Windows.SecureAssessmentBrowser", "Windows.MiracastView", "Windows.PrintDialog", "Microsoft.DesktopAppInstaller", "Microsoft.NET.Native.Runtime.1.3", "Microsoft.Windows.Photos", "Microsoft.NET.Native.Framework.1.3", "Microsoft.VCLibs.140.00", "Microsoft.NET.Native.Runtime.1.4", "Microsoft.WindowsCalculator", "Microsoft.VCLibs.120.00", "Microsoft.Windows.ShellExperienceHost", "Microsoft.NET.Native.Runtime.1.1", "Microsoft.NET.Native.Framework.1.2", "Microsoft.XboxGameCallableUI", "Windows.ContactSupport", "Microsoft.Windows.Cortana", "Microsoft.AccountsControl") #Array of AppxProvisionedPackages to keep $arrProvisionedPackageWL = @("Microsoft.DesktopAppInstaller", "Microsoft.WindowsAlarms", "Microsoft.WindowsCamera", "Microsoft.Windows.Photos", "Microsoft.WindowsStore", "Microsoft.WindowsCalculator") ########### #Functions# ########### #Export string to log function WriteToLog { Param([string]$strLogString) Add-Content $strLogFile -Value $strLogString } #Compare AppxBlacklist array against another array and return $true if there is a match function RemoveBlacklist($tmpBlacklistName) { foreach($strBlacklistApp in $arrAppxBlacklist) { $strSelect = Select-String -InputObject $tmpBlacklistName.ToLower() -Pattern $strBlacklistApp.ToLower() if($strSelect -ne $null) { return $true } } return $false } #Compare AppxPackageWL array against another array and return $false if there is a match function RemoveAppxPackage($tmpAppxName) { foreach($strWLAppxPackage in $arrAppxPackageWL) { if($strWLAppxPackage -eq $tmpAppxName) { return $false } } return $true } #Compare ProvisionedPackageWL array against another array and return $false if there is a match function RemoveProvisionedPackage($tmpProvisionedName) { foreach($strWLProvisionedPackage in $arrProvisionedPackageWL) { if($strWLProvisionedPackage -eq $tmpProvisionedName) { return $false } } return $true } ################ #Let's do stuff# ################ if($boolWinVersionMajor -eq $true) #Only continue if Windows major version equals 10 { if($intWinVersionBuild -le 15063) #Only continue if Windows is less or equal to build 15063 (version 1703...aka Creator) { #Blacklist Packages foreach($strBlacklistItem in $arrAppxPackages) { if((RemoveBlackList -tmpBlacklistName $strBlacklistItem.Name) -eq $true) { WriteToLog "Remove Blacklist AppxPackage", $strBlacklistItem.Name #Write removed package names to log Get-AppxPackage $strBlacklistItem.Name | Remove-AppxPackage #Remove the AppxPackage } } #AppxProvisionedPackages foreach($strProvisionedPackage in $arrProvisionedPackages) { $strProvisionedSplit = $strProvisionedPackage.PackageName.Split("_") #Split PackageName into array if((RemoveProvisionedPackage -tmpProvisionedName $strProvisionedSplit[0]) -eq $true) { WriteToLog "Remove ProvisionedPackage", $strProvisionedPackage.PackageName #Write removed package names to log Remove-AppxProvisionedPackage -Online -PackageName $strProvisionedPackage.PackageName #Removes the ProvisionedPackage } } #AppxPackages foreach($strAppxPackage in $arrAppxPackages) { if((RemoveAppxPackage -tmpAppxName $strAppxPackage.Name) -eq $true) { WriteToLog "Remove AppxPackage", $strAppxPackage.Name #Write removed package names to log Get-AppxPackage $strAppxPackage.Name | Remove-AppxPackage #Removes the AppxPackage } } } } |
When it comes to deployment I push with GPO and run the script at computer startup, but I run a batch script that copies the .ps1 file down to a restricted folder on the local machine first. I do this because this script needs to run as system to be fully effective. I also add the following arguments when running the script to hide the PowerShell window while it’s running.
1 |
-WindowStyle Hidden -NoProfile -File <FilePath> |