Install-WindowsStoreMinimal.ps1
|
<#PSScriptInfo
.VERSION 1.0.0 .GUID 94b0cf8f-12aa-4c53-ac47-ffff60587fe2 .AUTHOR Joel055 .TAGS WindowsStore MicrosoftStore AppX MSIX Restore .LICENSEURI https://github.com/Joel055/Install-WindowsStoreMinimal/blob/main/LICENSE .PROJECTURI https://github.com/Joel055/Install-WindowsStoreMinimal .RELEASENOTES Initial public release. .DESCRIPTION Installs the minimal latest Microsoft Store dependency set required for Store and winget functionality on Windows, without installing the full Store app suite. Resolves official Store packages via rg-adguard.net. #> #Requires -RunAsAdministrator <# .SYNOPSIS Installs the minimal set of Microsoft Store dependencies required for Microsoft Store and winget functionality, without installing the full Store app suite. Resolves official Store packages via rg-adguard.net and installs them offline using Add-AppxPackage. .NOTES Requires administrative privileges. For x64 installations only. May set the Microsoft Account Sign-in Assistant (wlidsvc) service to Manual if disabled. rg-adguard.net is not affiliated with Microsoft and is used at your own risk. .PARAMETER Force Forces re-evaluation of Store dependencies even if Microsoft Store is already installed. .PARAMETER KeepDownloads Prevents deletion of downloaded package files (subsequent runs will reset the folder). .EXAMPLE Install-WindowsStoreMinimal .EXAMPLE .\Install-WindowsStoreMinimal.ps1 -Force -KeepDownloads #> [CmdletBinding()] param( [switch]$Force, [switch]$KeepDownloads ) $bitsSvc = "BITS" $msSigninSvc = "wlidsvc" $downloadPath = "$env:TEMP\WindowsStoreMinimal" $packageTable = @( [pscustomobject]@{ FamilyBase = "Microsoft.WindowsStore" Publisher = "8wekyb3d8bbwe" IgnoreVer = $false Patterns = @( "Microsoft\.VCLibs\.[^A-Za-z]*x64.*\.appx" "Microsoft\.UI\.Xaml\..*x64.*\.appx" "Microsoft\.NET\.Native\.Framework\..*x64.*\.appx" "Microsoft\.NET\.Native\.Runtime\..*x64.*\.appx" "Microsoft\.WindowsStore.*neutral.*\.msixbundle" ) } [pscustomobject]@{ FamilyBase = "Microsoft.DesktopAppInstaller" Publisher = "8wekyb3d8bbwe" IgnoreVer = $true # DesktopAppInstaller's bundle version always mismatches local install (manifest ver), skip version check Patterns = @("Microsoft\.DesktopAppInstaller.*neutral.*\.msixbundle") } [pscustomobject]@{ FamilyBase = "Microsoft.StorePurchaseApp" Publisher = "8wekyb3d8bbwe" IgnoreVer = $false Patterns = @("Microsoft\.StorePurchaseApp.*neutral.*\.appxbundle") } ) function Get-AdguardLinks { param($PackageTable) $packageURLs = @() foreach ($pkg in $PackageTable) { Write-Host "Checking: $($pkg.FamilyBase)" -ForegroundColor Yellow $url = "https://store.rg-adguard.net/api/GetFiles" $body = "type=PackageFamilyName&url=$($pkg.FamilyBase)_$($pkg.Publisher)&ring=Retail&lang=en-US" $response = Invoke-WebRequest -Uri $url -Method POST -Body $body -UseBasicParsing -ErrorAction Stop foreach ($pattern in $pkg.Patterns) { $versions = $response.Links | Where-Object { $_.outerHTML -match $pattern } | ForEach-Object { if ($_.outerHTML -match '">(.*?)(?=_(x64|neutral)).*(\.[^.]+)</') { $name = $matches[1] $arch = $matches[2] $ext = $matches[3] # .appx / .appxbundle / .msixbundle etc if ($arch -eq 'neutral') { $arch = 'x64' } # Normalize version strings to a numeric value for comparison; Store package versions are inconsistent. [long]$ver = $name -replace "[^0-9]", "" [PSCustomObject]@{ Name = $name Architecture = $arch Extension = $ext Version = $ver URL = $_.href } } } # Fail fast at first sign of dependency-mismatch, avoids partial installations. if (-not $versions) { throw "Dependency resolution failed for $($pkg.FamilyBase) (pattern '$pattern'). Store dependency metadata has changed; this script is out of date." } $maxLen = ($versions.Version | ForEach-Object { $_.ToString().Length } | Measure-Object -Maximum).Maximum $latest = $versions | Sort-Object @{Expression={ $_.Version.ToString().PadRight($maxLen,'0') }} -Descending | Select-Object -First 1 Write-Host " Package : " -NoNewline Write-Host "$($latest.Name)" -ForegroundColor Cyan if ($pkg.IgnoreVer) { $installed = Get-AppxPackage -AllUsers | Where-Object { $_.Name -eq $pkg.FamilyBase -and $_.Architecture -eq $latest.Architecture } } else { $installed = (Get-AppxPackage -AllUsers | Where-Object { $_.PackageFullName -match "$($latest.Name)_(x64|neutral)" }) } if ($installed) { Write-host " Already installed, skipping`n" -ForegroundColor DarkGray continue } else { Write-Host " Queued for download/install`n" -ForegroundColor Green } $packageURLs += $latest } } $packageURLs } $isInstalled = Get-AppxPackage -AllUsers -Name "Microsoft.WindowsStore" -ErrorAction SilentlyContinue if ($isInstalled -and -not $Force) { Write-Warning "Microsoft Store is already installed. Use -Force to run anyway." return } try { Invoke-WebRequest -Uri "https://store.rg-adguard.net" -Method Head -TimeoutSec 5 -ErrorAction Stop | Out-Null } catch { throw "Cannot reach store.rg-adguard.net over HTTPS. Internet access, DNS, proxy, or firewall may be blocking the connection." } if ((Get-Service -Name $bitsSvc).StartType -eq "Disabled") { throw "BITS service is disabled but required for dependency downloads." } # Make sure MS sign-in assistant service is enabled, needed for Store auth if ((Get-Service -Name $msSigninSvc).StartType -eq "Disabled") { Set-Service -Name $msSigninSvc -StartupType Manual | Out-Null Write-Warning "Startup-type of service `"$msSigninSvc`" set to Manual, from Disabled`n" Start-Service -Name $msSigninSvc -ErrorAction Stop | Out-Null } $packages = Get-AdguardLinks -PackageTable $packageTable if (-not $packages) { Write-Host "All dependencies are already up to date." -ForegroundColor Yellow return } if (-not (Test-Path -Path $downloadPath)) { New-Item -ItemType Directory -Force -Path $downloadPath | Out-Null } else { # Remove old files from previous runs Get-ChildItem -Path $downloadPath -Recurse | Remove-Item -Force -Recurse } foreach ($pkt in $packages) { Write-Host "Processing: $($pkt.Name)" -ForegroundColor Yellow $outFile = Join-Path $downloadPath ($pkt.Name + $pkt.Extension) Write-Host " Downloading..." -ForegroundColor Cyan Start-BitsTransfer -Source $pkt.URL -Destination $outFile -ErrorAction Stop Write-Host " Installing...`n" -ForegroundColor Cyan Add-AppxPackage -Path $outFile -ForceApplicationShutdown -ErrorAction Stop } if (-not $KeepDownloads) { Remove-Item -Path $downloadPath -Recurse -Force } else { Write-Host "Downloaded package-files can be found at: `"$($downloadPath)`"`n" } Write-Host "Done!`n" -ForegroundColor Green |