Rene.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 eisabled 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 appendment 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
   }
}