Win32 tablet as NodeJs server using IIS & Kudu git deploy

Mad idea?

At Microsoft //build/2013, I received an Acer Iconia W3 8″ Tablet. Even back in 2013, the tablet, which come with Windows 8 Home, is under powered and with a TN LCD screen, it is very hard on eyes to use.

Since then I have upgraded it to Windows 10 via the free upgrade and Microsoft finally stop supporting the CPU and therefore unable to upgrade to newer version of Windows 10 except for security update.

Typically, old hardware could be used to run a more lightweight Linux. However, this CPU is 32-bit and has all sort of compatibility problems with Linux. Not giving up the hardware which come with a battery, consume little power and fanless. I have decided to turn it into a server for fun.

Why NodeJs?

NodeJs is lightweight, support Win32, easy to code and find libraries and frameworks. Besides, using .NET Core on Windows platform is not that challenging.

Azure App Service = IIS + Kudu + magic

I have been using Azure since inception when the Cloud and Storage services were still in beta. Nowadays, we have Azure App Service. One of the feature it offer is to use git to deploy code. Which make deployment very simple when building proof of concept.

Under the hood, App Service Windows Platform is powered by IIS and Kudu plus some infrastructure automation magic. I am going to go through how to setup both and some manual infrastructure configuration to make our own app service all on a Win32 tablet.

Installing NodeJs and git for Windows

Since I am trying to setup a Win32 tablet, I need to download the 32-bit version of NodeJs and git for Windows.

Download and install NodeJs:
https://nodejs.org/en/download/

Download and install git for Windows:
https://git-scm.com/download/win

Installing IIS

Start -> Search for Windows feature -> Click Turn Windows features on and off -> Click Internet Information Services -> Make sure Application Development Features\ASP.NET is also selected -> Click OK to start installation

After IIS is installed we will need to download and install a couple of IIS modules:

IIS URL Rewrite module is required for iisnode to work correctly. Download and install IIS URL Rewrite module from Microsoft:
https://www.iis.net/downloads/microsoft/url-rewrite

iisnode is needed to host the node process in IIS. Download and install iisnode Release from Github:
https://github.com/Azure/iisnode/releases

Installing Visual Studio

Download and install the latest version of Visual Studio:
https://visualstudio.microsoft.com/

The reason to install Visual Studio is to make sure all the relevant target files which required by Kudu are installed correctly and setting up path for commands. I believe we can get away with installing the build tools and the target packages only. But since this is a home project, it is easier to just install Visual Studio.

Installing Kudu

Download Kudu Release from GitHub
https://github.com/projectkudu/kudu/releases

Open the downloaded zip file and follow the steps below:

  1. Copy KuduWeb to C:\inetpub and create App_Data folder in KuduWeb.
  2. Right click on KuduWeb -> Properties -> Security tab -> Edit permission -> Add IIS_IUSRS with Read & execute, List folder contents and Read permission
  3. Open IIS manager, create a new application pool called KuduWeb then edit the Advanced Settings and change Identity to LocalSystem so that Kudu can manage IIS
  4. Create a new website calling KuduWeb selecting KuduWeb as application pool and set to path the C:\inetpub\KuduWeb. Set a port number of your choice. See the screenshot below.
  5. Copy SiteExtensions\Kudu to C:\inetpub and rename it to Kudu.Services.Web
  6. Create a new folder apps in C:\inetpub and set Users group with full access permission to C:\inetpub\apps which is where new sites are created by Kudu.

You may choose to copy contents of KuduWeb folder directly into wwwroot and skip step 2-4 above. That means Kudu will be installed on the IIS Default Web Site, however you will still need to change the Identity of the DefaultAppPool to use LocalSystem. The reason to create a separated website is to allow me to use the default web site as a reverse proxy later.

Open browser and go to http://localhost:38380 (where 38380 is your Kudu port number) will open the Kudu Dashboard. Click Admin at the top right corner and verify the setup is done correctly.

Creating new website using Kudu

On Kudu Dashboard, click Create Application and enter the name of the website and click the Create application button. Kudu will generate a SCM and application website using a randomly generated port number.

Setting up Reverse Proxy

Now the SCM and website are created, however they are protected by firewall and it is not accessible outside of localhost.

Download and install Application Request Routing module:
https://www.iis.net/downloads/microsoft/application-request-routing

In IIS Manager open Default Web site -> Under IIS section open URL Rewrite -> Right click and Add Rule(s)... -> Select Reverse Proxy

Create a rule for the SCM of the new website using the details above. Service URL localhost:23669. Note that, I also have an outbound rule to rewrite domain names of links in HTTP responses. In this case I use nodejs-website.scm.mytablet as the domain name.

Double click to edit the new rule and add a condition which check the host name with a regular expression pattern.

  1. Condition Input: {HTTP_HOST}
  2. Patterns: ^nodejs-website.scm.mytablet$
  3. Click OK then Apply on the right in IIS manager

Follow the same instruction to create a new rule for the application website. Application URL localhost:33425 and rewrite the domain name to nodejs-website.mytablet. Make sure you also add a condition to the rule using ^nodejs-website.mytablet$

Edit Bindings of the Default Web Site and add two bindings. One for the SCM and one for the application.

Now add these two domains into your hosts file or DNS server pointing to the IP of the server. Then you may access the SCM via http://nodejs-website.scm.mytablet. Check firewall rule if that does not work. TCP Port 80 need to accept inbound request.

Deploy NodeJs website with git

Now with the above Reverse Proxy setup. The GIT URL can be access outside of the machine via HTTP port 80 on http://localhost:23669/nodejs-website.git http://nodejs-website.scm.mytablet/nodejs-website.git

However, there are some quirks with Win32 and Kudu which will cause error when deploying NodeJs application. Win32 does not have C:\Program Files (x86) folder, however Kudu is expecting a ProgramFiles(x86) environment variable pointing to C:\Program Files (x86). To work around this, go to the Configuration of the nodejs-website and add a custom property ProgramFiles(x86) pointing it to C:\Program Files. This will allow the deployment to go through.

When Kudu create a new application, it will create a new user profile to go with the new application pool which it created for the application website. However the user profile location is not set correctly which will cause npm install to fail. Add the following custom properties to get around this problem. Note that, in this example, nodejs-website is the user profile created by Kudu.

Property nameProperty value
USERPROFILEC:\Users\nodejs-website
APPDATAC:\Users\nodejs-website\AppData\Roaming
LOCALAPPDATAC:\Users\nodejs-website\AppData\Local

After adding the above custom properties, we are ready to deploy the application.

For details on how to create a NodeJs website, there are many different frameworks and ways to do so. I am going to skip the details. However, I am using koa in this example.

Create server.js with the following code and commit it to master branch. iisnode will run server.js automatically after the deployment. iisnode will provide a PORT via environment variable for NodeJs HTTP server to listen to. Hence, the code to start the server is written this way app.listen(process.env.PORT || 3000);

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = 'Hello World from NodeJs website';
});

app.listen(process.env.PORT || 3000);

Now create a new remote for the git deployment. Let’s call it deploy and using the example above run this git command:

git remote add deploy http://nodejs-website.scm.mytablet/nodejs-website.git

To deploy, run the following git command:

git push deploy master

We should see a series of output prefixing with remote which shows the things happening during the deployment. After the git command is finished. Let’s check out the Deployments of nodejs-website on Kudu. Click View Log to review the deployment logs for debugging.

Now the NodeJs application is deployed, we can hit the site using the domain which we setup earlier. Open the browser and go to http://nodejs-website.mytablet/

That’s it! Now, I have my own version of app service running on an old Win32 tablet. We can further secure the site by adding authentication and HTTPS.

Conclusion

We have seen how to install Kudu and its dependencies such as IIS, NodeJs and git. We have also take the extra step to setup a reverse proxy and additional bindings to allow the server to serve different websites via different host name. We have also work around some quirks around Win32 and Kudu. Finally, we have deployed the NodeJs application to the server with git.

Next, I wonder if I could host the function app runtime in there too. It is a project for another day 🙂

References:

SAFE stack 1.0 and upgrade to 1.0

The SAFE stack 1.0 had been released for about two months now. I am looking at the different between 1.0 compared to the older version which one of the project I am working on is using. The best way to learn about it – is to try to upgrade the project to match what’s in version 1.0.

Upgrade Challenges

Fable.Core and Fable.Elmish 2 -> 3

The biggest change I see is the major version change of the Fable.Core and Fable.Elmish packages. These major version upgrade come with namespace / module reorganization and splitting some packages into smaller packages. Notable changes I see are:

  • Elmish.Browser.Navigation -> Elmish.Navigation
  • Elmish.Browser.UrlParser -> Elmish.UrlParser

There are some minor breaking change but they can be resolve pretty easily with a couple of minutes of googling.

Fable.React 4 -> 5

Similar to other packages, there are changes in namespace / module reorganization:

  • Fable.Helpers.React -> Fable.React
  • Fable.Helpers.React.Props -> Fable.React.Props

Fable.PowerPack and Thoth.Fetch

Fable.PowerPack is now archived on github and the pack is split into smaller individual packages, such as Fable.Fetch and Fable.Promise.

As of the time of writing Fable.Fetch actually commented out function like fetchAs in the source code. As a result, I decided to replace fetchAs with Thoth.Fetch which under the hood still use Fable.Fetch. But Thoth.Fetch come with the ability to deserialize the response using Thoth and tryFetchAs function to return a F# Results directly which make Railway Oriented Programming much easier.

However, there are some quirks to use Thoth.Fetch. When opening the module we need to open Fable.Fetch too. Below is the code snippet to do so correctly.

open Fetch
open Thoth.Fetch

Elmish Cmd changes

The Cmd in Elmish also come with “breaking” change. Cmd.ofPromise is now Cmd.OfPromise.either. The reason why breaking is quoted is because there is actually an upgrade path using just a slightly different API on Cmd and it is also documented very clearly. However, the catch is that the new API is hidden behind a compiler constant called FABLE_COMPILER. If the existing client side project does not already have this FABLE_COMPILER constant setup in the project file. The new API is not available to use. So a manual editing of the client side fsproj file is needed to add FABLE_COMPILER to it. You may add that like below:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <DefineConstants>FABLE_COMPILER</DefineConstants>
  </PropertyGroup>

Other quirks after upgrading Fable and FAKE

If running into problem trying to build the project after upgrading to SAFE stack 1.0. Try deleting the .fable and .fake folder and rebuild from a clean slate.

References:

  1. https://fable.io/
  2. https://elmish.github.io/
  3. https://safe-stack.github.io/
  4. https://compositional-it.com/blog/2017/09-22-safe-release/index.html

Raspberry Pi Desktop in VM

I am doing some research on low power computer because I wanted to get one to play with. Then I recall there is Raspberry Pi. Soon I found that we can run Raspberry Pi OS on x86 architecture in a VM nowadays. The OS is based on Debian Stretch. Here is how to do it on VMware.

    1. Download the OS ISO (about 2.3GB) from this link https://www.raspberrypi.org/downloads/raspberry-pi-desktop/
    2. Create a new VM in VMware. Point the installer disk image to the ISO and pick Debian 9 64-bit as the OS. You can actually do the same with VirtualBox. As far as I know VirtualBox is actually easier but I am running VMware at the moment.
    3. Start the VM and follow the instruction to install the OS. Unless you have special requirements, it is pretty straight forward.
    4. After installing the OS and the system boot up the first time. The first thing we notice is the resolution is quite low. To improve that, open terminal and install the open VM tools.
      sudo apt-get update
      sudo apt-get install open-vm-tools
      
    5. Mount the VMware Tools. We need to install VMware Tools in a Linux virtual machine using a Compiler. After mounting the tools, open a terminal to the path of the tools.
      vmware_tools_cdrom_contents
      Note the version number of the tools and run the following commands to install it. You need to change the version number accordingly.

      cp VMwareTools-10.2.0-7259539.tar.gz /tmp/
      cd /tmp
      tar -zxvf VMwareTools-10.2.0-7259539.tar.gz
      cd vmware-tools-distrib
      sudo ./vmware-install.pl
      

      Follow the prompt to complete the installation.

    6. Reboot the machine, then to change resolution, open terminal again and run:
      lxrandr
      

      pi_change_resolution

    7. If you want to have better integration with VMware in a desktop environment, also install the open VM tools for desktop.
      sudo apt-get install open-vm-tools-desktop
      
    8. We are done.
      pi_desktop

References: