Azure App Services - Deploying a .NET Core 3.1 worker as a schedule triggered WebJob via DevOps
This article will cover some issues I ran into creating a new .NET Core 3.1 worker as a scheduled triggered WebJob on Azure App Services (AAS)
You might need to be a little familiar with: .NET Core, Azure DevOps, AAS, Kudu.
Goals
Lean into conventions as much as possible. Avoid complicated customization to the pipelines YAML, or deployment pipeline or unnecessary extra scripts and files in the repo.
Getting Started
Environment
- Create the AAS as Windows only because WebJobs are not yet available on Linux.
- Enable Always-On (so the scheduler works). You may see Core 3.1 Runtime is not on the Windows AAS according to the Azure Portal GUI but it actually is.
Pipeline
Creating the app from scratch is easy: dotnet new worker --name MyFirst.Worker
Create a very basic .NET Core azure-pipeline.yaml, all it needs to do is run the core publish task and then publish the artifact. The core template for DevOps does this out of the box.
Use this guide: https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/dotnet-core?view=azure-devops
steps:
# ...
- task: DotNetCoreCLI@2
inputs:
command: publish
publishWebProjects: True
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: True
# this code takes all the files in $(Build.ArtifactStagingDirectory) and uploads them as an artifact of your build.
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'MyFirstWorker'
Run the build and verify your worker was published.
First gotcha:
Always generate an Exe. Kudu looks for specific files to run. If you use the DevOps Ubuntu agent, you wont get an Exe. So how can we ALWAYS get one?
You need to edit the publish arguments in your pipeline YAML and add -r win-x86 --self-contained false
. this will force Core to spit out an Exe for windows, even when publishing on Ubuntu. WITHOUT creating a self contained deployment. Although, there is nothing wrong with creating an SCD if you want. might even be a good idea, but for this tutorial I want to go from dotnet new
to a working deployment without lighting up every feature in Core and without adding more scripts and files to my repo.
NOTE: To see how Kudu picks something to run, check the Wiki for WebJobsKudu wiki.
Second gotcha:
The path you deploy to in AAS for web jobs is highly specific. It MUST be in one of the following:
For a triggered (or scheduled) job, the folder is wwwroot\app_data\jobs\triggered\{job name}
, and for a continuous job, it's wwwroot\app_data\jobs\continuous\{job name}
. This is easily achieved by setting the --output
flag on your dotnet publish
to publish into a directory like that.
Mark Heath has a great blog post on a very similar topic:
Why the
app_data
folder? Well that's a special ASP.NET folder that is intended for storing your application data. The web server will not serve up the contents of this folder, so everything in there is safe. It's also considered a special case for deployments - since it might contain application generated data files, its contents won't get deleted or reset when you deploy a new version of your app.
- Via Mark Heath - https://markheath.net/post/managing-webjobs-with-kudu-api
To expand on Marks last line, in Azure DevOps Pipelines -> Deployments -> Deploy AAS Task there is actually a checkbox to blow away the app_data folder if you want. I thought that was interesting.
The Kudu WebJob wiki also says:
As an alternative,
d:\home\site\jobs\...
can be used instead ofd:\home\site\wwwroot\app_data\jobs\...
. This is useful when using Run-From-Zip, which makesd:\home\site\wwwroot
become read-only.
But I haven't tested this.
Third gotcha:
Make sure to copy always the settings.job file. AND it uses https://github.com/atifaziz/NCrontab which requires a seconds field. I first tried 5 0 * * *
and then had to go back and do 0 5 0 * * *
this means run 5 minutes after midnight every day.
This is trivial to verify, go to your AAS in the portal, and click the WebJob blade, you should see your schedule.
Full YAML
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
steps:
- task: DotNetCoreCLI@2
inputs:
command: publish
publishWebProjects: False
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: True
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'myWebsiteName'
Things id like to know more about:
- How to use Run-From-Zip WebJobs
- How to add a WebApp to my repo, and deploy that with my WebJob at the same time.
SEO Tags: Azure,App,Services,Deploying,.NET,Core,3.1,worker,schedule,triggered,WebJob,DevOps
Top comments (3)
Great article!
Great article! Would be helpful if you'd included the finished modified yaml at the very end of the article though :)
There was only a couple missing lines, I added the full build YAML at the end though.