wsb.psm1
function start-wsb {
#
# start-wsb creates a .wsb file in the temp directory whose
# content is controlled by this function's parameters and
# then launches the sandbox.
#
param(
[int ] $memoryInMB = $null,
# Features that are enabled by default
[switch] $disableVGpu,
[switch] $disableNetworking,
[switch] $disableAudioInput,
[switch] $disableClipboardRedirection,
# Features that are disabled by default
[switch] $enableVideoInput,
[switch] $enableProtectedClient,
[switch] $enablePrinterRedirection,
[string] $logonCommand = $null,
[string] $visiblePsCommand = $null,
[string] $psScript = $null,
[array ] $mappedFolders = @(),
[switch] $keepWsb
)
#
# The WindowsSandbox.exe process might be running even when
# no sandbox is active. Therefore checking for
# WindowsSandboxClient.exe
#
$runningSandbox = get-process -name WindowsSandboxClient -errorAction silentlyContinue
if ($runningSandbox) {
write-host 'Windows Sandbox is already running.' -foregroundColor red
return
}
[xml] $xml = '<Configuration></Configuration>'
function add-config {
param(
[System.Xml.XmlElement] $Parent,
[string ] $ElementName,
[string ] $Value
)
if ($Value) {
$newElement = $xml.CreateElement($ElementName)
$newElement.InnerText = $Value
$Parent.AppendChild($newElement) | Out-Null
}
}
# Use DocumentElement to avoid the string conversion issue
$root = $xml.DocumentElement
if ($psBoundParameters.ContainsKey('memoryInMB')) { add-config $root 'MemoryInMB' $memoryInMB }
if ($disableVGpu ) { add-config $root 'VGpu' 'Disable' }
if ($disableNetworking ) { add-config $root 'Networking' 'Disable' }
if ($disableAudioInput ) { add-config $root 'AudioInput' 'Disable' }
if ($disableClipboardRedirection) { add-config $root 'ClipboardRedirection' 'Disable' }
if ($enableVideoInput ) { add-config $root 'VideoInput' 'Enable' }
if ($enableProtectedClient ) { add-config $root 'ProtectedClient' 'Enable' }
if ($enablePrinterRedirection ) { add-config $root 'PrinterRedirection' 'Enable' }
$cmd = $logonCommand
if ($psScript) {
#
# Somewhat convoluted addition of the new folder because
# mappedfolder is declared with [array] which makes it
# fixed size
#
$psScript_ = resolve-path $psScript
$mappedFolders = @($mappedFolders) + @(
@{ hostFolder = split-path $psScript_
sandboxFolder ='c:\scriptDir'
readOnly = $true
}
)
#
# executionPolicy is set to prevent
# File C:\scriptDir\...ps1 cannot be loaded because running scripts is disabled on this system.
#
$cmd = "cmd /c `"start powershell.exe -noLogo -noExit -executionPolicy unrestricted -file c:/scriptDir/$(split-path $psScript_ -leaf)`""
}
elseif ($visiblePsCommand) {
$cmd = $visiblePsCommand -replace '"', '\"'
#
# If cmd is a cmdlet (such as get-date), the -executionPolicy parameter is not needed.
# It is needed however, if cmd is the path of a script.
#
$cmd = "cmd /c `"start powershell.exe -noLogo -noExit -executionPolicy unrestricted -command $cmd`""
}
# MappedFolders
if ($mappedFolders.count -gt 0) {
$mappedFoldersElem = $xml.CreateElement('MappedFolders')
foreach ($folder in $mappedFolders) {
$mf = $xml.CreateElement('MappedFolder')
add-config $mf 'HostFolder' $folder.HostFolder
if ($folder.SandboxFolder) {
add-config $mf 'SandboxFolder' $folder.SandboxFolder
}
$readOnlyVal = if ($folder.ReadOnly) { 'true' } else { 'false' }
add-config $mf 'ReadOnly' $readOnlyVal
$mappedFoldersElem.AppendChild($mf) | Out-Null
}
$root.AppendChild($mappedFoldersElem) | Out-Null
}
# LogonCommand
if ($cmd) {
$logonElem = $xml.CreateElement('LogonCommand')
$cmdElem = $xml.CreateElement('Command')
$cmdElem.InnerText = $cmd
$null = $logonElem.AppendChild($cmdElem)
$null = $root.AppendChild($logonElem)
}
$wsbContent = $xml.OuterXml
# Create temporary .wsb file and launch sandbox
$tempWsbPath = join-path $env:temp "sandbox_$(get-date -format 'yyyyMMdd_HHmmss').wsb"
$wsbContent | out-file -filePath $tempWsbPath -encoding UTF8 -force
write-host "Created temporary config: $tempWsbPath" -foregroundColor Green
write-host "Starting Windows Sandbox..." -foregroundColor Cyan
invoke-item -literalPath $tempWsbPath
start-sleep -seconds 4
if (! $keepWsb ) {
# Remove wsb file
write-host "Deleting temporary .wsb file..." -foregroundColor Yellow
remove-item -path $tempWsbPath -force -errorAction stop
write-host "Temporary .wsb file deleted successfully." -foregroundColor green
}
}
function start-wsbWithNotepad {
#
# start-wsbWithNotepad maps the host System32 folder into the
# sandbox and copies the host's notepad.exe to the sandbox's
# System32 directory.
#
$lang = [System.Globalization.CultureInfo]::CurrentUICulture.Name
write-host "lang = $lang"
$mappedFolders = @(
@{
hostFolder = 'C:\Windows\System32'
sandboxFolder = 'C:\host\Windows\System32'
readOnly = $true
}
)
$psCmd = 'copy-item C:\host\Windows\System32\notepad.exe C:\Windows\System32\notepad.exe'
$encodedCopyScript = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($psCmd))
$logonCommand = "powershell.exe -noLogo -executionPolicy unrestricted -encodedCommand $encodedCopyScript"
start-wsb -mappedFolders $mappedFolders -logonCommand $logonCommand
}