29

I know if I have .appx package file, I can install it via powershell with the Add-AppxPackage cmdlet. However, I simply want to download & install Microsoft Store packages by name.

I don't want to have to go to the Microsoft Store page, start fiddler, start the download, capture the .appx file URL and then manually download it so that I can use Add-AppxPackage. (See how Windows OS Hub did that here)

That could be fun - but it will be flaky. I need a robust scriptable method for managing Windows Store apps.

(There are a few software packages that are only accessible via Microsoft Store. Everything else I can get via Chocolatey or direct msi download.)

One example that I can't yet script is installation of the HEIF Image Extensions (needed to view the image format from iPhones: *.HEIC format.

Once I install this from the Windows Store, it shows up with Get-AppxPackage

PS C:\Tools> Get-AppxPackage | Where-Object {$_.Name -eq "Microsoft.HEVCVideoExtension" }


Name              : Microsoft.HEVCVideoExtension
Publisher         : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture      : X64
ResourceId        :
Version           : 1.0.31053.0
PackageFullName   : Microsoft.HEVCVideoExtension_1.0.31053.0_x64__8wekyb3d8bbwe
InstallLocation   : C:\Program Files\WindowsApps\Microsoft.HEVCVideoExtension_1.0.31053.0_x64__8wekyb3d8bbwe
IsFramework       : False
PackageFamilyName : Microsoft.HEVCVideoExtension_8wekyb3d8bbwe
PublisherId       : 8wekyb3d8bbwe
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : False
NonRemovable      : False
Dependencies      : {Microsoft.VCLibs.140.00_14.0.27810.0_x64__8wekyb3d8bbwe}
IsPartiallyStaged : False
SignatureKind     : Store
Status            : Ok

What I want is the cmdlet: Download-AppxPackage so that I can do:

Download-AppxPackage -Name "Microsoft.HEVCVideoExtension"

Does anyone know how I can do this?

Dave M
  • 4,494
David Wilson
  • 403
  • 1
  • 4
  • 5

5 Answers5

21

You can now use winget to install msstore apps on windows 10 and 11. Use winget search <app_name> --source=msstore to make a search, and use the id of the app to install and upgrade the app. For example, to install Netflix:

  1. First I made a search with winget search Netflix --source=msstore, I see that the app id is 9WZDNCRFJ3TJ.
  2. I install app with winget install -e -i --id=9WZDNCRFJ3TJ --source=msstore.
  3. I upgrade app with winget upgrade -e -i --id=9WZDNCRFJ3TJ.
NJT145
  • 326
11

store.rg-adguard.net is a GUI for generating direct download links to store apps. Peeking at the source of that page, we can piggyback off them to download the content directly, but using PackageFamilyName, rather than Name (in your example it would be Microsoft.HEVCVideoExtension_8wekyb3d8bbwe).

function Download-AppxPackage {
[CmdletBinding()]
param (
  [string]$PackageFamilyName,
  [string]$Path
)

process { $WebResponse = Invoke-WebRequest -Method 'POST' -Uri 'https://store.rg-adguard.net/api/GetFiles' -Body "type=PackageFamilyName&url=$($PackageFamilyName)&ring=Retail&lang=en-US" -ContentType 'application/x-www-form-urlencoded' -UseBasicParsing $LinksMatch = $WebResponse.Links | where {$_ -like '_x64.appx*'} | Select-String -Pattern '(?<=a href=").+(?=" r)' $DownloadLinks = @($LinksMatch.matches.Value)

for ($i = 1; $i -le $DownloadLinks.Count; $i++) {
  Invoke-WebRequest -Uri $DownloadLinks[$i-1] -OutFile &quot;$Path\$PackageFamilyName($i).appx&quot;   
}

} }

This is limited to the x64 version, and the path must point to a folder. It will download the package and its dependencies and save them all as PackagefamilyName(n).appx

6

Building further on AJ's Answer. Download any app from windows app store with powershell just supply a url from the store and a path for saving.

Usage:

Download-AppxPackage "https://www.microsoft.com/p/dynamic-theme/9nblggh1zbkw" "$ENV:USERPROFILE\Desktop"
C:\Users\user\Desktop\55888ChristopheLavalle.DynamicTheme_1.4.30233.0_neutral_~_jdggxwd41xcr0.AppxBundle
C:\Users\user\Desktop\55888ChristopheLavalle.DynamicTheme_1.4.30234.0_neutral_~_jdggxwd41xcr0.AppxBundle
C:\Users\user\Desktop\Microsoft.NET.Native.Framework.1.7_1.7.27413.0_x64__8wekyb3d8bbwe.Appx
C:\Users\user\Desktop\Microsoft.NET.Native.Runtime.1.7_1.7.27422.0_x64__8wekyb3d8bbwe.Appx
C:\Users\user\Desktop\Microsoft.Services.Store.Engagement_10.0.19011.0_x64__8wekyb3d8bbwe.Appx
C:\Users\user\Desktop\Microsoft.VCLibs.140.00_14.0.29231.0_x64__8wekyb3d8bbwe.App

Code:

function Download-AppxPackage {
[CmdletBinding()]
param (
  [string]$Uri,
  [string]$Path = "."
)

process { $Path = (Resolve-Path $Path).Path #Get Urls to download $WebResponse = Invoke-WebRequest -UseBasicParsing -Method 'POST' -Uri 'https://store.rg-adguard.net/api/GetFiles' -Body "type=url&url=$Uri&ring=Retail" -ContentType 'application/x-www-form-urlencoded' $LinksMatch = $WebResponse.Links | where {$_ -like '.appx'} | where {$_ -like 'neutral' -or $_ -like ""+$env:PROCESSOR_ARCHITECTURE.Replace("AMD","X").Replace("IA","X")+""} | Select-String -Pattern '(?<=a href=").+(?=" r)' $DownloadLinks = $LinksMatch.matches.value

function Resolve-NameConflict{
#Accepts Path to a FILE and changes it so there are no name conflicts
param(
[string]$Path
)
    $newPath = $Path
    if(Test-Path $Path){
        $i = 0;
        $item = (Get-Item $Path)
        while(Test-Path $newPath){
            $i += 1;
            $newPath = Join-Path $item.DirectoryName ($item.BaseName+&quot;($i)&quot;+$item.Extension)
        }
    }
    return $newPath
}
#Download Urls
foreach($url in $DownloadLinks){
    $FileRequest = Invoke-WebRequest -Uri $url -UseBasicParsing #-Method Head
    $FileName = ($FileRequest.Headers[&quot;Content-Disposition&quot;] | Select-String -Pattern  '(?&lt;=filename=).+').matches.value
    $FilePath = Join-Path $Path $FileName; $FilePath = Resolve-NameConflict($FilePath)
    [System.IO.File]::WriteAllBytes($FilePath, $FileRequest.content)
    echo $FilePath
}

} }

This correctly downloads neutral and x64 packages but untested for arm and 32bit systems. the path must point to a folder. It will download the package and its dependencies and save them all as their original file names while avoiding name collisions like chrome.

Yorai Levi
  • 161
  • 1
  • 3
6
function Download-AppxPackage {
[CmdletBinding()]
param (
  [string]$Uri,
  [string]$Path = "."
)

  process {
    echo ""
    $StopWatch = [system.diagnostics.stopwatch]::startnew()
    $Path = (Resolve-Path $Path).Path
    #Get Urls to download
    Write-Host -ForegroundColor Yellow "Processing $Uri"
    $WebResponse = Invoke-WebRequest -UseBasicParsing -Method 'POST' -Uri 'https://store.rg-adguard.net/api/GetFiles' -Body "type=url&url=$Uri&ring=Retail" -ContentType 'application/x-www-form-urlencoded'
    $LinksMatch = ($WebResponse.Links | where {$_ -like '*.appx*'} | where {$_ -like '*_neutral_*' -or $_ -like "*_"+$env:PROCESSOR_ARCHITECTURE.Replace("AMD","X").Replace("IA","X")+"_*"} | Select-String -Pattern '(?<=a href=").+(?=" r)').matches.value
    $Files = ($WebResponse.Links | where {$_ -like '*.appx*'} | where {$_ -like '*_neutral_*' -or $_ -like "*_"+$env:PROCESSOR_ARCHITECTURE.Replace("AMD","X").Replace("IA","X")+"_*"} | where {$_ } | Select-String -Pattern '(?<=noreferrer">).+(?=</a>)').matches.value
    #Create array of links and filenames
    $DownloadLinks = @()
    for($i = 0;$i -lt $LinksMatch.Count; $i++){
        $Array += ,@($LinksMatch[$i],$Files[$i])
    }
    #Sort by filename descending
    $Array = $Array | sort-object @{Expression={$_[1]}; Descending=$True}
    $LastFile = "temp123"
    for($i = 0;$i -lt $LinksMatch.Count; $i++){
        $CurrentFile = $Array[$i][1]
        $CurrentUrl = $Array[$i][0]
        #Find first number index of current and last processed filename
        if ($CurrentFile -match "(?<number>\d)"){}
        $FileIndex = $CurrentFile.indexof($Matches.number)
        if ($LastFile -match "(?<number>\d)"){}
        $LastFileIndex = $LastFile.indexof($Matches.number)

        #If current filename product not equal to last filename product
        if (($CurrentFile.SubString(0,$FileIndex-1)) -ne ($LastFile.SubString(0,$LastFileIndex-1))) {
            #If file not already downloaded, add to the download queue
            if (-Not (Test-Path "$Path\$CurrentFile")) {
                "Downloading $Path\$CurrentFile"
                $FilePath = "$Path\$CurrentFile"
                $FileRequest = Invoke-WebRequest -Uri $CurrentUrl -UseBasicParsing #-Method Head
                [System.IO.File]::WriteAllBytes($FilePath, $FileRequest.content)
            }
        #Delete file outdated and already exist
        }elseif ((Test-Path "$Path\$CurrentFile")) {
            Remove-Item "$Path\$CurrentFile"
            "Removing $Path\$CurrentFile"
        }
        $LastFile = $CurrentFile
    }
    "Time to process: "+$StopWatch.ElapsedMilliseconds
  }
}


if (-Not (Test-Path "C:\Support\Store")) {
    Write-Host -ForegroundColor Green "Creating directory C:\Support\Store"
    New-Item -ItemType Directory -Force -Path "C:\Support\Store"
}

Download-AppxPackage "https://www.microsoft.com/p/snip-sketch/9mz95kl8mr0l" "C:\Support\Store"

Modified the script so that it delete old versions and only download latest.

Much thanks to Yorai Levi for the original script!

BCI
  • 61
  • 1
  • 2
0

I've found some additional tools that I think are worth noting and hopefully may be of use to others who are using Windows Sandbox.

Although I tried to install Windows Store manually inside Windows Sandbox, that failed due to required components being missing, but this project fills in all of the gaps:

git clone https://github.com/kkkgo/LTSC-Add-MicrosoftStore

In that folder, simply run Add-Store.cmd and you will now have an operational Store App on the Start Menu in Windows Sandbox, but it is not complete as a few services have to be started also:

# 'config start=auto' is not required on Sandbox,
# but is useful for non-Sandbox environments.
SC config wuauserv start=auto
SC config bits start=auto
SC config cryptsvc start=auto
SC config trustedinstaller start=auto
SC start wuauserv
SC start bits
SC start cryptsvc
SC start trustedinstaller

Finally, make sure to log in to the Store with a Microsoft account (press the Profile at top right) after which, you should have a fully operational Windows Store app inside Windows Sandbox.

The project was built for Windows Enterprise LTSC (Long-Term Servicing Channel, designed for Windows 10 devices where the key requirement is that functionality and features don’t change over time) that, like Windows Sandbox, uses a slimmed down set of features, but this project works perfectly on Windows Sandbox. The project installs four components to achieve this:

Microsoft Store
Store Purchase App
App Installer
Xbox Identity Provider
YorSubs
  • 135
  • 1
  • 7