Deploying Azure Functions in Python with Azure DevOps

Overview

Updated – August 2019: Deployment will now work properly even when including additional pip packages in the Function App! 

In this post, we’re looking at deploying Azure Functions in Python by using Azure DevOps for Continuous Integration or Continuous Deployment.

We’ve already covered how to write Azure Functions in Python in this last post, so this post is primarily focusing on automated deployment.

Deploying Python Functions with Azure DevOps

Setting up a new Azure DevOps Project

Before we start, we want to head over to Azure DevOps, sign in and create a new project.

Create a new Azure DevOps project

Once created, head over to Repos and select Files. From here, initialize the repo with a README:

Init the Git Repo

 

From here, we can clone the repo locally:

git clone https://xxx@dev.azure.com/xxx/python-functions-devops/_git/python-functions-devops
cd python-functions-devops/

Let’s follow through the tutorial from this former post to create a new Function App:

python3.6 -m venv .env
source .env/bin/activate
func init python-functions --worker-runtime python
cd python-functions/

Finally, we can create a new HTTP trigger-based function:

func new
Select a template:
1. Azure Blob Storage trigger
2. Azure Cosmos DB trigger
3. Azure Event Grid trigger
4. Azure Event Hub trigger
5. HTTP trigger
6. Azure Queue Storage trigger
7. Azure Service Bus Queue trigger
8. Azure Service Bus Topic trigger
9. Timer trigger
Choose option: 5
HTTP trigger
Function name: [HttpTrigger] GetProducts
Writing /Users/csiebler/Microsoft/learning/python-functions-devops/python-functions/GetProducts/sample.dat
Writing /Users/csiebler/Microsoft/learning/python-functions-devops/python-functions/GetProducts/__init__.py
Writing /Users/csiebler/Microsoft/learning/python-functions-devops/python-functions/GetProducts/function.json
The function "GetProducts" was created successfully from the "HTTP trigger" template.

As always, we’ll load it up in VSCode:

code .

Now we are ready to update our template code briefly and then deploy it to Azure.

Writing a dummy function

First, we’ll update our function with some dummy code in GetProducts/__init__.py:

Writing our Azure Python Function

Here’s the code for copy/pasting:

We’ll keep it simple here, so let’s start our function and give it a short dry-run:

pip install -r requirements.txt # required if we added more pip packages
func host start

Let’s head to http://localhost:7071/api/GetProducts and check if it is working:

Function is running

Looks good, so let’s commit everything to our git repo:

cd ..
git add python-functions/
git commit -m "Initial commit"
git push

Setting up our Azure Pipeline

Now that we have our basic Python Functions created from a coding point of view, we can now provision the backend Azure services where we’ll deploy to. For sake of time, we won’t focus on rolling out this infrastructure automatically in our CI/CD pipeline, but tools like ARM templates or az command line commands will easily get this done.

Hence, we’ll manually create a resource group, storage account and Python-based Function App:

az group create --name functions-python-test --location westeurope
az storage account create --name functionspythontest275 --location westeurope --resource-group functions-python-test --sku Standard_LRS
az functionapp create --resource-group functions-python-test --consumption-plan-location westeurope --os-type Linux --name functions-python-test --storage-account functionspythontest275 --runtime python

Let’s check if everything is there:

Azure Functions App in Python is there

Looks good, now we are ready to set up our Azure DevOps Pipeline!

Setup our Azure Pipeline

In Azure DevOps, navigate to Pipelines, then select Builds and click New pipeline. I prefer to use the classical editor as it is a lot easier to configure, if you haven’t used Azure DevOps a lot:

Use Classical Editor in Azure DevOps

During Repository selection, we can keep the defaults:

Select Repository

And then we select Start with an Empty job:

Start with empty job

Next, we will directly turn on Continuous Integration under the Triggers tab by selecting Enable continuous integration. We can keep the default settings for the sake of this tutorial:

Enable Continuous Integration

As we will build our Function App under Linux, let’s also switch our Pipeline Agent Pool to Hosted Ubuntu 1604:

Ubuntu Build Agent

Next, we go back to Tasks, and add three new Tasks via the + button (just search for the names):

  1. A Python Version task for deploying Python 3.6 on the Build Agent
  2. A Azure CLI task for installing the Function Runtime on the Build Agent
  3. Another Azure CLI task for deploying our Function App to Azure

Add Build Tasks

Note: Using the Deploy Azure Function task won’t properly work with Python Functions, as it won’t deploy the runtime extension and additional pip packages from requirements.txt.

First, let’s set the Python version to 3.6:

Python 3.6

Second, we’ll install the Function Runtime on the Build Host:

Install Function Runtime

You can copy and paste the code from here:

Third, we’ll use the last Azure CLI task to deploy the Function App:

Deploy Function App

Again, the snippet for copy and paste:

Lastly, we can hit Save & queue and give our Pipeline a test run.

If we go to Pipelines, then click Builds and select the current run, we can see what our pipeline is doing. After 2-3 minutes, the pipeline should be done:

Build Success

Let’s check if our Function shows up in the Azure Portal:

Function in Portal

Looks good, our GetProducts Function shows up in the portal. If we select it, we can retrieve the function’s URL through Get function URL:

Get Function URL

Let’s check if it is working:Function running in Azure

Looks good!

From here, we can add another function using func new on the command line, commit it, and it should be automatically get deployed to Azure.

If you would like to have a quicker deployment option: one simple solution could be to just deploy your own build agent with the Function Runtime pre-installed. A cheap B-Series Azure VM instance would probably sufficient for that task and cost less than $10-20/month. This should bring the build and deployment time to under a minute.

Summary

Automatically deploying Azure Function in Python to Azure by using Azure DevOps is an easy way to enable Continuous Integration or Continuous Deployment for function-based projects.

In a real-world setup, the rollout of the underlying Azure Function App should be performed programmatically, e.g., via ARM templates or az command line commands. This will ensure that the underlying infrastructure is always set up correctly and versioned in source control.

If you have any questions or want to give feedback, feel free to reach out via Twitter or just comment below.

One thought on “Deploying Azure Functions in Python with Azure DevOps

Leave a Reply

Your email address will not be published. Required fields are marked *