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.
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.
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.
… and the inline script for you to use …
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 😎