PowerShell script to download all AppX files and their dependencies for currently installed packages.
Tags: powershell
Published: 2020-07-03 18:37:00

#######Script to download Appx/AppxBundle/MSIX files for all packages and their dependencies currently installed on your computer.

I have successfully ran this version on several machines and EVERY dependency does get downloaded and copied.

This script must be run as an administrator.

Resulting packages are in c:\appx_install.

Cached packages are stored in C:\Windows\SoftwareDistribution\Download (which you may want to manually empty after running this script).

```
if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
   Write-Warning "This script must be run as an Administrator"
   Break
}

Clear-Host

#######Function to enumerate bundled packages and copy bundled packages/dependencies to c:\appx_install.
function Copy-Bundled ($up, $root) {
   $count = $up.count
   For ($i = 0; $i -lt $count; $i++) {
      $folder = ("$root\$($up.item($i).title)") -replace "\.", "_"
      if (!(Test-Path $folder -PathType Container)) {
         $ftemp = New-Item -ItemType Directory -Force $folder
      }
      try {
         write-host "Copying files to $folder"
         $up.item($i).CopyFromCache($folder, $False)
      }
      catch {
         if ($_ -like "*0x80246010*") {
            write-host "`n$folder with identity $($up.item($i).Identity.updateid) does not exist. Will try to download...."
            download-updates $up
            $up.item($i).CopyFromCache($folder, $False)
            if ($?) {
               write-host "`nCopy successfull`n"
            }
         }
         elseif (!($_ -like "*0x80070050*")) {                                #Ignore "The file exists." errors so that script can be run repeatedly without having to remove resulting files from c:\appx_install.
            write-host "`n $_ `n"
         }
      }
      if (($up.item($i).bundledupdates.count)) {
         Copy-Bundled $up.item($i).bundledupdates $root
      }
   }
}

#######Function to enumerate downloaded packages.
Function Copy-Updates ($up, $root) {
   $count = $up.count
   For ($i = 0; $i -lt $count; $i++) {
      $folder = ("$root\$($up.item($i).title)") -replace "\.", "_"
      if (!(Test-Path $folder -PathType Container)) {
         $ftemp = New-Item -ItemType Directory -Force $folder
      }
      Copy-Bundled $up.item($i).bundledupdates $folder
   }
}

Function Download-Updates ($updates) {
   $UpdatesToDownload = New-Object -Com Microsoft.Update.UpdateColl
   For ($i = 0; $i -lt $updates.count; $i++) {
      $UpdatesToDownload.Add($updates.item($i)) | out-null
   }
   $Downloader = (New-Object -Com Microsoft.Update.Session).CreateUpdateDownloader()
   $Downloader.IsForced = $True                                               #Forces the downloader to download packages that are already installed. https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nn-wuapi-iupdatedownloader?redirectedfrom=MSDN
   $Downloader.Updates = $UpdatesToDownload
   $Downloader.Download() | Out-Null
   Write-Host "`nDownload Finished.`n"
}

Function Find-Updates {
   $Session = New-Object -ComObject Microsoft.Update.Session
   $Searcher = $Session.CreateUpdateSearcher()
   $Searcher.ServiceID = '855e8a7c-ecb4-4ca3-b045-1dfa50104289'                #Microsoft Store Service ID.
   $Searcher.SearchScope = 2                                                   #https://docs.microsoft.com/en-us/windows/win32/api/wuapi/ne-wuapi-searchscope
   $Searcher.ServerSelection = 3                                               #https://docs.microsoft.com/en-us/previous-versions/windows/desktop/aa387280(v=vs.85)
   $Searcher.Online = $False                                                   #Changing this to $True will not only result in no downloads, but changing it back to false (even with a reboot) will not rectify the situation immediately (however, *eventually* the situation resolves itself).
   $Criteria = "IsInstalled = 1"                                               #https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdatesearcher-search
   Write-Host "`nEnumerating main package list....`n"
   $SearchResult = $Searcher.Search($Criteria)
   $Updates = ($SearchResult.Updates)
   Write-Host "`n$($updates.count) packages found.`n"
   $Updates
}

#######Main

$updates = Find-Updates
Write-Output "`nDownloading currently installed software packages and dependencies. This may take a while...`n"
Download-updates $Updates

Write-Output "`nCopying packages and dependencies to c:\appx_install...`n"
if (!(Test-Path "c:\appx_install" -PathType Container)) { New-Item -ItemType Directory -Force "c:\appx_install\" | Out-Null }
Copy-Updates $updates "c:\appx_install"

Write-Output "`nScript completed. Your downloaded files are located in c:\appx_install."
Write-Output "Files that do not have an extension can be renamed to .appx (or any other extension that the app installer supports. No renaming necessary to install with PowerShell."

```