Create your own VS Code codespace using a custom build box

Using the Remote SSH plugin in VS Code, you can connect to any Linux VM, using it as a custom build box. While codespaces brings ease, a custom VM gives you more flexibility, if you’re willing to go through the setup process.

Planning

From Feburary 17, the option to run codespaces with your repositories, hosted in Azure (maybe even paid with credits), is gone and now all you can do is running it in GitHub while paying hard earned cash for it. But there are options. You can setup your own Azure VM and let it be your build box by using the Remote SSH plugin in VS Code. In this blog post I’ll show you all the steps you need to setup the VM, connect to it from VS Code, and finally create, build and deploy a .NET Core app with Docker.

Setup the build VM

The first step is to prepare a VM to run your builds on. This can be a local computer or a VM in the cloud, depending on what your have available. I’ll use an Azure VM in this example.

Generate key with password

To secure a VM properly, Microsoft recommends that you use a password protected key rather than just a password. There are different ways of creating keys to use. In this example, I’ll show you how to create the key in Azure cloud shell associated with your personal login, but there are other ways to do it also.

If you don’t already heave a password protected key, create one with this command in the cloud shell. This will create a 4096 bits key for the user name azureuser. Remember this user name because you’ll use it later on. You can change this user name to whatever you prefer. This command will ask you for a password. This password will be used whenever this key is being used to make note of it somewhere.

1
2
3
4
5
ssh-keygen \
  -m PEM \
  -t rsa \
  -b 4096 \
  -C "azureuser"

This key will be stored in the .ssh folder in your cloud account. It’ll create two files, one for your private key (id_rsa) and one for the public (is_rsa.pub).

Create SSH Key in Azure

Whenever you’re creating a new VM with key authentication, the public key will be used as the VMs way to authenticate you. To make it easier to handle the public key, you can create an SSH Key in Azure that holds the public key so it’s ready when you create a VM.

Search for SSH Key in Azure and create a new instance. In the public key section, paste in the public key you just created (from id_rsa.pub). To show the content of that file, simply type cat id_rsa.pub inside the .ssh folder.

Create VM

If you create the VM in the portal, make sure you select Linux image. For this post, I used Ubuntu 18.04 LTS. Also deselect Availability options if they are preselected for you.

As size, I’m using a Standard_B1s with 1 VCPU and 1 GB of RAM costing me around 9 USD/month. This is actually enough to even create docker builds.

For the authentication, select SSH public key. Username should be the same as you used when creating the key earlier on. If you created an SSH Key from you public key in the previous step, then select Use existing key stored in Azure as SSH public key source. You’ll then be able to select from a list the SSH key.

Make sure SSH port 22 is open so we can connect to the VM later on.

Considering OS disk, you have several options. Running a managed disk will give you several improvements regarding reliability and flexibility. You can dynamically increase the size of the disk (but not decrease) something you can’t easily with an unmanaged page-blob disk. A 32 GB managed standard ssd disk will currently cost you around 2.60 USD/month.

Options under Networking can be left as default, unless you have specific needs.

Under Management, make sure to select Auto-shutdown to save a little more on your VM cost.

Click Review + create and then Create.

If you don’t want to pay for a constand static IP then you’ll get a new IP each time you restart your VM. A better way of doing this is to assign it a DNS name that you can use when connecting to it. This name needs to be unique in the region where you have your VM. If you didn’t set a DNS name when you created the VM then you can do that from the VM overview page.

Create VM with Azure CLI

If you want to create the VM in Azure CLI from the cloud shell, use the following commands. Since a VM creates a number of resources (disk, nic, etc) it’s easier if the VM gets it’s own resource group to stay in. Then, if you want to delete it you know exactly what items to delete.

The parameter --generate-ssh-keys will only generate new keys if you don’t have any existing ones. Since we created keys above, they will be used instead and the command will then ask you for the password for the keys.

As described in the section about creating a VM in the portal, it’s beneficial to assign a DNS name to your VM. Change the variable uniquednsname below to something unique before running the script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
subscription=<subscription guid>
location=westeurope
resourceGroup=linuxbox
vmName=linuxbox
uniquednsname=linboxtest
image=UbuntuLTS
username=azureuser

# Create a new resource group
az group create --name $resourceGroup --location $location --subscription $subscription

# Create the VM
az vm create \
  --subscription $subscription \
  --resource-group $resourceGroup \
  --name $vmName \
  --image $image \
  --admin-username $username \
  --generate-ssh-keys \
  --storage-sku StandardSSD_LRS \
  --os-disk-size-gb 30 \
  --size Standard_B1s \
  --public-ip-address-dns-name $uniquednsname

# Enable auto shutdown at 6 pm
az vm auto-shutdown \
  --subscription $subscription \
  --resource-group $resourceGroup \
  --name $vmName \
  --time 1800

Config details for disks, vm and vm sizes.

The response from creating the VM will look like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "fqdns": "linboxtest.westeurope.cloudapp.azure.com",
  "id": "/subscriptions/___/resourceGroups/linuxbox/providers/Microsoft.Compute/virtualMachines/linuxbox",
  "location": "westeurope",
  "macAddress": "00-11-BB-AA-55-22",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.16",
  "publicIpAddress": "100.200.200.100",
  "resourceGroup": "linuxbox",
  "zones": ""
}

Test the connection by using SSH in the cloud console

1
ssh azureuser@linboxtest.westeurope.cloudapp.azure.com

On new VM

Now it’s time to setup the tools you need/want on your build box. In the examples below, I’ll build a ASP.NET Core app, build it as a docker container and deploy it azure so these are the tools I’ll install here.

Install docker

1
2
3
4
5
6
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update
apt-cache policy docker-ce
sudo apt install -y docker-ce

Verify docker is running

1
sudo systemctl status docker

As it currently stands, you can only run the docker commands by logging in as root or if running with sudo. But from VS Code, we’ll run as your custom user. We therefore need to enable that user to run docker commands.

Add your user to the docker groups

1
2
3
4
5
sudo groupadd docker
sudo usermod -aG docker azureuser
newgrp docker
sudo chown azureuser:azureuser ~/.docker -R
sudo chmod g+rwx ~/.docker -R

Docker is working from your user account if you can run docker version without getting any permission errors at the end of the message. If you get permission errors then try to logout and login as your user, or even restart the VM.

Install .Net 5 SDK and runtime

1
2
3
4
5
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y apt-transport-https
sudo apt-get install -y dotnet-sdk-5.0

Install Azure CLI

There’s a all-in-one script to install Azure CLI on Linux. If you want to do it the long way you can read more here.

1
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Setup your local work computer

Now the VM is prepared and we continue with setting up the things you need on your local work computer

Configure SSH

The basic idea behind the workflow is that your work computer can communicate with the Linux VM using SSH. The VM is configured to authenticate using a key so we need to start by setting up the key configuration. The SSH configuration files can be found in the .ssh folder in your user folder.

Copy the file .ssh/id_rsa in your cloud shell to a file with the same name on your local work computer. This is a secret file so don’t store it anywhere public on the computer.

Create a file config in the .ssh folder on your computer. Inside the file, add the following setup, with adjustments to point to your created VM.

1
2
3
4
Host linuxbox
HostName linboxtest.westeurope.cloudapp.azure.com
User azureuser
IdentityFile ~/.ssh/id_rsa

Try the SSH connection by running the following command in Powershell. You should be prompted for the password you’ve configured for the key.

1
ssh linuxbox

If everything works well and you’re able to connect, then you can continue to the next section.

Configure VS Code

In VS Code, find and install the extension Remote - SSH by Microsoft.

Open the Remote Explorer tab to the left and select SSH Targets in the drop-down menu. You should be able to see linuxbox as an SSH Target. The name linuxbox will depend on the Host you defined in the config file. Right-click on linuxbox and select to open in same or new window.

You will be prompted to select what target OS type that you’re connecting to. Select Linux. This setting will be stored in VS Code as the setting remote.SSH.remotePlatform.

Next you’ll have to enter the password for the key used.

If you see a green little box down in the left corner saying SSH: linuxbox then VS Code is connected and you’re ready to create your first remotely built program with this setup.

One thing you’ll notice soon is that each time you have to reload VS Code (i.e. you add an extension that needs a reload, or you select a folder to open) you’ll also have to enter your SSH key password again. This is just to keep your connection secure so you have to get used to it.

If you have a lot of extensions installed on your local VS Code that you also want to use on your remote build machine then there’s now a new section in the Extensions tab, called SSH: LINUXBOX - INSTALLED. Go through your local extensions and look for them that have Install in SSH: linuxbox in blue written under them. These can also be installed on your remote server.

Some extensions that are useful to install on your VM, in this specific setup, are:

  • Docker (from Microsoft) - if you’ve configured docker to run from your local user then you can control docker on the VM from this Docker extension.
  • Azure Account (from Microsoft) - enables you to login to Azure from your VM. Inside the Docker extension, you can now browse your Azure Container Registry resources you have.
  • C# (from Microsoft) - C# editing support

Create a sample application

VS Code is now connected to your VM and everything done in VS Code will be executed on the VM. If you open a terminal window in VS Code (press CTRL + '), this will also be connected to the VM.

We’ve previously installed .NET Core CLI on the VM so let’s use that to create an app. Run this command to create the app structure. The app will also be loaded in VS Code.

1
2
dotnet new mvc -o MyNiceApp
code -r MyNiceApp

You can test run your application with dotnet run

Create a docker file named dockerfile in the root of your project, next to the project file. Add the following code to the file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build-env
WORKDIR /app

# Copy csproj and restore as distinct layers
COPY \*.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "MyNiceApp.dll"]

To create a new image file in docker you have two options. The first is to build it with a command.

1
docker build -t myniceapp .

And the second is to right-click the dockerfile in VS Code and select Build image...

You can list your created images with

1
docker images

If needed, you can test run the image locally

1
docker run -it --rm -p 8080:80 myniceapp

Assuming you have Azure Container Registry up and running, you can now push this image there directly from the terminal in VS Code. First you need to login in Azure Container Registry.

1
2
az login
az acr login --name mycontainerregistry

When that is done, you can tag the image and finally push it. Replace myacr with the name of your container registry before running the commands.

1
2
docker tag myniceapp myacr.azurecr.io/myniceapp
docker push myacr.azurecr.io/myniceapp

Your webapp is now available in Azure Container Registry to be deployed to a cluster, a container intance or an app service.

Conclusions

So, your custom build box is ready to be used. But is it worth all the fuss? Here are some pros and cons.

Pros

  • You can run a VM using Azure credits
  • You can use the above setup steps to connect to a linux computer locally that you already own and then there’s no VM charges at all.
  • You can use your build box for many repositories. The Code Space can only be used for a single repository. If you need to work on two repositories at once, then you need to pay for two running Code Spaces, but not two VMs.

Cons

  • A Code Space is very easy to setup, especially in GitHub, while a custom VM build box has many configuration steps involved
  • You have to automatically shut your VM down when you don’t need it (auto-shutdown of VM at specific time is helpful)

If you decide to build your own or use the existing GitHub one is maybe down to personal preferences. I’ll most likely end up using a combination of both. But I’m very grateful to the VS Code team for building the Remote SSH plugin making this option even possible.

Load Comments?