Remove-MS-Store-Apps.ps1

<#PSScriptInfo
 
.VERSION 22.03.27
 
.GUID 888f5987-8b64-4a4a-ab8e-00a1bc99ff54
 
.AUTHOR Mike Galvin Contact: mike@gal.vin / twitter.com/mikegalvin_ / discord.gg/5ZsnJ5k
 
.COMPANYNAME Mike Galvin
 
.COPYRIGHT (C) Mike Galvin. All rights reserved.
 
.TAGS Remove Clean up Microsoft Store Windows UWP in-box built-in included app Windows 10 11 Customisable removal utility
 
.LICENSEURI
 
.PROJECTURI https://gal.vin/utils/remove-ms-store-apps-utility/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
#>


<#
    .SYNOPSIS
    Remove MS Store Apps - Customisable Windows 10/11 Microsoft Store app removal utility
 
    .DESCRIPTION
    Removes apps included in Windows 10/11
 
    To retrieve the names of the apps available to all users, run the following command in an elevated PowerShell session:
 
    Get-AppxProvisionedPackage -Online | Select Displayname
 
    To retrieve the names of the apps available to only the current user, run the following command in a PowerShell session:
    Get-AppxPackage | Select Name
 
    .PARAMETER List
    The full path to the txt file listing the apps to remove.
 
    .PARAMETER Wim
    The full path to the wim file to remove the apps from.
 
    .PARAMETER WimIndex
    The index number of the image to operate on.
    Run the following command to find out what images are present in the wim file:
    Get-WindowsImage -ImagePath "C:\foo\Windows 10\sources\install.wim" | Format-Table -Property ImageIndex, ImageName
 
    .PARAMETER WimMountPath
    The full path to a folder that the wim file should be mounted to.
 
    .PARAMETER NoBanner
    Use this option to hide the ASCII art title in the console.
 
    .PARAMETER L
    The path to output the log file to.
    The file name will be Remove-MS-Store-Apps_YYYY-MM-dd_HH-mm-ss.log
    Do not add a trailing \ backslash.
 
    .EXAMPLE
    Remove-MS-Store-Apps.ps1 -List C:\scripts\w10-apps-2004.txt -L C:\scripts\logs
 
    The above command will remove the apps in the specified text file from the running system for all users, and will generate a log file.
#>


## Set up command line switches.
[CmdletBinding()]
Param(
    [parameter(Mandatory=$True)]
    [alias("List")]
    $AppListFile,
    [alias("Wim")]
    $WimFile,
    [alias("WimIndex")]
    $WIndex,
    [alias("WimMountPath")]
    [ValidateScript({Test-Path $_ -PathType 'Container'})]
    $WimMntPath,
    [alias("L")]
    $LogPath,
    [switch]$NoBanner)

If ($NoBanner -eq $False)
{
    Write-Host -Object ""
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " _____ __ __ _____ _____ _ "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " | __ \ | \/ |/ ____| / ____| | "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " | |__) |___ _ __ ___ _____ _____ | \ / | (___ | (___ | |_ ___ _ __ ___ "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " | _ // _ \ '_ ' _ \ / _ \ \ / / _ \ | |\/| |\___ \ \___ \| __/ _ \| '__/ _ \ "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " | | \ \ __/ | | | | | (_) \ V / __/ | | | |____) | ____) | || (_) | | | __/ "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " |_| \_\___|_| |_| |_|\___/ \_/ \___| |_|_ |_|_____/ |_____/ \__\___/|_| \___| "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " /\ | | | | | (_) (_) | "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " / \ _ __ _ __ ___ | | | | |_ _| |_| |_ _ _ "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " / /\ \ | '_ \| '_ \/ __| | | | | __| | | | __| | | | "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " / ____ \| |_) | |_) \__ \ | |__| | |_| | | | |_| |_| | "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " /_/ \_\ .__/| .__/|___/ \____/ \__|_|_|_|\__|\__, | "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " | | | | __/ | Mike Galvin "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " |_| |_| Version 22.03.27 |___/ https://gal.vin "
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object " "
    Write-Host -Object ""
}

## If logging is configured, start logging.
## If the log file already exists, clear it.
If ($LogPath)
{
    ## Make sure the log directory exists.
    $LogPathFolderT = Test-Path $LogPath

    If ($LogPathFolderT -eq $False)
    {
        New-Item $LogPath -ItemType Directory -Force | Out-Null
    }

    $LogFile = ("Remove-MS-Store-Apps_{0:yyyy-MM-dd_HH-mm-ss}.log" -f (Get-Date))
    $Log = "$LogPath\$LogFile"

    $LogT = Test-Path -Path $Log

    If ($LogT)
    {
        Clear-Content -Path $Log
    }

    Add-Content -Path $Log -Encoding ASCII -Value "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") [INFO] Log started"
}

## Function to get date in specific format.
Function Get-DateFormat
{
    Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}

## Function for logging.
Function Write-Log($Type, $Evt)
{
    If ($Type -eq "Info")
    {
        If ($Null -ne $LogPath)
        {
            Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [INFO] $Evt"
        }
        
        Write-Host -Object "$(Get-DateFormat) [INFO] $Evt"
    }

    If ($Type -eq "Succ")
    {
        If ($Null -ne $LogPath)
        {
            Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [SUCCESS] $Evt"
        }

        Write-Host -ForegroundColor Green -Object "$(Get-DateFormat) [SUCCESS] $Evt"
    }

    If ($Type -eq "Err")
    {
        If ($Null -ne $LogPath)
        {
            Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [ERROR] $Evt"
        }

        Write-Host -ForegroundColor Red -BackgroundColor Black -Object "$(Get-DateFormat) [ERROR] $Evt"
    }

    If ($Type -eq "Conf")
    {
        If ($Null -ne $LogPath)
        {
            Add-Content -Path $Log -Encoding ASCII -Value "$Evt"
        }

        Write-Host -ForegroundColor Cyan -Object "$Evt"
    }
}

## Configure the apps to be removed.
$AppsList = Get-Content $AppListFile | Where-Object {$_.trim() -ne ""}

## getting Windows Version info
$OSVMaj = [environment]::OSVersion.Version | Select-Object -expand major
$OSVMin = [environment]::OSVersion.Version | Select-Object -expand minor
$OSVBui = [environment]::OSVersion.Version | Select-Object -expand build
$OSV = "$OSVMaj" + "." + "$OSVMin" + "." + "$OSVBui"

##
## Display the current config and log if configured.
##

Write-Log -Type Conf -Evt "************ Running with the following config *************."
Write-Log -Type Conf -Evt "Utility Version:.......22.03.27"
Write-Log -Type Conf -Evt "Hostname:..............$Env:ComputerName."
Write-Log -Type Conf -Evt "Windows Version:.......$OSV."
Write-Log -Type Conf -Evt "Using list from file:..$AppListFile."

If ($Null -ne $WimFile)
{
    Write-Log -Type Conf -Evt "Wim File:..............$WimFile."
}

else {
    Write-Log -Type Conf -Evt "Wim File:..............No Config"
}

If ($Null -ne $WIndex)
{
    Write-Log -Type Conf -Evt "Wim Index:.............$WIndex."
}

else {
    Write-Log -Type Conf -Evt "Wim Index:.............No Config."
}

If ($Null -ne $WimMntPath)
{
    Write-Log -Type Conf -Evt "Wim Mount Path:........$WimMntPath."
}

else {
    Write-Log -Type Conf -Evt "Wim Index:.............No Config."
}

If ($Null -ne $LogPath)
{
    Write-Log -Type Conf -Evt "Logs directory:........$LogPath."
}

else {
    Write-Log -Type Conf -Evt "Logs directory:........No Config"
}

Write-Log -Type Conf -Evt "Apps to remove:"

ForEach ($App in $AppsList)
{
    Write-Log -Type Conf -Evt ".......................$App"
}

Write-Log -Type Conf -Evt "************************************************************"
Write-Log -Type Info -Evt "Process started"
##
## Display current config ends here.
##

##
## Online Mode
##

If ($Null -eq $WimFile)
{
    ## Remove the Apps listed in the file or report if app not present.
    ForEach ($App in $AppsList)
    {
        $PackageFullName = (Get-AppxPackage $App).PackageFullName
        $ProPackageFullName = (Get-AppxProvisionedPackage -Online | Where-Object {$_.Displayname -eq $App}).PackageName

        If ($PackageFullName)
        {
            Write-Log -Type Info -Evt "Removing Package: $App"
            Remove-AppxPackage -Package $PackageFullName | Out-Null
        }

        else {
            Write-Log -Type Info -Evt "Unable to find package: $App"
        }

        If ($ProPackageFullName)
        {
            Write-Log -Type Info -Evt "Removing Provisioned Package: $ProPackageFullName"
            Remove-AppxProvisionedPackage -Online -PackageName $ProPackageFullName | Out-Null
        }

        else {
            Write-Log -Type Info -Evt "Unable to find provisioned package: $App"
        }
    }

    Write-Log -Type Info -Evt "Process finished."
}

##
## Offline Mode
##

If ($Null -ne $WimFile)
{
    ## Mount the Image
    Mount-WindowsImage -ImagePath $WimFile -Index $WIndex -Path $WimMntPath

    ## Remove the Apps listed above or report if app not present
    ForEach ($App in $AppsList)
    {
        $ProPackageFullName = (Get-AppxProvisionedPackage -Path $WimMntPath | Where-Object {$_.Displayname -eq $App}).PackageName

        If ($ProPackageFullName)
        {
            Write-Log -Type Info -Evt "Removing Provisioned Package: $ProPackageFullName"
            Remove-AppxProvisionedPackage -Path $WimMntPath -PackageName $ProPackageFullName | Out-Null
        }

        else
        {
            Write-Log -Type Info -Evt "Unable to find provisioned package: $App"
        }
    }

    ## Dismount the image and save changes
    Dismount-WindowsImage -Path $WimMntPath -Save

    Write-Log -Type Info -Evt "Process finished."
}

## End