Simple Azure Function App in Python

In the first post on this topic, Learning Azure (Python) Functions, I provided some introductory reading on Azure Functions to give a background on what they can do and how your Python code interacts with Azure services. Now we’re going to use that knowledge to implement a simple Function App. Later we may build on this simple app to learn about the other triggers and bindings available with Functions.

I should note this material is not new nor is it fresh. You can achieve basically the same by following the Microsoft Docs for Create Your First Function in the Azure Portal. I’m writing this as I learn more about Azure Functions and hope that my post provides some additional context useful to others also building their first Function Apps.

Prerequisites

I recommend having the following knowledge to get the most out of this exercise.

Scope

This post covers a simple Function App titled ‘unrot’. The app accepts ROT13 ciphertext using an HTTP trigger and returns deciphered plaintext to the user via an HTTP response. Our purpose is to build upon basic Function App knowledge by creating a real function to demonstrate some of the basic files and creation. The Python code will be simple and we’ll create more complicated Function Apps in future posts.

I use a Linux environment for my examples, but Microsoft provides tools for all major environments. My IDE of choice at home is Sublime Text. VS Code provides a great experience also, and can do much of the heavy lifting for less prep work and more coding. I encourage you to use the environment that suits you best, but perform a few manual builds to understand what is going on in the background.

Building Your App Environment Locally

We start by developing our code. In my Linux environment I have a ‘source’ directory in my home path where I hold my source code. From there I use the Azure Functions Core Tools to initialize a Function App environment.

ed@block:~/source$ func init httptest
Select a worker runtime: 
1. dotnet
2. node
3. python
4. powershell (preview)
Choose option: 3
python
Writing .gitignore
Writing host.json
Writing local.settings.json
Writing /home/ed/source/httptest/.vscode/extensions.json
ed@block:~/source$

This just sets up a Function App environment, not a function! Think of a Function App as your application. It can consist of multiple Python functions all performing tasks that make up your serverless application. The nomenclature can be a bit tricky until you get used to it. You will develop and locally test your function app in this environment. Notice it is initialized as a git repo and contains a few files:

  • host.json – A global settings file for all functions in the Function App. This file will be pushed to Azure when you deploy your function. We won’t cover it in this post.
  • local.settings.json – [secret] This file may contain secrets and should be protected. It contains settings used when running the function in the local environment. In Azure, these settings will be configured in the Application Settings.
  • requirements.txt – A Python file used to specify packages that must be installed to run the app. This will be pushed to Azure when you deploy so the Azure Function App environment can load the required packages. This file can be generated by the pip freeze command.
  • .gitignore – A standard .gitignore file automatically generated to prevent pushing some unnecessary or potentially sensitive files to your source repository.
  • We won’t cover the .git and .vscode directory here

Now we’ll create the first function. Let’s call it ‘unrot’. Run this from the directory created by the last command.

ed@block ~/source/httptest $ 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] unrot
Writing /home/ed/source/httptest/unrot/__init__.py
Writing /home/ed/source/httptest/unrot/function.json
The function "unrot" was created successfully from the "HTTP trigger" template.
ed@block ~/source/httptest $ ls unrot
function.json __init__.py

A directory containing two files is created for the function. The files are described below:

  • function.json – Bindings, entry point, and other settings for this single function.
  • __init__.py – Put your function code here. You can get more complex with this – maybe we’ll explore that later.

Take a look at your function.json file. My example uses the default file which is displayed below when choosing Python and HTTP trigger.

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

The documents in my first post covered this generally, and you can find more detail in Microsoft Docs Azure Functions HTTP Triggers and Bindings. For this example, understand the two entries in the bindings section:

  • The first binding specifies the input (direction=in) and notes that your Python function will receive a parameter named “req” (for “request”). You can change the name here to anything you want as long as your Python code uses the name you choose as it’s parameter. Also notice we’re accepting HTTP GET and POST methods.
  • The second binding denotes that the output (direction=out) will be provided by a return value, specified by the special value $return.

Add Your Python Code

For this example put your Python code in the init.py file. It can be almost anything as long as it accepts a string into the parameter req and outputs via return value. You can use the code in my Github repository as an example.

Important: Note that the return in the code is of the form return func.HttpResponse(f"Translation: {translated}"). The output object is from the azure.functions package, and formats the data into a proper HTTP response. If you’re new to Python and a little confused, just leave this alone other than making sure the variable matches the one you’ve used in your code.

The code in my Github repository does the following:

  • Accepts ciphertext either from the query string of the GET request, or the body of the POST request
  • Uses str.maketrans() and str.translate() to translate the ROT13 ciphertext into plaintext
  • Returns the plaintext to the caller, or help text if no ciphertext was found

Test Your Code

I love this part – you can easily test your function locally before deploying to Azure. Make sure you are in the root folder of your Function App environment – the folder created when you ran the func init httptest command. In my example the root folder is ~/source/httptest. Run the command func host start –python per below. You’ll get some spiff ASCII text and finally some output showing how to access your function for testing.

ed@block:~/source/azure_func_httptest$ func host start --python

                  %%%%%%
                 %%%%%%
            @   %%%%%%    @
          @@   %%%%%%      @@
       @@@    %%%%%%%%%%%    @@@
     @@      %%%%%%%%%%        @@
       @@         %%%%       @@
         @@      %%%       @@
           @@    %%      @@
                %%
                %

Azure Functions Core Tools (2.7.1704 Commit hash: fbab3b9c1de5ab95e3b4b6a471ead62c4f37e89c)
Function Runtime Version: 2.0.12742.0
[----OUTPUT TRUNCATED FOR BREVITY----]
Hosting environment: Production
Content root path: /home/ed/source/azure_func_httptest
Now listening on: http://0.0.0.0:7071
Application started. Press Ctrl+C to shut down.

Http Functions:

    unrot: [GET,POST] http://localhost:7071/api/unrot

[10/12/19 7:50:15 PM]  INFO: Starting Azure Functions Python Worker.
[----OUTPUT TRUNCATED FOR BREVITY----]

Test your function by browsing to the URL specified in the logging. Don’t forget to include your input data!

Testing Function App locally in browser using query parameter

Example using cURL and a POST request:

ed@block:~$ curl --data '{"ciphertext": "Or fher gb qevax lbhe Binygvar!"}' -H "Content-Type: application/json" http://localhost:7071/api/unrot
Translation: Be sure to drink your Ovaltine!ed@block:~$

Deploy Your Code

I’m going to skim through parts of this, such as setting up the Function App in Azure and other exercises that are thoroughly covered in docs. However I will provide specific settings where needed.

Create a new Function App. Ensure you set it up for a Python runtime environment using the settings below.

  • OS: Linux (needed for the Python runtime environment)
  • Publish: code
  • Hosting Plan: consumption
  • Runtime Stack: Python
  • Application Insights: disabled (just for simplicity)

I created a Function App appropriately titled badcrypto. You can now deploy your function using the Core Tools command below. Run this command from the root directory of your Function App.

Before deploying your Function App, ensure you are logged into the correct account in Azure CLI.

func azure functionapp publish <function app name>
Example:

func azure functionapp publish badcrypto

Azure pushes your code to the Function App, downloads and builds dependencies, and will output the invocation URL for your shiny new function.

(venv) ed@block:~/source/httptest$ func azure functionapp publish badcrypto
Getting site publishing info...
[----OUTPUT TRUNCATED FOR BREVITY----]

Uploading package...
Uploading 13.17 MB [##############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in badcrypto:
unrot - [httpTrigger]
Invoke url: https://badcrypto.azurewebsites.net/api/unrot?code=[REDACTED]==

(venv) ed@block:~/source/httptest$

Your Function App is now ready for business!

Production Data

Browser example of production Function App
ed@block:~/source/httptest$ curl --data '{"ciphertext": "Purpx bhg guvf zvyvgnel-tenqr rapelcgvba."}' http://badcrypto.azurewebsites.net/api/unrot?code=$SIGNATURE
Translation: Check out this military-grade encryption.(venv) ed@block:~/source/httptest$

(Note in the cURL example above I obscure the token using a bash variable, $SIGNATURE.)

Summary

Certainly nothing groundbreaking here but I hope it provides some context or help for those beginning with Azure Functions and Python. You can do so much more and the supported bindings allow you to interact with many other Azure services. Perhaps in a future post we will explore more of those bindings and new triggers to develop a useful serverless application.

Lessons

  • I recommend using a Python virtual environment for your Function App environment. The .gitignore file generated by the Function App initializer includes common virtual environment directories to prevent replication to your git repo.
  • Ensure your version of pip is up to date

Comments are closed.

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: