Remove-MS-Store-Apps.ps1

<#PSScriptInfo
 
.VERSION 23.04.28
 
.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 11 10 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 11/10 Microsoft Store app removal utility
 
    .DESCRIPTION
    Removes apps included in Windows 11/10.
    Run with -help or no arguments for usage.
#>


## Set up command line switches.
[CmdletBinding()]
Param(
    [alias("List")]
    $AppListFile,
    [alias("Wim")]
    $WimFile,
    [alias("WimIndex")]
    $WIndex,
    [alias("WimMountPath")]
    $WimMntPath,
    [alias("L")]
    $LogPathUsr,
    [alias("LogRotate")]
    $LogHistory,
    [switch]$PCApps,
    [switch]$UserApps,
    [switch]$Help,
    [switch]$NoBanner)

If ($NoBanner -eq $False)
{
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object "
         _____ __ __ _____ _____ _
        | __ \ | \/ |/ ____| / ____| |
        | |__) |___ _ __ ___ _____ _____ | \ / | (___ | (___ | |_ ___ _ __ ___
        | _ // _ \ '_ ' _ \ / _ \ \ / / _ \ | |\/| |\___ \ \___ \| __/ _ \| '__/ _ \
        | | \ \ __/ | | | | | (_) \ V / __/ | | | |____) | ____) | || (_) | | | __/
        |_| \_\___|_| |_| |_|\___/ \_/ \___| |_|_ |_|_____/ |_____/ \__\___/|_| \___|
            /\ | | | | | (_) (_) |
           / \ _ __ _ __ ___ | | | | |_ _| |_| |_ _ _
          / /\ \ | '_ \| '_ \/ __| | | | | __| | | | __| | | | Mike Galvin
         / ____ \| |_) | |_) \__ \ | |__| | |_| | | | |_| |_| | https://gal.vin
        /_/ \_\ .__/| .__/|___/ \____/ \__|_|_|_|\__|\__, |
                 | | | | __/ | Version 23.04.28
                 |_| |_| |___/ See -help for usage
                                                                                             
                              Donate: https://www.paypal.me/digressive
"

}

If ($PSBoundParameters.Values.Count -eq 0 -or $Help)
{
    Write-Host -Object "Usage:
    From an elevated terminal run: [path\]Remove-MS-Store-Apps.ps1 -List [path\]apps-to-remove.txt
    This will remove the apps in the txt file from your Windows installation for all users.
 
    To list apps for all users: -PCApps.
    To list apps for the current user: -UserApps.
 
    To operate on a wim file: -Wim [path\]install.wim -WimIndex [number] (optional: -WimMountPath [path\])
 
    Run the following command to find out the WimIndex for your wim file:
    Get-WindowsImage -ImagePath [path\]install.wim | Format-Table -Property ImageIndex, ImageName
 
    To output a log: -L [path\].
    To remove logs produced by the utility older than X days: -LogRotate [number].
    Run with no ASCII banner: -NoBanner"

}

else {
    ## If logging is configured, start logging.
    ## If the log file already exists, clear it.
    If ($LogPathUsr)
    {
        ## Clean User entered string
        $LogPath = $LogPathUsr.trimend('\')

        ## Make sure the log directory exists.
        If ((Test-Path -Path $LogPath) -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"

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

    ## 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 ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [INFO] $Evt"
            }
            
            Write-Host -Object "$(Get-DateFormat) [INFO] $Evt"
        }

        If ($Type -eq "Succ")
        {
            If ($LogPathUsr)
            {
                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 ($LogPathUsr)
            {
                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 ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$Evt"
            }

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

    ## Function for Update Check
    Function UpdateCheck()
    {
        $ScriptVersion = "23.04.28"
        $RawSource = "https://raw.githubusercontent.com/Digressive/Remove-MS-Store-Apps/master/Remove-MS-Store-Apps.ps1"

        try {
            $SourceCheck = Invoke-RestMethod -uri "$RawSource"
            $VerCheck = $SourceCheck -split '\n' | Select-String -Pattern ".VERSION $ScriptVersion" -SimpleMatch -CaseSensitive -Quiet

            If ($VerCheck -ne $True)
            {
                Write-Log -Type Conf -Evt "*** There is an update available. ***"
            }
        }

        catch {
        }
    }

    ## Check for the apps list file, if it exists then sanitise it and if it doesn't exist then report and exit.
    If ($AppListFile)
    {
        If (Test-Path -Path $AppListFile)
        {
            $AppsList = Get-Content $AppListFile | Where-Object {$_.trim() -ne ""}
        }

        else {
            Write-Log -Type Err -Evt "The app list file $AppListFile does not exist."
            Exit
        }
    }

    If ($null -eq $AppListFile -And $PCApps -eq $false -And $UserApps -eq $false)
    {
        Write-Log -Type Err -Evt "No app list specified."
        Exit
    }

    ## 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: 23.04.28"
    UpdateCheck ## Run Update checker function
    Write-Log -Type Conf -Evt "Hostname: $Env:ComputerName."
    Write-Log -Type Conf -Evt "Windows Version: $OSV."

    If ($AppListFile)
    {
        Write-Log -Type Conf -Evt "Using list from file: $AppListFile."
    }

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

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

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

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

    If ($Null -ne $LogHistory)
    {
        Write-Log -Type Conf -Evt "Logs to keep: $LogHistory days"
    }

    If ($AppListFile)
    {
        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"
            }
        }
    }

    ##
    ## Offline Mode
    ##
    If ($WimFile)
    {
        ## Default Wim Mount Path if none is configured.
        If ($Null -eq $WimMntPath)
        {
            $WimMntPath = "$Env:temp\RemMSStoreApps-WimMount"
        }

        ## Make sure the mount directory exists, if it doesn't create it.
        If ((Test-Path -Path $WimMntPath) -eq $False)
        {
            New-Item $WimMntPath -ItemType Directory -Force | Out-Null
        }

        ## Mount the Image.
        Mount-WindowsImage -ImagePath $WimFile -Index $WIndex -Path $WimMntPath | Out-Null

        ## 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 | Out-Null
    }

    If ($PCApps)
    {
        Get-AppxProvisionedPackage -Online | Select-Object DisplayName | Format-Table -HideTableHeaders
        If ($LogPathUsr)
        {
            Get-AppxProvisionedPackage -Online | Select-Object DisplayName | Format-Table -HideTableHeaders | Out-File -Append $Log -Encoding ASCII
        }
    }

    If ($UserApps)
    {
        Get-AppxPackage | Select-Object Name | Format-Table -HideTableHeaders
        If ($LogPathUsr)
        {
            Get-AppxPackage | Select-Object Name | Format-Table -HideTableHeaders | Out-File -Append $Log -Encoding ASCII
        }
    }

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

    If ($Null -ne $LogHistory)
    {
        ## Cleanup logs.
        Write-Log -Type Info -Evt "Deleting logs older than: $LogHistory days"
        Get-ChildItem -Path "$LogPath\Remove-MS-Store-Apps_*" -File | Where-Object CreationTime -lt (Get-Date).AddDays(-$LogHistory) | Remove-Item -Recurse
    }
}
## End