Skip to content

Instantly share code, notes, and snippets.

@doggy8088
Forked from Trenly/README.md
Last active February 24, 2024 07:44
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save doggy8088/4f2cbd14864294e6df1a064916716346 to your computer and use it in GitHub Desktop.
Save doggy8088/4f2cbd14864294e6df1a064916716346 to your computer and use it in GitHub Desktop.
Install Winget to the Windows Sandbox Base Image

This powershell script modifies the Base Image, or the Virtual Hard Disk, which the Windows Sandbox launches upon startup. It will copy the required files to the sandbox and add a registry key which will install them upon startup. By default the script will install the latest stable release of Winget. You can specify to use the latest pre-release with the -PreRelease switch.

When a new version of Winget is released, run this script again to update the installation in the sandbox to the latest version

#Requires -Version 5
#Requires -RunAsAdministrator
Param(
[switch] $PreRelease
)
Function Enable-WindowsSandbox {
if (!(Get-Command 'WindowsSandbox.exe' -ErrorAction 'SilentlyContinue')) {
try {
Enable-WindowsOptionalFeature -FeatureName 'Containers-DisposableClientVM' -All -Online
Write-Output 'Windows must be restarted to finish enabling Windows Sandbox'
} catch {
Write-Error 'Windows Sandbox cannot be enabled on your machine. Please ensure your system meets the prerequisites shown at https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview.'
exit 1
}
} else {
Write-Output 'WindowsSandbox is already installed'
}
}
Function Close-WindowsSandbox {
$_Sandbox = Get-Process 'WindowsSandboxClient' -ErrorAction SilentlyContinue
if ($_Sandbox) {
Write-Output 'Windows Sandbox must be closed to modify the base image'
Write-Output 'Closing Windows Sandbox'
$_Sandbox | Stop-Process
Start-Sleep -Seconds 5
} else {
Write-Output 'Windows Sandbox is not running. Installation will proceed'
}
}
Function Set-CMServiceStatus() {
Param(
[boolean] $Enabled
)
$_ContainerManagerService = Get-Service -Name 'CmService'
switch ($Enabled) {
$true {
if ($_ContainerManagerService.Status -eq 'Stopped') {
Write-Output 'Starting Container Manager Service'
$_ContainerManagerService | Start-Service
} else {
Write-Output 'Container Manager Service is running'
}
}
$false {
if ($_ContainerManagerService.Status -ne 'Stopped') {
Write-Output 'Stopping Container Manager Service'
$_ContainerManagerService | Stop-Service -Force
} else {
Write-Output 'Container Manager Service is stopped'
}
}
default {
Write-Error "Unknown parameter value - Set-CMServiceStatus $Enabled"
exit 1
}
}
}
Function Mount-SandboxImage {
Write-Output 'Attempting to find the virtual hard disk'
$script:BaseImageFolder = Resolve-Path("$ENV:PROGRAMDATA\Microsoft\Windows\Containers\BaseImages") -ErrorAction 'SilentlyContinue'
if ($null -eq $BaseImageFolder) {
Write-Error 'Could not locate BaseImages Folder'
exit 1
}
$script:GUIDFolder = @((Get-ChildItem $BaseImageFolder -Directory).Where({ $_.Name -match '[a-z0-9]{8}(-[a-z0-9]{4}){3}-[a-z0-9]{12}' }))[0]
if ($null -eq $GUIDFolder) {
Write-Error 'Could not locate Sandbox GUID Folder'
exit 1
}
$script:BaseLayerVHDX = @((Get-ChildItem $GUIDFolder.FullName -File).Where({ $_.FullName -cmatch 'BaseLayer\.vhdx$' }))[0]
if ($null -eq $BaseLayerVHDX) {
Write-Error 'Could not locate Sandbox Virtual Hard Disk'
exit 1
}
Write-Output 'Virtual hard disk found. Attempting to mount the disk'
Mount-VHD $BaseLayerVHDX.FullName
$script:VHDXDriveLetter = (Get-VHD $BaseLayerVHDX.FullName | Get-Disk | Get-Partition | Get-Volume).DriveLetter
Write-Output "Virtual hard disk mounted at $VHDXDriveLetter`:\"
}
Function Dismount-SandboxImage {
Write-Output 'Dismounting the virtual hard disk'
Dismount-VHD $BaseLayerVHDX.FullName
}
Function Mount-SandboxRegistry {
$_NTUserDatFile = Resolve-Path "$VHDXDriveLetter`:\Files\Users\WDAGUtilityAccount\ntuser.dat"
if ($null -eq $_NTUserDatFile) {
Write-Error 'Unable to locate sandbox user registry'
exit 1
}
Write-Output 'Loading Sandbox User Registry'
$_GUID = (New-Guid).ToString()
Write-Output "Assigning Temporary User GUID - $_GUID"
$script:SandboxRegKey = "HKEY_Users\$_GUID"
REG LOAD $SandboxRegKey $_NTUserDatFile.Path
Write-Output 'Sandbox User Registry Loaded!'
}
Function Dismount-SandboxRegistry {
Write-Output 'Unloading Sandbox User Registry'
$null = REG UNLOAD $SandboxRegKey
}
Function Publish-BinaryFiles {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$WebClient = New-Object System.Net.WebClient
$_WindowsPath = Resolve-Path "$VHDXDriveLetter`:\Files\Windows"
Write-Output 'Fetching Latest Winget CLI Release'
$_LatestUrl = ((Invoke-WebRequest 'https://api.github.com/repos/microsoft/winget-cli/releases' -UseBasicParsing | ConvertFrom-Json).Where({ $_.prerelease -eq $PreRelease }) | Select-Object -First 1).assets.Where({ $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle$' }).browser_download_url
Write-Output 'Downloading Winget to Sandbox'
$WebClient.DownloadFile($_LatestUrl, $(Join-Path -Path $_WindowsPath -ChildPath 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle'))
Write-Output 'Downloading VCLibs to Sandbox'
$WebClient.DownloadFile('https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx', $(Join-Path -Path $_WindowsPath -ChildPath 'Microsoft.VCLibs.x64.14.00.Desktop.appx'))
Write-Output 'Fetching Microsoft UI Nuget Package'
$_UILibsZipPath = Join-Path -Path $env:TEMP -ChildPath 'Microsoft.UI.Xaml.2.7.zip'
$WebClient.DownloadFile('https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.0', $_UILibsZipPath)
Write-Output 'Extracting Microsoft UI Appx to Sandbox'
Expand-Archive -Path $_UILibsZipPath -DestinationPath $(Join-Path -Path $env:TEMP -ChildPath 'Microsoft.UI.Xaml.2.7') -Force
$_UILibsAppxPath = Join-Path -Path $env:TEMP -ChildPath 'Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx'
Copy-Item -Path $_UILibsAppxPath -Destination $(Join-Path -Path $_WindowsPath -ChildPath 'Microsoft.UI.Xaml.2.7.appx')
Write-Output 'Writing registry key to install Winget'
$_RunKey = Get-Item Registry::$SandboxRegKey\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -ErrorAction 'SilentlyContinue'
if ($null -eq $_RunKey) {
$_RunKey = New-Item Registry::$SandboxRegKey\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -Force
}
$null = New-ItemProperty -Path $_RunKey.PSPath -Name 'InstallWingetAndDependencies' -Value 'powershell.exe "Add-AppxPackage C:\Windows\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle -DependencyPath "C:\Windows\Microsoft.UI.Xaml.2.7.appx","C:\Windows\Microsoft.VCLibs.x64.14.00.Desktop.appx""' -PropertyType 'String' -Force
$_RunKey.Close()
Write-Output 'Writing registry key to enable powershell script execution'
$_PowershellKey = Get-Item Registry::$SandboxRegKey\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell -ErrorAction 'SilentlyContinue'
if ($null -eq $_PowershellKey) {
$_PowershellKey = New-Item Registry::$SandboxRegKey\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell -Force
}
$null = New-ItemProperty -Path $_PowershellKey.PSPath -Name 'ExecutionPolicy' -Value 'RemoteSigned' -PropertyType 'String' -Force
$null = New-ItemProperty -Path $_PowershellKey.PSPath -Name 'Path' -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType 'String' -Force
$_PowershellKey.Close()
[GC]::Collect()
Write-Output 'Setup complete'
}
Enable-WindowsSandbox
Close-WindowsSandbox
Set-CMServiceStatus -Enabled $false
Mount-SandboxImage
Mount-SandboxRegistry
Publish-BinaryFiles
Dismount-SandboxRegistry
Dismount-SandboxImage
Set-CMServiceStatus -Enabled $true
@Dragon1573
Copy link

Still unable to use this script ...

PS D:\Download\Aria2> gsudo .\WingetSandbox.ps1
WindowsSandbox is already installed
Windows Sandbox must be closed to modify the base image
Closing Windows Sandbox
Stopping Container Manager Service
Stop-Service : 无法停止服务“容器管理器服务 (CmService)”,因为具有依赖它的服务。只有在设置了 Force 标志时才能停止该服务。
所在位置 D:\Download\Aria2\WingetSandbox.ps1:52 字符: 45
+                 $_ContainerManagerService | Stop-Service
+                                             ~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.ServiceProcess.ServiceController:ServiceController) [Stop-Service],ServiceCommandException
    + FullyQualifiedErrorId : ServiceHasDependentServices,Microsoft.PowerShell.Commands.StopServiceCommand

Attempting to find the virtual hard disk
Mount-SandboxImage : Could not locate Sandbox Virtual Hard Disk
所在位置 D:\Download\Aria2\WingetSandbox.ps1:152 字符: 1
+ Mount-SandboxImage
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Mount-SandboxImage

@DFLiddle
Copy link

It may be important to mention that the entirety (?) of the Hyper-V feature needs to be installed for this script to work. That was my experience on Windows 10.

@brenocrs
Copy link

brenocrs commented Mar 1, 2023

@DFLiddle there is no need for that.

Only the tools for manage Hyper-v using powershell is needed as far i could test here in my machine.

Enable-WindowsOptionalFeature -FeatureName 'Microsoft-Hyper-V-Management-PowerShell' -All -Online

I mencioned this in the original code as well

@cssaph
Copy link

cssaph commented Mar 29, 2023

After running this script i can no longer open powershell in the sandbox..
It just opens briefly and then closes..
I get this error when i try via cmd - "Version v4.03019 of the .NET Framework is not installed and it is required to run version 3 of Windows PowerShell"
Kinda defeats the purpose if i manually have to install that everytime.. so 2 things, how do i fix this and if not how do i reset sandbox back to original?
MicrosoftTeams-image (9)

@Dragon1573
Copy link

How do I reset Sandbox back to original?

I can't sure if there's something important inside the image. Is it possible to un-and-reinstall Windows Sandbox?

@cssaph
Copy link

cssaph commented Mar 30, 2023

How do I reset Sandbox back to original?

I can't sure if there's something important inside the image. Is it possible to un-and-reinstall Windows Sandbox?

Yes that worked, actually running into another issues, i get an error when running the script that it cant find the sandbox virtual hard drive?

"WindowsSandbox is already installed
Windows Sandbox is not running. Installation will proceed
Container Manager Service is stopped
Attempting to find the virtual hard disk
Mount-SandboxImage : Could not locate Sandbox Virtual Hard Disk
At C:\ProgramData\Microsoft\Windows\Containers\WingetSandbox.ps1:152 char:1

  • Mount-SandboxImage
  •   + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
      + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Mount-SandboxImage"
    

@jonnyhotchkiss
Copy link

first run attempt from elevated ps_ise session got as far as Starting Container Manager Service then seemed to stop.
on win10pro. sandbox works when launched from startmenu (but no winget of course)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment