Deploying Azure Functions in Python with Azure DevOps
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.
Once created, head over to
Repos and select
Files. From here, initialize the repo with a
From here, we can clone the repo locally:
git clone https://email@example.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:
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
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:
Looks good, so let’s commit everything to our
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:
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:
During Repository selection, we can keep the defaults:
And then we select
Start with an 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:
As we will build our Function App under Linux, let’s also switch our Pipeline Agent Pool to
Hosted Ubuntu 1604:
Next, we go back to
Tasks, and add three new Tasks via the
+ button (just search for the
Python Versiontask for deploying Python 3.6 on the Build Agent
Azure CLItask for installing the Function Runtime on the Build Agent
Azure CLItask for deploying our Function App to Azure
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
First, let’s set the Python version to 3.6:
Second, we’ll install the Function Runtime on the Build Host:
You can copy and paste the code from here:
Third, we’ll use the last
Azure CLI task to deploy the 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:
Let’s check if our Function shows up in the Azure 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:
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.
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.