Planet PowerShell logo

Contents

Using Azure Functions and Azure Api Management to Host Yout Api Serverless

In this article, I will walk through how you can build and deploy a serverless API in Azure using: Azure Functions and Azure API Management. I will walk through how you can set up an azure function that can be triggered with an HTTP call to either enter data into Azure Tables NoSQL Database or to retrieve data from the DB. Then Once the two API endpoint has been built I will set up Azure API Management to create a single entry point with an API Key to utilize the API.

Creating the Infrastructure

In this tutorial, I will explain how you can use Azure CLI to quickly set up the infrastructure on Azure to host your serverless API. The following resources will be deployed:

  • Storage Account
  • Azure Function App
  • API Management Service

If you don’t have the Azure CLI Tool installed, you can download it Here:

First I will create a new resource group to hold all the resources for the project:

1
2
3
4
5
$resourceGroup = "rg-test-api"

$location = "North Europe"

az group create -n $resourceGroup -l $location

Then I will create the storage account and add a NoSQL table for storing my backend data

1
2
3
4
5
$storageAccountName = "mytestserverlessapi"

az storage account create -n $storageAccountName -g $resourceGroup -l $location --sku Standard_LRS

az storage table create --name Users --account-name $StorageAccountName

Then I will create the Azure Function App

1
az functionapp create --name fa-test-sl-api --storage-account $storageAccountName --consumption-plan-location "northeurope" --resource-group $resourceGroup --os-type Linux --runtime python --runtime-version "3.8" --functions-version "4"

Creating the Azure API Management Services

Now the only thing I need to spin up is the Azure API Management, but unfortunately at the time of writing the article the CLI commands for creating an apim resource are experimental, and I experienced some problems creating the resource through the CLI, therefore I will showcase both methods.

CLI Methods:

1
az apim create --name "scriptingchris-test-api" --resource-group $resourceGroup --publisher-name "ScriptingChris" --publisher-email "[email protected]" --no-wait

Portal Method:

  1. Go to the Azure Portal and Create a new resource.

  2. Search for “API Management” and click on create

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/Create-API-Management.png

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/Create-API-Management2.png

Then for the sake of simplicity, I will just press the button “Review + Create”

Your Azure environment should now be ready for developing API

Creating the API Endpoint using Azure Functions

For the actual endpoint, I will create Azure Functions. For this example, I will use Python for running the functions, but you could use PowerShell if you wanted.

I am using Visual Studio Code for my development, and for creating and editing Azure Functions you can install the vscode extension

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/vscode-Azure-Functions.png

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/vscode-Azure-Functions2.png

Now the first thing I will do is to create a new Azure Function with an HTTP Trigger. The HTTP Trigger means that the function will be activated or started with an HTTP request, just look at an API call.

to create a new function, click on your functions app in vscode and then press on the lightning with a plus over it, above the function. If you get a warning saying: “The selected folder is not a function project. Create new project?” then just select “Yes”.

Then just walk through the Function Initialization Process:

“Select a Language” - Python

“Select a template for your project’s first function” - HTTP trigger

“Provide a function name” - GetDataFromBackend

“Authorization level” - Function

vscode should now provide you with a boiler template for a PowerShell HTTP Triggered function.

Creating the Azure Function endpoints

For this example, I have just created two simple functions. The first function will take a POST request and add a new user to the database. The second function will take a GET request and return a specific user from the database.

Defining the POST Funciton for adding data to the database

I have named the function “AddDataToBackend”. Then inside the file function.json, I have edited the parameter route. This route is used when you are calling the URL for the API. This is used for dividing the different resources or functions of your API into specific URL endpoints.

I have also removed the GET method from the functions since I only want the endpoint to be called with a POST method.

Here is the function.json file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "post"
      ],
      "route": "v1/users"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

Connecting the python code to the Azure Storage Table:

  • Here is the microsoft docs for using key vault

  • Here is the Micorosoft docs for using app settings and environment variables in your code

For connecting the python code to the Azure Storage table you will need to add the python library “azure-data-tables” to your requirements.txt

An important note here is that you should not store your connection string in your code. This should be passed to the function either by Azure Key Vault or as an App Setting in the Function app.

Then inside your function, you will need to import the library with:

1
from azure.data.tables import TableServiceClient

Then for connecting to your storage account you will need the connection string from your Storage Account. To get this you can use the CLI command:

1
az storage account show-connection-string -g $resourceGroup -n $storageAccountName

output:

1
2
3
{
  "connectionString": "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=mytestapistorage;AccountKey=UZIR2NX3X4V34uKmE9VEAD4vAiR8mxP1DTcK0fXBP8zYwhT7j8llVXJmfyCDLhC46JC6QJkwa9D6yIsuDYYxWA=="
}

Function for adding data to the Azure Storage Table

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import logging
import azure.functions as func
from azure.data.tables import TableServiceClient


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    connection_string = "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=mytestapistorage;AccountKey=UZIR2NX3X4V34uKmE9VEAD4vAiR8mxP1DTcK0fXBP8zYwhT7j8llVXJmfyCDLhC46JC6QJkwa9D6yIsuDYYxWA=="
    table_service_client = TableServiceClient.from_connection_string(conn_str=connection_string)
    table_client = table_service_client.get_table_client(table_name="Users")

    req_body = req.get_json()

    my_entity = {
        u'PartitionKey': "User",
        u'RowKey': f"{req_body['country']}",
        u'username': f"{req_body['username']}",
        u'firstName': f"{req_body['firstName']}",
        u'lastName': f"{req_body['lastName']}",
    }

    try:
        entity = table_client.create_entity(entity=my_entity)

        logging.info("Successfully created new user")

        return func.HttpResponse(
            "Object created!",
            status_code=201
        )

    except Exception as e:
        logging.error(e)

        return func.HttpResponse(
            "Bad Request",
            status_code=400
        )

Defining the GET Funciton for retrieving data from the database

just like the first function, I will create a new function and then edit the function.json file.

For the function which retrieves the data from the database, I will use a route parameter to query the user by username. Here I will use a variable in the parameter route to be able to pass a username as a variable to the function. The variable is defined with “{}”.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get"
      ],
      "route": "vi/users/{username}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

Function for retrieving the data from the backend

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import logging
import json
import azure.functions as func
from azure.data.tables import TableServiceClient


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    connection_string = "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=mytestapistorage;AccountKey=UZIR2NX3X4V34uKmE9VEAD4vAiR8mxP1DTcK0fXBP8zYwhT7j8llVXJmfyCDLhC46JC6QJkwa9D6yIsuDYYxWA=="
    table_service_client = TableServiceClient.from_connection_string(conn_str=connection_string)
    table_client = table_service_client.get_table_client(table_name="Users")

    username = None
    username = req.route_params.get('username')

    logging.info(f"Searching for user: {username}")

    my_filter = f"username eq '{username}'"
    entities = table_client.query_entities(my_filter)

    try:
        for entity in entities:
            user_object = {}
            for key in entity.keys():
                user_object[key] = entity[key]

            json_object = json.dumps(user_object)

            logging.info(f"Found object: {json_object}")
    except Exception as e:
        logging.error(e)
        return func.HttpResponse(
            "Failed querying the Azure Storage Table",
            status_code=400
        )

    if json_object:
        return func.HttpResponse(
                json_object,
                status_code=200
        )
    else:
        return func.HttpResponse(
                "Resource not found",
                status_code=404
        )

Deploying the functions to Azure.

To deploy the functions, is as simple as under the Azure Functions extension in vscode, find your Functions App, right-click and press “Deploy to functions app” and then click on “Deploy” for the pop-up.

A small toast message will appear in vscode once the functions have been deployed.

Connecting the Azure Functions with Azure API Management

All there is left to do now is to import the Azure Functions into Azure API Management, create a “subscription”(API key) for accessing the API, and then test it.

I will use the Azure portal for importing the Functions. Open the Azure Portal and go to API Management, open your apim instance.

In the left menu click on “API’s”, Then select “Function App”

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/api-import-function.png

Choose your function app, and be sure that all of your functions are selected.

Then give your API a Displayname, Name, and API URL suffix.

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/api-create-from-functionapp.png

once the API has been created you should be able to see the two functions you created as endpoints.

You can test your functions by clicking on one, and then clicking on the “test” pane. Here I am testing my GET endpoint for returning a user from the database. I can enter a username into the “Template parameters” and click on “Send”.

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/api-testing-serverless-api.png

In the image above you can see that I am searching for the user “ScriptingChris” in the Azure Storage Table Database. The API Endpoint successfully return status code 200, and the data for the user I searched for.

In this test section, you can also see the Request URL. This is the URL you should use for connecting to your API. In my case it is:

https://scriptingchris-test-api.azure-api.net/api/v1/users

Creating an Azure API Management Subscription (API Key)

To create an API key I will click on “Subscriptions” in the left menu pane.

Then click on “+ Add subscription”.

Give the subscription a Name, and Displayname set the scope to the newly created API.

Then click on “Create”.

Once the API key has been created you can click on the three dots on the right side of the screen. Here you can select “Show/hide keys”, this will display the API key for you.

Testing the Azure Serverless Function API

To test the API I am using a vscode extension called Thunder Client. This extension can be used for testing HTTP calls. You can read more about the extension here.

Now for calling the API I will need to set the API key in the header of my request. By default, the name of the header, for the API key is: “Ocp-Apim-Subscription-Key”.

/images/using-azure-functions-and-azure-api-management-to-host-yout-api-serverless/api-test-with-tunder-client.png

As the response shows, the API works exactly as expected.

Conclusion

Azure Functions is an extremely easy way to build an entire API and host it serverless in Azure. You can create the entire environment in vscode using the Azure Functions extension and the Azure CLI for creating the resources.

Using Azure API Management makes it extremely easy to make a single way of connecting to your serverless API. No matter if the API is writing with PowerShell, C#, or even Logic Apps, just to name a few.

The API Management Services also makes it easy for managing your API, keep your documentation up to date, and manage access to your endpoints.