Remote Visual Studio Code PowerShell development on a Windows Sandbox instance

You can develop PowerShell scripts on your workstation and run and test them there too… But sometimes… That’s not what you want. You could break your workstation with PowerShell code, and your workstation is not clean with all features installed, or settings enabled that your target systems don’t. And that’s where Visual Studio Code and its Remote SSH extension come into play. This blog post will show you how to combine that with a Windows Sandbox instance.

What is Remote SSH in VSCode?

“The Visual Studio Code Remote – SSH extension allows you to open a remote folder on any remote machine, virtual machine, or container with a running SSH server and take full advantage of VS Code’s feature set. Once connected to a server, you can interact with files and folders anywhere on the remote filesystem.

No source code needs to be on your local machine to gain these benefits since the extension runs commands and other extensions directly on the remote machine.”

Source: https://code.visualstudio.com/docs/remote/ssh

Why use Remote SSH?

Instead of editing and running PowerShell scripts on your workstation or in a VM (Hyper-V/VMware, etc.), you can start a clean Windows Sandbox instance on your machine and run and save your code on that. The significant benefit of a Windows Sandbox instance is that it’s clean every time you start it with no previous code or software. You can also do that by reverting to a snapshot in a VM, but running Visual Studio Code in a VM is not always as easy as running it from your workstation.

How does this Windows Sandbox solution work?

I tried several different approaches and wanted to automate as much as possible. It’s just three clicks now after running the script before you have a Visual Studio Code connected to Windows Sandbox 🙂 Below I listed what the script automates for you, and you only have to have Visual Studio Code and the Windows Sandbox feature installed in Windows.

  • Installation of the OpenSSH client feature if it’s not already installed
  • Installation of the Remote-SSH extension in Visual Studio Code if it’s not already installed
  • Creation of a shared folder between your workstation and Windows Sandbox in C:\VSCodeSandbox
  • Creation of a public/private key pair for SSH access
  • Starting the SSH Agent service
  • Adding the private key to your SSH key library
  • Creation of a WSB configuration file for Windows Sandbox with a logon script that executes all the configuration steps
  • Creation of the logon script, which does the following steps:
    • Start Transcript logging in the shared C:\VSCodeSandbox folder.
    • Download and install the OpenSSH server from GitHub
    • Creation of a local vscode Administrator user account
    • Retrieve and save the IP address in the shared C:\VSCodeSandbox folder, which will be used to connect to the OpenSSH server (Windows Sandbox instances have a different IP address every time)
  • Start Windows Sandbox and run all the commands listed above to setup everything needed for Visual Studio Code Remote SSH access
  • Wait until it’s done and connect to Windows Sandbox from Visual Studio Code

Preparation before running it for the first time

You probably also have a lot of extensions installed in Visual Studio Code. If you want to have those available in your Windows Sandbox remote session, you must configure this setting in Visual Studio Code first:

  • Go to File, Preferences, Settings (Or press CTRL+,)
  • Search for default extensions and select Remote – SSH on the left
  • Select Add Item and enter the name of the extension. In this example, I only added the PowerShell extensions by entering ms-vscode.powershell

  • Press Ok en close the Settings tab

You can find the name of an extension by selecting the extension and looking at the More Info part on the right (The Identifier line)

But how does that look in real life?

Below are the things you will see when running the script:

  • Checks for the Windows Sandbox, Remote SSH extension, Visual Studio Code, Shared Folder, and OpenSSH client are done, and the SSH keys are created.
Windows Sandbox Feature is installed, continuing...
Remote-SSH extension is already installed. Continuing...
Visual Studio Code is already installed, continuing...
Folder C:\VSCodeSandbox already exists, continuing...
OpenSSH client found, continuing...
Creating SSH Keys, importing private key and copying .pub file to C:\VSCodeSandbox...
Starting Windows Sandbox...
  • Windows Sandbox is started:
  • The script waits for all the installation steps and displays on the screen that it’s waiting every 10 seconds. When done, it will start Visual Studio Code and connect to Windows Sandbox.
Waiting for installation of OpenSSH in Windows Sandbox...
Waiting for installation of OpenSSH in Windows Sandbox...
Waiting for installation of OpenSSH in Windows Sandbox...
Installation done, continuing...
Starting Visual Studio Code and connecting to Windows Sandbox...

  • You can see in the right corner that it’s connecting to 172.31.163.170. At the top, you will have to select Windows to continue. (You will have to choose this for every new Windows Sandbox instance)

Select Continue to add the fingerprint of the SHA256 key (You will have to do this for every new Windows Sandbox instance)

  • Answer Yes, I trust the authors to trust scripts in the Shared Folder.

  • Visual Studio Code Server is now being installed using SSH.

  • The top bar shows that Visual Studio Code is now connected to your Windows Sandbox instance using SSH.

Note: Sometimes, installing VS Code Server in Windows Sandbox takes too long, and a message will pop up in Visual Studio Code. You can press Retry to continue the process.

And how can I use it from there?

Visual Studio on your workstation is now connected to Windows Sandbox. In the screenshot below, I opened the vscode_sandbox.ps1 installation script from within Windows Sandbox ran the whoami command in the terminal to show you that you’re logged in using the newly created vscode user.

Suppose you added the PowerShell extension to the Remote.SSH Default Extensions list in your Preferences, you are ready to start developing and testing PowerShell scripts in your Windows Sandbox instance. (Or other things like Python, which I might start learning someday 😉 )

Note: If Windows Sandbox is closed and you start a new Visual Studio Code session, it will try to connect to it again. Just select File, Open Folder (Or recent), and select a folder on your workstation to start a new local Visual Studio Code session again.

The script

Below is the script that I created to automate this all. Run it as Administrator and enjoy!

#Requires -RunAsAdministrator

# Check if Windows Sandbox is installed
try {
    Get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM -ErrorAction Stop | Out-Null
    Write-Host ("Windows Sandbox Feature is installed, continuing...") -ForegroundColor Green
}
catch {
    Write-Warning ("Windows Sandbox Feature is not installed, exiting...")
    break
}

# Check if VSCode is installed, exit if not
if (code --help) {
    Write-Host ("Visual Studio Code is already installed, continuing...") -ForegroundColor Green
}
else {
    Write-Warning ("Visual Studio Code is not installed, exiting...")
    break
}

# Check if Remote-SSH extension is installed, install if not
if ((code --list-extensions | Select-String 'ms-vscode-remote.remote-ssh').Length -gt 0) {
    Write-Host ("Remote-SSH extension is already installed. Continuing...") -ForegroundColor Green
}
else {
    Write-Warning ("Remote-SSH extension is not installed. Installing now...")
    code --install-extension ms-vscode-remote.remote-ssh | Out-Null
}

# Create a C:\VSCodeSandbox folder if it does not exist
if (-not (Test-Path -Path C:\VSCodeSandbox)) {
    New-Item -Type Directory -Path C:\VSCodeSandbox -Force:$true -Confirm:$false | Out-Null
    Write-Host ("Created C:\VSCodeSandbox folder") -ForegroundColor Green
}
else {
    Write-Host ("Folder C:\VSCodeSandbox already exists, continuing...") -ForegroundColor Green
}

# Install OpenSSH Client if needed
if (-not (Test-Path -Path C:\Windows\System32\OpenSSH\ssh-keygen.exe)) {
    Add-WindowsCapability -Online -Name OpenSSH.Client* -ErrorAction Stop
    Get-Service ssh-agent | Set-Service -StartupType Automatic
    Start-Service ssh-agent
    Write-Host ("Installing OpenSSH client...") -ForegroundColor Green
}
else {
    Write-Host ("OpenSSH client found, continuing...") -ForegroundColor Green
}

# Create SSH keys for connecting to Windows Sandbox
if (-not (Test-Path -Path $env:USERPROFILE\.ssh\vscodesshfile*)) {
    try {
        if (-not (Test-Path -Path $env:USERPROFILE\.ssh)) {
            New-Item -ItemType Directory -Path $env:USERPROFILE\.ssh | Out-Null
        }
        C:\Windows\System32\OpenSSH\ssh-keygen.exe -t ed25519 -f $env:USERPROFILE\.ssh\vscodesshfile -q -N """"
        Get-Service ssh-agent | Set-Service -StartupType Automatic
        Start-Service ssh-agent
        C:\Windows\System32\OpenSSH\ssh-add.exe $env:USERPROFILE\.ssh\vscodesshfile *> $null
        Copy-Item $env:USERPROFILE\.ssh\vscodesshfile.pub C:\VSCodeSandbox -Force:$true -Confirm:$false
        Write-Host ("Creating SSH Keys, importing private key and copying .pub file to C:\VSCodeSandbox...") -ForegroundColor Green
    }
    catch {
        Write-Warning ("Error Creating SSH keys, check logs. Exiting...")
        break
    }
}
else {
    Write-Host ("SSH keys found, continuing...") -ForegroundColor Green
}

# Remove previous ip.txt
if (Test-Path -Path C:\VSCodeSandbox\IP.txt) {
    Remove-Item -Path C:\VSCodeSandbox\IP.txt -Force:$true -Confirm:$false
}

# Create a VSCode.wsb file in C:\Windows\Temp\VSCodeSandbox
$wsb = @"
<Configuration>
    <VGpu>Enable</VGpu>
    <Networking>Enable</Networking>
    <MappedFolders>
        <MappedFolder>
              <HostFolder>C:\VSCodeSandbox</HostFolder>
              <ReadOnly>false</ReadOnly>
        </MappedFolder>
    </MappedFolders>
    <LogonCommand>
    <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -sta -WindowStyle Hidden -noprofile -executionpolicy unrestricted -file "C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\vscode_sandbox.ps1" </Command>
    </LogonCommand>
</Configuration>
"@
$wsb | Out-File C:\VSCodeSandbox\vscode.wsb -Force:$true -Confirm:$false

# Create the vscode_sandbox.ps1 for installation of OpenSSH Server, creation of local vscode admin account and vscodesshfile SSH Key
# Logging can be found in C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\sandbox_transcript.txt if needed in the Windows Sandbox VM
$vscode_sandbox = @"
Start-Transcript C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\sandbox_transcript.txt
Invoke-Webrequest -Uri https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.2.2.0p1-Beta/OpenSSH-Win64-v9.2.2.0.msi -OutFile C:\Windows\Temp\OpenSSH-Win64.msi
msiexec.exe /i C:\Windows\Temp\OpenSSH-Win64.msi /qn
New-LocalUser -Name vscode -Password ("vscode" | ConvertTo-SecureString -AsPlainText -Force)
Add-LocalGroupMember -Group Administrators -Member vscode
if (-not (Test-Path C:\ProgramData\ssh)) {New-Item -Type Directory -Path C:\ProgramData\ssh}
Copy-Item C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\vscodesshfile.pub C:\ProgramData\ssh\administrators_authorized_keys
(Get-NetIPAddress -InterfaceAlias Ethernet -AddressFamily IPv4).IPAddress | Out-File C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox\IP.txt -Force
Stop-Transcript
"@
$vscode_sandbox | Out-File C:\VSCodeSandbox\vscode_sandbox.ps1 -Force:$true -Confirm:$false

# Start Windows Sandbox using the VSCode.wsb file
Write-Host ("Starting Windows Sandbox...") -ForegroundColor Green
C:\VSCodeSandbox\vscode.wsb

# Wait for installation of OpenSSH server and creation of IP.txt
while (-not (Test-Path -Path C:\VSCodeSandbox\IP.txt)) {
    Write-Host ("Waiting for installation of OpenSSH in Windows Sandbox...") -ForegroundColor Green
    Start-Sleep 10
}
Write-Host ("Installation done, continuing...") -ForegroundColor Green

# Start new VSCode session to Sandbox using SSH key, retrieve current IP of Sandbox from C:\VSCodeSandbox\IP.txt for connection
$ip = get-content C:\VSCodeSandbox\IP.txt
Write-Host ("Starting Visual Studio Code and connecting to Windows Sandbox...") -ForegroundColor Green
code --remote ssh-remote+vscode@$($ip) C:\Users\WDAGUtilityAccount\Desktop\VSCodeSandbox

Download the script(s) from GitHub here

2 thoughts on “Remote Visual Studio Code PowerShell development on a Windows Sandbox instance

  1. NFW
    Where do I sign up for more of this?!
    Stellar work, epic post, epic everything!
    You got a blog on your learning-tech-stack?
    You must be some PS Jedi right?
    10/10 💗

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.