Home NodeJs Azure Web App continuous delivery via Github and some Kudu magic
Post
Cancel

NodeJs Azure Web App continuous delivery via Github and some Kudu magic

This post is about how toconfigure the continuous deploymentsettings for your NodeJs application in a Web App using Kudu.

You will learn how to configure your Azure Web App to automate deployments from Github and how to properly configure your environment via Kudu, tosuccessfully run your application.

Before proceeding, these are the links to the repository and the deployed application.
The application repository that I am going to deploy to the Azure Web App:
https://github.com/gdyrrahitis/angular-101

The application deployed on Azure:
https://angular-101.azurewebsites.net/

Little bit about the application

This is a simple NodeJs server, that hosts an Angular client application. The Angular client containsmany code samples on latest Angular's version, so if you are interested go take a look.

Now, there are two configurations here, one isdevelopment and the other isproduction. As you know, while coding its more handy to have tools like browser sync, watchers, etc. toimprove productivity. But devconfigurationis usually entirely different from prod, whereasvarious things change. In Angular, we wish to enable prod mode when turn to production.

Here is a small snippet from thepackage.json file

"scripts": {
    "start:prod": "npm run tsc && SET NODE_ENV=production && node server.js",
    "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
    "start:dev": "concurrently \"npm run tsc:w\" \"npm run lite\" ",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "lite": "lite-server"
  },

I went ahead and omitted some of the code for the sake of brevity.
So, as I mentioned before, there are two different configurations here. The one for production is thestart:prod and the one for development isstart:dev.

For development server I use thelite-server , by John Papa, which is ideal for a simple development process as this project needed. It provides a NodeJs server with BrowserSync support by default and it has minimal to none configuration.

For production, I created a simple express server, code can be found atserver.ts file.

In order to load the server and enable prod mode for Angular, I need to set theNODE_ENV environment variable. This is a NodeJs environment variable which is used to distinguish/flag which configuration the project is currently building.
Thestart command loads that configuration conditionally by using theif-env npm module. IfNODE_ENV value is production, it runs thestart:prod command, else it runs thestart:dev command.

  • start:dev - It compiles the typescript files, watches them for file changes and also runs the lite web server
  • start:prod - It compiles the typescript files once, sets theNODE_ENV variable to production (this syntax here is only for Windows) and then runs the express server

Quick word on the client code

So, in order to run Angular in prod mode I need to know the value ofNODE_ENV variable. But I cannot! This is a NodeJs variable and it is not availableon browser! So, whatshould I do?
Well, my workaround was to create a.json filewhich I will modify when application is build on production.

So, I created aenv-config.json file with one object:

{
    "node_env": ""
}

And in Angular's main I have the following code:

import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app.module";

let config: { node_env: string } = require("../../env-config.json");
if (config.node_env.trim() === "production") {
    enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule).catch((error) => { console.error(error); });

Pretty much self-explanatory, Ifetch the JSON file from the root of the project and I examine the value ofconfig.node_env property. If production, I enable the prod mode.
Quick and simple solution. If you wish to know more on how to properly load JSON filesas modules in your code, check out this post.

Quick word on express server

Last part on application code, before I proceed to deployment configuration and Kudu.
I setup an express server, again, very simple, what it does it to get express up and running, specify static content, catch all routes and redirect to index.html (usual stuff for a SPA) and lastly, specify the build configuration.

import * as express from "express";
import * as path from "path";
import http = require("http");
import * as fs from "fs";

const port: number = process.env.PORT || 8080;
const env: string = process.env.NODE_ENV || "development";

let app: express.Application = express();

app.use(express.static(__dirname));
app.use(express.static("src"));
app.use(express.static("node_modules"));
app.all("/*", (request, response, next) => {
    response.sendFile("index.html", { root: __dirname });
});

let contents = fs.readFileSync("env-config.json", "utf8");
let config = JSON.parse(contents);
config.node_env = env;
let json = JSON.stringify(config);
fs.writeFileSync("env-config.json", json, { encoding: "utf8" });

http.createServer(<any>app).listen(port);

Note: This is important! Take a look at the highlighted line.You can't just have your server listen on port 80. You need to grab the right port from process.env.PORT, else you might experience some weirdinternal server errors in your Web App, so be careful and always remember to fetch the port from process.env.PORT and set it manually yourself, using thelisten method.

Configuring Azure Web App

Setting up an App Service and a Web App on Azure is out of this post context, I assume you know how to do this in first place.
So after you have your Web App set, go to 'Deployment options', set to Github provider (provide your credentials if needed) and to the branch that you wish to deploy. Follow similarstepsas onthe screens below:

Choose 'Deployment options'

Then choose 'Github' as your provider

Finally, setup your credentials, the repository and the branch you wish to deploy from

 

Click OK and done!

You are all set,after a push to your repository, Azure will pick it up and deploy to the Web App.

Hosting application with some Kudu Voodoo

Now comes the fun part.

As you probably know, Azure websites use IISNode to host the node process inside IIS. They need a server.js or anapp.js as an entry point of your NodeJs server, so follow these conventions. Also,the name IIS should ring some bells. Oh yeah, you are going to need a web.configfor your IISfor your application to work correctly. We'll go through that in a second.
Lastly, you will wonder and its a fair statement, on how are you going to install all theapplication dependencies withnpm? You can't run yourapplication without them, right? What about your typescript code? In this case it is not precompiled, soI need to compile on deployment. Jeez,this is getting out of hands, I'll give up.
Just kidding, you shouldn't give up, you are just 3-4 steps away from having your application on Azure.

First of all, install the kuduscript tool globally.npm install kuduscript -g

This tool will handle the deployment script creation for you. You will need to make some minor modifications to it, maybe none, depending on your scenarios.
After installing Kuduscript,open a CMD window on your application's root and type the following command:

kuduscript -y --node --sitePath.

This will create a.deployment and adeploy.cmd files for you on that directory. (ForsitePath option, I provided the current directory, which is the root)

The.deployment file is simple:

[config]
command = deploy.cmd

It's just invoking the deploy.cmd file.
Nice,my suggestion would be toreview thedeploy.cmd file a bit, get more familiar with the process.
Now, I want not only to install the packages, but I want to compile my Typescript priordeployment. Open the file and navigate to line 100. I modified the code a bit there

:: 3. Install npm packages
IF EXIST "%DEPLOYMENT_TARGET%\package.json" (
  pushd "%DEPLOYMENT_TARGET%"
  call :ExecuteCmd !NPM_CMD! install
  call :ExecuteCmd !NPM_CMD! run tsc
  IF !ERRORLEVEL! NEQ 0 goto error
  popd
)

Take a look at line 4. Initially, it was install --production, but I wanted thedevDependencies also, in order to compile.
On line 5, I run the Typescript compiler. This is a custom script that I added there. You are free to add whatever you want, this is mostly the place where you will be toying around,installing dependencies, running tests or compiling, whatever.

You are two steps away. You need to add theweb.config file and the.iisnode.yml (yes, they are supporting yaml for IIS, isn't it great?).
The web.config is pretty standard and you won't need to bother about it, just paste the following code

<?xml version="1.0" encoding="utf-8"?>  
<configuration>  
    <system.webServer>           
      <handlers>  
           <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>  
     </handlers>  
      <rewrite>  
           <rules>  
                <rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">  
                     <match url="iisnode"/>  
                </rule>  
                <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">                      
                    <match url="^server.js\/debug[\/]?" />  
                </rule>  
                <rule name="StaticContent">  
                     <action type="Rewrite" url="src"/>  
                </rule>  
                <rule name="DynamicContent">  
                     <conditions>  
                          <add input="" matchType="IsFile" negate="True"/>  
                     </conditions>  
                     <action type="Rewrite" url="server.js"/>  
                </rule>  
           </rules>  
      </rewrite>  
   </system.webServer>  
 </configuration>

The only modificationneeded, was to set the static content folder to besrc (this is the folder where my static files reside) as you can see from the highlighted line.

Finally, my favorite part, the YAML file.
I just override the build environment, I didn't need all the other configuration, so this is the only code line in the file:

node_env: production

Remember, I want to run on production and instruct Angular to enable prod mode, soI needed to set thisNodeJs environment variable and the.iisnode.yml file helped me a lot with that.
Forfull list of configuration overrides, look here.

Finally, Deploy

Everything seems to be ready, go ahead andpush.
If you navigate to your Azure portal, tothat specific Web App and to deployment options, you will see that it's currently deploying (if not finished already). After it finishes, open that web site.

Note: If you need to fiddle around with some defects on your web.config or want to check the log files, use the Kudu portal. Just add an .scm after your host name and before azurewebsites, like: http://myhostname.scm.azurewebsites.com
Navigate to Debug Console, investigate the site, the LogFiles, etc.

Summary

In this post I've shown howimportant is Kudu for your NodeJs application! Without it, you couldn'tsee your magic hosted on Azure!
What you need to take away is, setting up your deployment is not enough. Azure will not guess what is going on with your application, you need to explicitly instruct it that this is a NodeJs app and here are the various options you wish to set. Use Kudu to make your deployment easier and remember, that even if it is a NodeJs app, you still need a web.config for instructingIIS to host your app.

Happy coding!

This post is licensed under CC BY 4.0 by the author.

C# 7 out variables, tuples & other new features

Migrating from typings to npm @types

Comments powered by Disqus.