/ git

Let Kudu change your API endpoints in Angular when deploying to Azure Webs with Gulp

The problem

Have you ever manually changed the API endpoint in your JavaScript from https://localhost:23434 to https://prodenv.azurewebsites.net before pushing that deploy button? Have you ever forgotten to do this before deploying, resulting in a broken app?

Relying on manual steps in your deployment process is an accident waiting to happen. The example where Knight got a $460 million loss in 45-minutes due to a faulty manual deployment comes to mind. Hopefully a misconfigured API endpoint in an angular app won't be as drastic, but the point is still valid. Having a repeatable process makes things easier.

What I like to do instead of making updates in my JavaScript code by hand is to type git push production and sit back and watch my site deploy with the correct endpoints.

Enter gulp

In a recent project I used Gulp to achieve a workflow that automatically updated the environment variables my angular app needs depending on my environment. Dev variables in dev and prod variables in production.

Gulp is a tool for automating tasks in your build process. It can handle things like minifying your js and css, make your running browser automatically reflect changes in real time or in our case, generate environment variables at deployment time.

One of the really great things about gulp is the fact that it has a tons of different plugins for different problems that you can add.

Gulp-ng-constant is a library for handling the task of setting constants in your JavaScript. When doing normal backend ASP.NET development you could use things like Azure web app settings/web.config and use the CloudConfigurationManager class to read environment variables.

But it's not really optimal to expose the azure app settings via clientside technologies. So what we have to do is make sure that the constants are set at deployment/build time.


The first thing we need is a gulpfile to decsribe the tasks that we are going to use to do our transformations. We describe two commands: dev-config that visual studio runs before each build during development and prod-config which runs at deployment time.

var gulp = require('gulp');
var ngConstant = require('gulp-ng-constant');

gulp.task('dev-config', function () {
          name: 'myAngularModule.config',
          deps: [],
          constants: {
              ENV: {
                  apiEndpoint: '../../api',
                  tenant: "tenant.onmicrosoft.com",
                  sharepointUrl: "mydevtenant.sharepoint.com",
                  targetLibraryName: "Dev library",
                  appId: "6ce673fb-4827-40f3-af5d-bcaddac5f76a"

gulp.task('prod-config', function () {
          name: 'myAngularModule.config',
          deps: [],
          constants: {
              ENV: {
                  apiEndpoint: 'https://azureweb.azurewebsites.net/api',
                  tenant: "tenant.onmicrosoft.com",
                  sharepointUrl: "myprodtenant.sharepoint.com",
                  targetLibraryName: "Records",
                  appId: "dd952be6-090b-4350-8697-8181a1ff5b1d"

Notice that we are using a constants.json file for other, non-dynamic constants that we use in our app and that we use some simple gulp code to handle the dynamic variables.


    "appName": "My Awesome App"

In order to be able to run the code we just wrote we need to install gulp and gulp-ng-constant to our project directory.

npm install gulp --save-dev
npm install gulp-ng-constant --save-dev

The prod-config and dev-config commands in the gulpfile generates a dist/constants.js which we include when loading our app. It gives us a nice new angular module that contains our environment variables.

angular.module("myAngularModule.config", [])
.constant("ENV", {
	"apiEndpoint": "../../api",
	"tenant": "tenant.onmicrosoft.com",
	"sharepointUrl": "mydevtenant.sharepoint.com",
	"targetLibraryName": "Dev library",
	"appId": "6ce673fb-4827-40f3-af5d-bcaddac5f76a"


In order for our main module to use it we need to declare the module as a dependency of our base app module.

  'use strict';
  var myAngularModule = angular.module('myAngularModule ', [

And seeing as adal is more or less a requirement for all dev that we do nowadays I thought it would be nice to show how we use the constants for setting up the required endpoints.


I'm not going to cover how adal.js works seeing as it's described in the github repo's readme. For our case all we need to do is include our 'ENV' dedepency.

(function () {
  'use strict';

  var myAngularModule = angular.module('myAngularModule');

  myAngularModule.config(['$httpProvider', 'ENV', 'adalAuthenticationServiceProvider', adalConfigurator]);

  function adalConfigurator($httpProvider, ENV, adalProvider) {
      var adalConfig = {
          tenant: ENV.tenant,
          clientId: ENV.appId,
          endpoints: {
          cacheLocation: 'localStorage'

      adalConfig["endpoints"]["https://" + ENV.sharepointUrl + "/_api/"] = "https://" + ENV.sharepointUrl;

      adalProvider.init(adalConfig, $httpProvider);


Now you probably have your code that actually uses the endpoint we configured earlier in angular services. Notice that all we need to do is include the 'ENV' dep to get the ENV object which contains our constants in the same way as we did with our service above.

(function () {
    'use strict';

           .service('dataService', ['$q', '$http', 'ENV', dataService]);

    function dataService($q, $http, ENV) {

        return {
            getSomething: getSomething
        function getSomething(dataBlob) {
            var deferred = $q.defer();

                method: 'POST',
                url: ENV.apiEndpoint + "/Controller/Action",
                data: dataBlob
            }).then(function (r) {
            }, function (err) {

            return deferred.promise;


In order for Kudu to use our gulp actions we first need to pull down and modify the kudu deployment file.

We do this by going to https://azurewebsitename.scm.azurewebsites.net and downloading the script.

This will give us a deploy.cmd and a .deployment file. You need to put these in the base directory of your git repository. Kudu will then see these when you deploy and use them instead of the ones already present/generated by Kudu.

Updating deploy.cmd and .deployment

First let's tell Kudu about our deploy.cmd file by editing the .deployment we just downloaded.

command = deploy.cmd

Now let's move on to the depoy.cmd. I decided to break the file up in a few different paragraphs here so that you can have the complete file as a reference.
The first part of the file we don't have to change, it only sets things up that are required for us to keep doing magic later on.

@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off

:: ----------------------
:: KUDU Deployment Script
:: Version: 1.0.4
:: ----------------------

:: Prerequisites
:: -------------

:: Verify node.js installed
where node 2>nul >nul
  echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
  goto error

:: Setup
:: -----

setlocal enabledelayedexpansion

SET ARTIFACTS=%~dp0%..\artifacts





  :: Install kudu sync
  echo Installing Kudu Sync
  call npm install kudusync -g --silent
  IF !ERRORLEVEL! NEQ 0 goto error

  :: Locally just running "kuduSync" would also work
  SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
  SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random%


IF DEFINED MSBUILD_PATH goto MsbuildPathDefined
SET MSBUILD_PATH=%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe

Now we must update the deployment part of the script to actually perform our transformations. In order to do this we utilize the fact that the repo is cloned to one location and then later synced to the wwwroot. If we apply our transformations before the filesync happens we can do all sorts of nice things, like running gulp commands.

:: Deployment
:: ----------

echo Handling .NET Web Application deployment.

echo Restore NuGet packages
IF /I "MyProject.sln" NEQ "" (
  call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\MyProject.sln"
  IF !ERRORLEVEL! NEQ 0 goto error

echo Install npm packages
IF EXIST "%DEPLOYMENT_SOURCE%\MyProjectWeb\package.json" (
  pushd "%DEPLOYMENT_SOURCE%\MyProjectWeb"
  call npm install
  IF !ERRORLEVEL! NEQ 0 goto error

echo Install bower packages
IF EXIST "%DEPLOYMENT_SOURCE%\MyProjectWeb\bower.json" (
  pushd "%DEPLOYMENT_SOURCE%\MyProjectWeb"
  call :ExecuteCmd .\node_modules\.bin\bower install
  IF !ERRORLEVEL! NEQ 0 goto error

echo Run gulp transformations
IF EXIST "%DEPLOYMENT_SOURCE%\MyProjectWeb\gulpfile.js" (
  pushd "%DEPLOYMENT_SOURCE%\MyProjectWeb"
  call :ExecuteCmd .\node_modules\.bin\gulp prod-config
  IF !ERRORLEVEL! NEQ 0 goto error

In the code above we make sure all packages are installed in our %DEPLOYMENT_SOURCE% and then the magic happens in the line where we call gulp prod-config.

The rest of the deployment file is listed below as a reference.

echo Build to the temporary path
  call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\MyProjectWeb\MyProjectWeb.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS%
) ELSE (
  call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\MyProjectWeb\MyProjectWeb.csproj" /nologo /verbosity:m /t:Build /p:AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS%

IF !ERRORLEVEL! NEQ 0 goto error

echo KuduSync
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error


:: Post deployment stub
IF !ERRORLEVEL! NEQ 0 goto error

goto end

:: Execute command routine that will echo out when error
set _CMD_=%*
call %_CMD_%
if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
exit /b %ERRORLEVEL%

echo An error has occurred during web site deployment.
call :exitSetErrorLevel
call :exitFromFunction 2>nul

exit /b 1


echo Finished successfully.

Now when we git push production everything is handled for us without ever having to do the dreaded manual replacement of the API endpoints. Notice how it copies the dist/constants.js at the end.

Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 314 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Updating branch 'master'.
remote: Updating submodules.
remote: Preparing deployment for commit id 'e35be64bcd'.
remote: Running custom deployment command...
remote: Running deployment command...
remote: Handling .NET Web Application deployment.
remote: Restore NuGet packages
remote: All packages listed in packages.config are already installed.
remote: Install npm packages
remote: ...............................
remote: Install bower packages
remote: ...................................
remote: Run gulp transformations
remote: .................
remote: [15:24:36] Using gulpfile D:\home\site\repository\MyProject\gulpfile.js
remote: [15:24:36] Starting 'prod-config'...
remote: [15:24:36] Finished 'prod-config' after 47 ms
remote: Build to the temporary path
remote: .......
remote:   MyProjectWeb -> D:\home\site\repository\MyProject\bin\MyProject.dll
remote:   Transformed Web.config using D:\home\site\repository\MyProject\Web.Release.config into obj\Release\TransformWebConfig\transformed\Web.config.
remote:   Copying all files to temporary location below for package/publish:
remote:   D:\local\Temp\8d303d14772064c.
remote: KuduSync
remote: KuduSync.NET from: 'D:\local\Temp\8d303d14772064c' to: 'D:\home\site\wwwroot'
remote: Copying file: 'bin\MyProject.dll'
remote: Copying file: 'dist\constants.js'
remote: Finished successfully.
remote: Deployment successful.
To https://user@mysite.scm.azurewebsites.net:443/MyProject.git
   d2150b0..e35be64  master -> master

Hope this helps and reduces future failed deployments in your future.

As usual, let me know in the comment section if you have any comments or ideas.

Let Kudu change your API endpoints in Angular when deploying to Azure Webs with Gulp
Share this

Subscribe to Cognit Dev Blog