Deploy Python-based Azure Functions using Azure DevOps

Published on
Reading time
Authors

Updated for General Availability (August 2019)

As promised I've updated this post to show you how to achieve builds and releases now Azure Functions Python support is Generally Available (GA).

Build and package

You now have the choice to package Python Azure Functions using a pre-built template in Azure DevOps.

Python Functions Build Template

Once you select this you will have all you need to restore any Python modules you have in your requirements.txt file and ensure any Azure Function Extensions are also installed. The resulting footprint is then zipped for use in a release.

If, like me, you put multiple independent Azure Functions into a single Azure Git Repo then you will need to modify three of the build Tasks to ensure you have the sub-folder selected when running commands.

First we need to ensure we restore the Azure Functions Extensions so we'll add a 'cd' command in and switch into the right folder.

Extensions subfolder

Then we have make sure we restore Python modules based on the requirements.txt in the subfolder.

Subfolder for requirements.txt

Finally we need to edit the Archive Files Task ensure you select the same sub-folder as the previous step.

Set Subfolder in Archive Files Task

Right, if you run this build you'll end up with a zip file ready to deploy.

Releasing into Azure

We can now create a Release that uses the standard Azure Function template (shown below). There are no special steps to use as you are deploying into a host environment already setup for Python Functions.

Release Template

Once you run the Release your Function is deployed! If you need to deploy App Settings at the same time you can use the 'Application and Configuration Settings' section of the 'Deploy Azure Function App' Task to specify the settings to deploy.

That's it - so much easier than when this capability was all in preview!

I'm leaving the content below if you're interested to see how this was done previously, but those steps are no longer required.


💣 BELOW HERE IS OLD CONTENT AND SHOULD NOT BE USED! 💣

Important note: this blog post covers in-preview services that are subject to change before launch. As such I'll update this post as / when required to reflect official guidance from the Functions team on the best approach to deployments. Additionally, for the purpose of this post I am not deploying App Settings as there are existing methods that continue to work.

Know your versions

As this is under-development software it is important to provide context on versions that were used to produce this blog post. The key version being that of the Azure Functions Core Tools. The version installed and working here is 2.3.199-1. Python support currently sits at Python 3.6 with the underlying Functions tools needing .Net Core 2.1.502.

Foundation

Before working through this post it's important that you are familiar with the Azure Functions (version 2) Python support. At the time of writing the best place to go for this is the Microsoft Docs site. Once you undestand the development and lifecycle of them then you're ready for the rest of this post.

You will need to make sure you have a target Linux Function App in Azure ready for deployment as per the "Create Linux function app in Azure" section of the documentation.

When you have a working Python Function check it into an Azure Repos Git repository.

Define the Azure Pipelines Build

As a starting point ensure you have a Service Principal-based connection into the Azure Subscription you are going to deploy your Python Function to. This avoids anyone having to know usernames and passwords which is 👍.

The easiest way to setup your Build is to import the following YAML build definition and make the changes to the three locations (**yourfoldername**, **yourfunctionname**, **your azure subscription connection**). If you can't see the YAML build definition embedded below then you can view it on GitHub.

If you are setting up the build manually, make sure to select a Linux-based build agent otherwise the build will fail.

My approach assumes that your Python Functions are stored as sub-folders inside Git. This allows you to store multiple unrelated Functions in a single repository which works in my scenerio as I am manually triggering builds because I don't need to deploy all Functions at once.

https://gist.github.com/sjwaight/b60047b27e6c93bc285f0b5166850229

You might be wondering why all this inline code is inside an Azure CLI task - the answer is that the actual deployment to Azure requires an active Azure CLI session.

I could probably break some of the earlier steps out into one or more Bash script task steps if I wanted, but for ease of maintenance I just made them a single script. I could also make the inline script an artefact in the repository and maintain it that way.

A lot of the work involved in the inline script (a copy of which is at the bottom of this post) is the result of a few items:

  • The requirement for the Azure Functions Core Tools to be installed
  • .Net Core is required by the Azure Functions Core Tools
  • Python 3.6 is required for Azure Functions Python support. The age of the Ubuntu distribution running the Azure DevOps hosted build agent means it doesn't have Python 3.6 installed and Ubuntu don't provide it (only Python 3.5). When these hosts are updated by Microsoft to a more modern Ubuntu release then this script will become much simpler
  • The need to re-initialise the Functions environment after check-out from Git with 'init' which overwrites our existing requirements.txt file
  • The need to manually restore any Extensions used in the Function.

My approach is functional, but there are clearly some sharp edges to it. 🙂

There is also an implicit requirement (no pun intended!) on developers to maintain an up-to-date requirements.txt file, particularly as the Functions core Python and Functions Python runtime are both listed.

If you can't (or don't want to) use the XAML build definition above the visual definition setup is very simple - as shown below.

Build Definition

... and the inline script for you to use ...

https://gist.github.com/sjwaight/e0047a235790b2d7489428ea5c750eb7

Depending on the complexity of your Python Functions and the location of your build Agent versus your Function App you should see a deployment complete within a few minutes (I've averaged around 7 minutes for a Python Function App with three Functions and a deployment package of 48MB).

Happy Days 😎