Sohaibs Blog

Deploying a .Net Core Console app as Azure WebJob

January 21, 2021

If you are using Azure App Service, WebJobs are a great way of running background tasks without provisioning new resources or incurring additional costs. However, Deploying a Console app as a WebJob to Azure is not the smoothest of experiences, specially if you want to setup Continuous Deployment. In this post I’ll take you through the entire process, and hopefully save you some time.

Overview

An Azure App Service instance can host multiple WebJobs. WebJobs can be deployed by placing their binaries at a predefined location, either manually, OR programmatically using a build script.

Azure supports two type of WebJobs:

  1. Triggered
  2. Continuous

The default location for deploying triggered WebJobs is: d:\home\site\wwwroot\app_data\jobs\triggered\<name of job> The default location for deploying continuous WebJobs is: d:\home\site\wwwroot\app_data\jobs\continuous\<name of job>

Manual Deployment

If you want to get up and running quickly and want to forego CI/CD, you can manually deploy your console app as a WebJob using the Kudu Console.

Based on the type of WebJob, create a directory structure for your code according to the path shown above and upload your binaries to it. If the entry-point for your application is an .exe file, you’re good to go, however, if you are using .dll files, you may have to create a run.exe or run.cmd file to allow Azure to recognize which file to run. Your run.cmd file may be as simple as

MyWebjob.dll

Continuous Deployment with Kudu App Service

There are multiple methods of setting up CI/CD for Azure App Service. For the purpose of this tutorial, we will use Kudu App Service. However, you will easily be able to translate the following steps to the platform of your choice.

When you set up Continuous Deployment for an App Service with Kudu, a deployment script is generated automatically. This script handles the setup and deployment of your application. You can modify this deployment script in order to specify where your WebJob binaries ought to be published.

To access the default deployment script used by Kudu, open the Tools menu in the Kudu Debug Console and select ’Download Deployment Script‘. Kudu Console The downloaded folder contains 2 files, .deployment and deploy.cmd. Add both files to the root folder of your repository so that you can modify and commit them with your code.

Alt Text

The file we will be editing is the deploy.cmd file.
If this is your first time working with a deployment script, do not feel overwhelmed, we will only be editing a small part of it.

Deployment Script

As you will see in the Deployment section of the attached script, MSBuild is used to restore, build and publish the code for your App Service.
You will need to add a similar step for your Console App project with a slight modification:
use the OutputPath option to specify the location where the binaries for your WebJob need to go. This example is for a triggered WebJob so we will set this option to "%DEPLOYMENT_TEMP%\app_data\jobs\triggered\AnalyticsEmail"

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

:: ----------------------
:: KUDU Deployment Script
:: Version: 1.0.17
:: ----------------------

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

:: Verify node.js installed
where node 2>nul >nul
IF %ERRORLEVEL% NEQ 0 (
  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

IF NOT DEFINED DEPLOYMENT_SOURCE (
  SET DEPLOYMENT_SOURCE=%~dp0%.
)

IF NOT DEFINED DEPLOYMENT_TARGET (
  SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
)

IF NOT DEFINED NEXT_MANIFEST_PATH (
  SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest

  IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
    SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
  )
)

IF NOT DEFINED KUDU_SYNC_CMD (
  :: 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
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------

echo Handling ASP.NET Core Web Application deployment with MSBuild16.

:: 1. Restore, Build and publish
call :ExecuteCmd "%MSBUILD_16_DIR%\MSBuild.exe" /restore "%DEPLOYMENT_SOURCE%\AppServiceProject\AppServiceProject.csproj" /p:DeployOnBuild=true /p:configuration=Release /p:publishurl="%DEPLOYMENT_TEMP%" %SCM_BUILD_ARGS%
IF !ERRORLEVEL! NEQ 0 goto error

::--------THIS IS THE CODE WE HAVE ADDED--------------
::----------------------------------------------------

:: 1. Restore, Build and publish WebJob
call :ExecuteCmd "%MSBUILD_16_DIR%\MSBuild.exe" /restore "%DEPLOYMENT_SOURCE%\WebJobProject\WebJobProject.csproj" /p:DeployOnBuild=true /p:configuration=Release;OutputPath="%DEPLOYMENT_TEMP%\app_data\jobs\triggered\MyWebJob" /p:publishurl="%DEPLOYMENT_TEMP%" %SCM_BUILD_ARGS%

::----------------------------------------------------
IF !ERRORLEVEL! NEQ 0 goto error

:: 4. Run web job deploy script
IF DEFINED WEBJOBS_DEPLOY_CMD (
  call :ExecuteCmd "%WEBJOBS_DEPLOY_CMD%"
)

:: 2. 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

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto end

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

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

:exitSetErrorLevel
exit /b 1

:exitFromFunction
()

:end
endlocal
echo Finished successfully.

Making this small change in the deployment script will allow the Kudu App Service to build and deploy your WebJob to the correct location.



Schedule

We are building a triggered WebJob so we must have some sort of trigger to to execute it. For this demonstration we will use a scheduled or time-triggered WebJob. This will require a cron expression. All we need to do is create a settings.job file inside our Console App directory which contains a cron expression against a schedule property.

{
    "schedule": "0 */15 * * * *"
}

When our WebJob is deployed, the schedule property will automatically be used to set up a schedule for our WebJob

Congratulations! you just deployed a WebJob to Azure and didnt have to pull your hair out while doing it.

If you would like more information about how Kudu handles WebJobs, you can visit the kudu project wiki on github.


Sohaib Tariq is a fullstack developer and serial procrastinator. This is a blog about his wanderings in technology and life. You can reach him on linkedin or via Email.