Windows Sandbox Development PowerShell Code

title: Contents 
style: nestedList # TOC style (nestedList|inlineFirstLevel)
minLevel: 1 # Include headings from the specified level
maxLevel: 4 # Include headings up to the specified level
includeLinks: true # Make headings clickable
debugInConsole: false # Print debug info in Obsidian console

Overview

Sources:

  • **

Code Snippet

InSandboxScript.ps1

Sources:

  • InSandboxScript.ps1:
Param(
  [String[]] $DesktopAppInstallerDependencyPath
)
 
$ProgressPreference = 'SilentlyContinue'
 
$desktopPath = "C:\Users\WDAGUtilityAccount\Desktop"
 
Write-Host @"
--> Installing WinGet
 
"@
 
foreach($dependency in $DesktopAppInstallerDependencyPath)
{
  Write-Host @"
  ----> Installing $dependency
"@
  Add-AppxPackage -Path $dependency
}
 
Write-Host @"
  ----> Enabling dev mode
"@
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
 
$devPackageManifestPath = Join-Path $desktopPath "DevPackage\AppxManifest.xml"
Write-Host @"
  ----> Installing $devPackageManifestPath
"@
Add-AppxPackage -Path $devPackageManifestPath -Register

Launch-DevPackageInSandbox.ps1

Sources:

Param(
  [Parameter(HelpMessage = "The directory where local dev build is located; only the release build works.")]
  [String] $DevPackagePath
)
 
$ErrorActionPreference = "Stop"
 
# Validate that the local dev manifest exists
 
if (-not $DevPackagePath)
{
  $DevPackagePath = Join-Path $PSScriptRoot "..\..\src\AppInstallerCLIPackage\bin\x64\Release\AppX"
}
 
$DevPackagePath = [System.IO.Path]::GetFullPath($DevPackagePath)
 
if ($DevPackagePath.ToLower().Contains("debug"))
{
  Write-Error -Category InvalidArgument -Message @"
The Debug dev package does not work for unknown reasons.
Use the Release build or figure out how to make debug work and fix the scripts.
"@
}
 
if (-not (Test-Path (Join-Path $DevPackagePath "AppxManifest.xml")))
{
  Write-Error -Category InvalidArgument -Message @"
AppxManifest.xml does not exist in the path $DevPackagePath
Either build the local dev package, or provide the location using -DevPackagePath
"@
}
 
# Check if Windows Sandbox is enabled
 
if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue))
{
  Write-Error -Category NotInstalled -Message @'
Windows Sandbox does not seem to be available. Check the following URL for prerequisites and further details:
https://docs.microsoft.com/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview
 
You can run the following command in an elevated PowerShell for enabling Windows Sandbox:
$ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClientVM'
'@
}
 
# Close Windows Sandbox
 
function Close-WindowsSandbox {
    $sandbox = Get-Process 'WindowsSandboxClient' -ErrorAction SilentlyContinue
    if ($sandbox)
    {
      Write-Host '--> Closing Windows Sandbox'
 
      $sandboxServer = Get-Process 'WindowsSandbox' -ErrorAction SilentlyContinue
 
      $sandbox | Stop-Process
      $sandbox | Wait-Process -Timeout 120
 
      # Also wait for the server to close
      if ($sandboxServer)
      {
        try
        {
            $sandboxServer | Wait-Process -Timeout 120
        }
        catch [System.TimeoutException]
        {
            # Force stop the server if it does not automatically stop
            $sandboxServer | Stop-Process
            $sandboxServer | Wait-Process -Timeout 120
        }
 
      }
 
      Write-Host
    }
    Remove-Variable sandbox
}
 
Close-WindowsSandbox
 
# Initialize Temp Folder
 
$tempFolderName = 'DevInSandboxStaging'
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $tempFolderName
 
New-Item $tempFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
 
# Set dependencies
 
$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop'
 
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$WebClient = New-Object System.Net.WebClient
 
# Hide the progress bar of Invoke-WebRequest
$oldProgressPreference = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
 
$ProgressPreference = $oldProgressPreference
 
$vcLibsUwp = @{
  fileName = 'Microsoft.VCLibs.x64.14.00.Desktop.appx'
  url      = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
  hash     = '9BFDE6CFCC530EF073AB4BC9C4817575F63BE1251DD75AAA58CB89299697A569'
  folderInLocal = Join-Path ${env:ProgramFiles(x86)} "Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.VCLibs.Desktop\14.0\Appx\Retail\x64"
}
 
$dependencies = @($vcLibsUwp)
 
# Clean temp directory
 
Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force -Recurse
 
# Download dependencies
 
Write-Host '--> Checking dependencies'
 
foreach ($dependency in $dependencies)
{
  $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName)
 
  # First see if the file exists locally to copy instead of downloading
  if ($dependency.folderInLocal -ne $null)
  {
    $dependencyFilePath = Join-Path -Path $dependency.folderInLocal -ChildPath $dependency.fileName
    if (Test-Path -Path $dependencyFilePath -PathType Leaf)
    {
      $dependencyFilePath
      $tempFolder
      Copy-Item -Path $dependencyFilePath -Destination $tempFolder -Force
      continue
    }
  }
 
  # File does not exist locally, we need to download
 
  $dependency.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName
 
  # Only download if the file does not exist, or its hash does not match.
  if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash))
  {
    Write-Host @"
  - Downloading:
    $($dependency.url)
"@
 
    try
    {
      $WebClient.DownloadFile($dependency.url, $dependency.file)
    }
    catch
    {
      #Pass the exception as an inner exception
      throw [System.Net.WebException]::new("Error downloading $($dependency.url).",$_.Exception)
    }
    if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash))
    {
      throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file')
    }
  }
}
 
# Copy main script
 
$mainPs1FileName = 'InSandboxScript.ps1'
Copy-Item (Join-Path $PSScriptRoot $mainPs1FileName) (Join-Path $tempFolder $mainPs1FileName)
 
$dependenciesPathsInSandbox = "@('$($vcLibsUwp.pathInSandbox)')"
$bootstrapPs1Content = ".\$mainPs1FileName -DesktopAppInstallerDependencyPath @($dependenciesPathsInSandbox)"
 
$bootstrapPs1FileName = 'Bootstrap.ps1'
$bootstrapPs1Content | Out-File (Join-Path $tempFolder $bootstrapPs1FileName) -Force
 
# Create Windows Sandbox configuration file
 
$bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName)
$tempFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath $tempFolderName
 
$devPackageInSandbox = Join-Path -Path $desktopInSandbox -ChildPath "DevPackage"
$devPackageXMLFragment = ""
 
$devPackageXMLFragment = @"
    <MappedFolder>
        <HostFolder>$DevPackagePath</HostFolder>
        <SandboxFolder>$devPackageInSandbox</SandboxFolder>
    </MappedFolder>
"@
 
$sandboxTestWsbContent = @"
<Configuration>
  <MappedFolders>
    <MappedFolder>
        <HostFolder>$tempFolder</HostFolder>
        <ReadOnly>true</ReadOnly>
    </MappedFolder>
    $devPackageXMLFragment
  </MappedFolders>
  <LogonCommand>
    <Command>PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$tempFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox'</Command>
  </LogonCommand>
</Configuration>
"@
 
$sandboxTestWsbFileName = 'SandboxTest.wsb'
$sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName
$sandboxTestWsbContent | Out-File $sandboxTestWsbFile -Force
 
Write-Host @"
--> Starting Windows Sandbox
      $sandboxTestWsbFile
"@
 
Write-Host
 
WindowsSandbox $SandboxTestWsbFile

Details

About

This note is about …

See Also


Appendix

Note created on 2024-04-24 and last modified on 2024-04-24.

LIST FROM [[PowerShell - Windows Sandbox Development]] AND -"CHANGELOG" AND -"04-RESOURCES/Code/PowerShell/PowerShell - Windows Sandbox Development"

(c) No Clocks, LLC | 2024