While working on a web project recently that was written in .NET Core, there was a need for a small background task to run either on a timer, or on an Azure queue message. No worries, we can just use an Azure WebJob for that, no extra infrastructure or Azure setup required right? Well, it then occurred to me that I had never actually written a WebJob in .NET Core. What started as a “Oh you just add a WebJob in Visual Studio and you are done” quickly turned into “OK… So I need this specific beta version of this nuget package that a random Microsoft developer mentioned over here”. So here it is, here is how you create an Azure WebJob in .NET Core.
Azure WebJobs In .NET Core
Part 1 – Initial Setup/Zip Deploy
Part 2 – App Configuration and Dependency Injection
Part 3 – Deploying Within A Web Project and Publish Profiles
Part 4 – Scheduled WebJobs
Part 5 – Azure WebJobs SDK
Forewarning
It’s tempting that if you already have a WebJob written in .NET Framework, to skip through this tutorial right to the part where it mentions something specifically related to your existing project. If there is one thing I learned about WebJobs in .NET Core, is that it pays to start with the real basics. I would recommend even if you already know about WebJobs and how they work, to still start on Part 1 and work your way through. Everything from configuration to publishing is totally different than what you might have expected, so even if it feels like we are going right back to Hello World level, it’s worth it to get a total understanding of how WebJobs currently work under .NET Core.
What You Should Already Know
While we may be creating a “Hello World” type WebJob along the way, this is certainly not a beginners series on “what” WebJobs are. Infact, we probably brush over some really important basics that if you’ve never used WebJobs (Or even Azure Web Apps) before, you are going to have a really rough time. This series is more on the .NET Core part, and less on the “How do I use Azure” part. So if you’ve never created an Azure WebJob before, go ahead and create one in the full .NET Framework, have a play, then come back when you are ready to get cracking with .NET Core.
A Console App And Nothing More
Did you know that a WebJob is actually just a console application? Infact I’ve seen people upload diagnostic tools as plain .exe’s as “WebJobs” to an Azure Web App just so they can run a specific tool on the same machine as their web app. For example a tool to spit out what SSL certificates were available on the machine, or what it thought certain environment variables were set to.
So let’s start there. Let’s forget all about the concept of a “WebJob”, and let’s just create a simple console application that runs in an Azure Web App.
First go ahead and create a new .NET Core Console Application. At the time of writing, there are no Visual Studio templates for creating web jobs for .NET Core. So for example you may see below inside VS:
But this is for full framework only. In the grand scheme of things, it’s not a big deal. The VS Template just installs a few nuget packages and gives us some boiler plate code, but realistically it’s just a console application anyway.
So what we want is :
After creating, we are going to have code that looks like the following :
class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } }
Congrats! You just created your first Web Job. How easy was that! But now how to push things up to Azure. Hmmm.
Kicking Our Job Into Gear
Now let’s say we deploy this as is. Given that .NET Core applications compile down to .dll files, in our publish file we could have multiple .dll files, how will Azure know which one to run? Well, we actually need to give it a nudge in the right direction. The easiest way to do this is to create a “run” file.
The easiest way to explain a run file, is that we take an extension from the following list : .fsx, .cmd, .bat, .exe, .ps1, .sh, .php. .py, .js and we create a file called “run” with that extension. So for example, “run.cmd”. Azure checks our WebJob folder for any run file, and kicks it into gear. Also note that .cmd file extension is obviously used for Windows App Services, and .sh for Linux etc.
In the root of your project, go ahead and create a file called “run.cmd” with the contents :
dotnet WebJobExamples.WebJobExample.dll
Pretty simple. When we upload our WebJob, it’s going to find our run file, and then use the dotnet SDK to kick off our console app. Obviously replace the .dll filename with your projects dll name, and you are good to go.
Very Important!
Do not create this file in Visual Studio. It creates the file as UTF-8 BOM, and will break the WebJob. Even if you don’t create this file in Visual Studio, you should still check that you are not using BOM. The easiest way is to use Notepad++ and check the encoding on this file. Ensure that it’s set to UTF-8 (Without BOM).
And the second thing is to ensure that the file itself is set to copy to your output directory.
If either of these steps aren’t adhered to, your WebJob can either just fail to start, or Azure will fail the deployment (And silently, I might add) which can be a bit frustrating.
Zip Publishing
Later on in this series we are going to cover a much better way of getting Web Jobs into Azure, but for now let’s just just get things up and running. For that we are going to publish a very simple Zip with our webjob inside it, then just upload via the Azure Portal.
To publish our app, we just run the following in our project directory :
dotnet publish -c Release
You then need to zip the following folder : bin\Release\netcoreapp2.1\publish . Note that’s the publish folder not the parent. Once we have all of that zipped up, head over to Azure. If you haven’t already, now would be a great time to spin up a test Azure Web App (Windows) before progressing. I’ll wait…
…
Now with your Azure Web App in the Azure Portal, select Web Jobs from the side menu, then Add.
Don’t worry too much about the various settings, we can dive into these later. But it should look a bit like the following :
Once uploaded, we now need to check the logs of our WebJob and make sure it’s all working as intended. Select our WebJob from the list (You may need to refresh the list a couple of times after the upload), then select Logs. You should be taken to a screen where you can see the output of your WebJob :
[10/08/2018 21:22:41 > d0fd33: INFO] D:\local\Temp\jobs\continuous\TestWebJob\c5pmygp0.mm3\publish>dotnet WebJobExamples.WebJobExample.dll [10/08/2018 21:22:41 > d0fd33: INFO] Hello World!
Woop! So it ran and printed out Hello World! Our first WebJob is all go!
What’s Next?
I know the above might not seem like you’ve achieved a lot. I mean it took us probably a good 30 mins just to print Hello World on a log window in Azure, not that fancy right! But everything we’ve done so far will be built upon and give us better understanding of exactly how WebJobs work under the hood.
In future parts in this series, we will cover using .NET Core’s configuration, dependency injection, scheduled jobs, triggered jobs, uploading as part of a web app, and so much more! Check our Part 2 here!
Awesome piece of work thank you.
I had a real run for my money on this missing run.cmd within my DevOps Release pipeline. The strangest thing is that VS 2019 does include it but dotnet publish does not.
@Alex for me running ´dotnet publish´ without configration parameters makes it include the run.cmd file
Good tutorial, thanks.
One thing has stopped me continuing though. My webjob fails in Azure because of the command in the run.cmd file. What are the details of the name.name.dll parameter? I’m using my project name followed by the project’s dll name, is that correct?
thanks in advance
dotnet name.dll works.
Why can’t I just name my program run.exe?
That’s actually a good question, I guess you can!
Thank so much for the UTF-8 encoding tip on the run.cmd.
My problem was that the generated file was using dotnet *.dll but it defaulted to the 32 bit dotnet on the App Service.
So i set it to run the exe directly instead via including a run.cmd but, it only worked after I opened the file via Kudu and saved it which must have updated the encoding