Sources/RemoveApps.ps1

# ***************************************************************************
#
# File: RemoveApps.ps1
#
# Version: 1.3
#
# Author: Michael Niehaus
#
# Purpose: Removes some or all of the in-box apps on Windows 8, Windows 8.1,
# or Windows 10 systems. The script supports both offline and
# online removal. By default it will remove all apps, but you can
# provide a separate RemoveApps.xml file with a list of apps that
# you want to instead remove. If this file doesn't exist, the
# script will recreate one in the log or temp folder, so you can
# run the script once, grab the file, make whatever changes you
# want, then put the file alongside the script and it will remove
# only the apps you specified.
#
# Usage: This script can be added into any MDT or ConfigMgr task sequences.
# It has a few dependencies:
# 1. For offline use in Windows PE, the .NET Framework,
# PowerShell, DISM Cmdlets, and Storage cmdlets must be
# included in the boot image.
# 2. Script execution must be enabled, e.g. "Set-ExecutionPolicy
# Bypass". This can be done via a separate task sequence
# step if needed, see http://blogs.technet.com/mniehaus for
# more information.
#
# ------------- DISCLAIMER -------------------------------------------------
# This script code is provided as is with no guarantee or waranty concerning
# the usability or impact on systems and may be used, distributed, and
# modified in any way provided the parties agree and acknowledge the
# Microsoft or Microsoft Partners have neither accountabilty or
# responsibility for results produced by use of this script.
#
# Microsoft will not provide any support through any means.
# ------------- DISCLAIMER -------------------------------------------------
#
# ***************************************************************************


# ---------------------------------------------------------------------------
# Initialization
# ---------------------------------------------------------------------------

if ($env:SYSTEMDRIVE -eq "X:")
{
  $script:Offline = $true

  # Find Windows
  $drives = get-volume | ? {-not [String]::IsNullOrWhiteSpace($_.DriveLetter) } | ? {$_.DriveType -eq 'Fixed'} | ? {$_.DriveLetter -ne 'X'}
  $drives | ? { Test-Path "$($_.DriveLetter):\Windows\System32"} | % { $script:OfflinePath = "$($_.DriveLetter):\" }
  Write-Verbose "Eligible offline drive found: $script:OfflinePath"
}
else
{
  Write-Verbose "Running in the full OS."
  $script:Offline = $false
}


# ---------------------------------------------------------------------------
# Get-LogDir: Return the location for logs and output files
# ---------------------------------------------------------------------------

function Get-LogDir
{
  try
  {
    $ts = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction Stop
    if ($ts.Value("LogPath") -ne "")
    {
      $logDir = $ts.Value("LogPath")
    }
    else
    {
      $logDir = $ts.Value("_SMSTSLogPath")
    }
  }
  catch
  {
    $logDir = $env:TEMP
  }
  return $logDir
}

# ---------------------------------------------------------------------------
# Get-AppList: Return the list of apps to be removed
# ---------------------------------------------------------------------------

function Get-AppList
{
  begin
  {
    # Look for a config file.
    $winVer = (Get-CimInstance Win32_OperatingSystem).Version
    if ($winVer -like '6.3.*') {
        $configFile = "$PSScriptRoot\RemoveApps81.xml"
    }
    elseif ($winVer -like '10.0.*') {
        $configFile = "$PSScriptRoot\RemoveApps10.xml"
    }

    if (Test-Path -Path $configFile)
    {
      # Read the list
      Write-Verbose "Reading list of apps from $configFile"
      $list = Get-Content $configFile
    }
    else
    {
      # No list? Build one with all apps.
      Write-Verbose "Building list of provisioned apps"
      $list = @()
      if ($script:Offline)
      {
        Get-AppxProvisionedPackage -Path $script:OfflinePath | % { $list += $_.DisplayName }
      }
      else
      {
        Get-AppxProvisionedPackage -Online | % { $list += $_.DisplayName }
      }

      # Write the list to the log path
      $logDir = Get-LogDir
      $configFile = "$logDir\RemoveApps.xml"
      $list | Set-Content $configFile
      Write-Output "Wrote list of apps to $logDir\RemoveApps.xml, edit and place in the same folder as the script to use that list for future script executions"
    }

    Write-Output "Apps selected for removal: $list.Count"
  }

  process
  {
    $list
  }

}

# ---------------------------------------------------------------------------
# Remove-App: Remove the specified app (online or offline)
# ---------------------------------------------------------------------------

function Remove-App
{
  [CmdletBinding()]
  param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [string] $appName
  )

  begin
  {
    # Determine offline or online
    if ($script:Offline)
    {
      $script:Provisioned = Get-AppxProvisionedPackage -Path $script:OfflinePath
    }
    else
    {
      $script:Provisioned = Get-AppxProvisionedPackage -Online
      $script:AppxPackages = Get-AppxPackage
    }
  }

  process
  {
    $app = $_

    # Remove the provisioned package
    Write-Output "Removing provisioned package $_"
    $current = $script:Provisioned | ? { $_.DisplayName -eq $app }
    if ($current)
    {
      if ($script:Offline)
      {
        $a = Remove-AppxProvisionedPackage -Path $script:OfflinePath -PackageName $current.PackageName
      }
      else
      {
        $a = Remove-AppxProvisionedPackage -Online -PackageName $current.PackageName
      }
    }
    else
    {
      Write-Warning "Unable to find provisioned package $_"
    }

    # If online, remove installed apps too
    if (-not $script:Offline)
    {
      Write-Output "Removing installed package $_"
      $current = $script:AppxPackages | ? {$_.Name -eq $app }
      if ($current)
      {
        $current | Remove-AppxPackage
      }
      else
      {
        Write-Warning "Unable to find installed app $_"
      }
    }

  }
}


# ---------------------------------------------------------------------------
# Main logic
# ---------------------------------------------------------------------------

$logDir = Get-LogDir
Start-Transcript "$logDir\RemoveApps.log"

Get-AppList | Remove-App

Stop-Transcript