Azure Functions and Serverless - configuration and bindings

Azure Functions support in Serverless is still a fresh thing, yet it gives us plenty of different configuration options and bindings with many Azure components. In this post we'll quickly go through configuration file and compare Serverless implementation of functions with an original solution using Azure Portal or Visual Studio.

Configuration

In Serverless the file responsible for configuring a function app is named serverless.yml. If you go to the one attached to the boilerplate code, which you can using following command:

/
serverless install --url https://github.com/azure/boilerplate-azurefunctions

You'll see following structure:

/
service: azure-functions-app1234

provider:
  name: azure
  location: West US
  #armTemplate:
     #file: YourARMTemplate.json
     #parameters:
      #VariableNameToUserInArmTemplate: Value

plugins:
  - serverless-azure-functions

package:
  exclude:
    - node_modules/**
    - .gitignore
    - package.json
    - .git/**

functions:
  httpjs: 
     handler: handler.hello
     events: 
       - http: true
         x-azure-settings:
           authLevel : anonymous
           
  queuejs: 
     handler: handler.helloQueue
     events: 
       - queue: SampleQueue
         x-azure-settings:
           name: queueItem
           connection : AzureWebJobsStorage
       - blob:
         x-azure-settings:
           name: blobOut
           direction: out
           connection : AzureWebJobsStorage

As you can still you can store whole configuration and functions references in the only one file. This greatly improves manageability and helps in keeping a solution clean. Let's dive a bit deeper into it.

Bindings

There're planty of different bindings to choose from when working both with Azure Functions and Serverless. Currently you can use HTTP, Timer, Queue, Service Bus, Event Hub or Blob Storage. What is more, Serverless allows to use additional bindings like DocumentDB, Notification Hubs or Mobile Apps. In fact, because the implementation of bindings is something that you don't have to think about, whole solution is pretty flexible and straightforward. Let's compare serverless.yml with a basic C# example:

/
functions:
  example:
    handler: handler.hello
    events:
      - http: true
        x-azure-settings:
            name: req #<string>, default - "req", specifies which name it's available on `context.bindings` 
            methods: #<array> [GET, POST, PUT, DELETE], default - all
                - GET
            route: example/hello #<string>, default - <function name>
            authLevel: anonymous #<enum - anonymous|function (default)|admin>
/
{
    "disabled": false,
    "bindings": [
        {
            "name": "req",
            "type": "httpTrigger",
            "direction": "in",
            "authLevel": "function",
            "methods": [
                "post"
            ],
            "route": "example/hello"
        },
        {
            "name": "res",
            "type": "http",
            "direction": "out"
        }
    ]
}

As you can see, both configurations are quite similar. The main difference is the whole structure of a solution - when it comes to working with Serverless, you have a single file where it stores all the data.

Remember that if you'd like to use multiple bindings, each one will be available from the context.bindings array with a corresponding name. Consider this example:

/
/
functions:
  example:
    handler: handler.hello
    events:
      - http: true
        x-azure-settings:
            name: req #<string>, default - "req", specifies which name it's available on `context.bindings` 
            methods: #<array> [GET, POST, PUT, DELETE], default - all
                - GET
            route: example/hello #<string>, default - <function name>
            authLevel: anonymous #<enum - anonymous|function (default)|admin>
      - eventHub:
        x-azure-settings:
            name: item #<string>, default - "myEventHubMessage", specifies which name it's available on `context.bindings` 
            path: hello #<string>, specifies the Name of the Event Hub
            consumerGroup: $Default #<string>, default - "$Default", specifies the consumerGroup to listen with
            connection: EventHubsConnection #<string>, App Setting/environment variable which contains Event Hubs Namespace Connection String

In such case you'll have both context.bindings.req and context.bindings.item properties available and usable.

Examples for Serverless were taken from the official documentation.

Serverless framework with Azure Functions

Recently an update to Azure Functions has been published containing proxies and integration with Serverless framework. In a nutshell it's a tool for building serverless architectures. Don't get the name fool you - in fact there's a server behind the whole functionality and still all principals regarding web development are true. What in reality it gives you is focusing only on the very functionality of your application. There're no pipelines, layers or areas - you're going to ensure that each component follows the single responsibility pattern and can be deployed separately. 

We're going to build a very simple application using Azure Functions and Serverless to present the current capabilities of the integration.

Provisioning you environment

I decided, that the easiest solution here is to obtain a pre-configured Ubuntu VM with installed NodeJS - you can easiliy get it from the marketplace in the portal. Once you have it, connect to it(as usual in my case, from Windows PuTTY seems like the best idea) and we can start. Type in following commands:

sudo npm install -g serverless
serverless install --url https://github.com/azure/boilerplate-azurefunctions --name ServerlessExample

Those two will:

  • install Serverless globally
  • install a new service locally using name ServerlessExample

Once it's finished, you can go to the ServerlessExample directory - you'll see some files which are the core of our application. In fact, this boilerplate gives us all we need to get started as the example is fully functional.

Deploying a function

Before you're able to deploy your function to Azure you have to provide a credentials for deployment. The whole process is pretty well described here, however I attaching it here with my comments because in my case it didn't go as flawlessly as I thought:

\
bitnami@nodejsblog:~$ sudo npm i -g azure-cli
bitnami@nodejsblog:~$ azure login
bitnami@nodejsblog:~$ azure account show
bitnami@nodejsblog:~$ azure login
bitnami@nodejsblog:~$ azure ad app create -n service_name --home-page http://www.my-app.org --identifier-uris https://www.my-app.org/example -p pass
bitnami@nodejsblog:~$ azure ad sp create -a application_id
bitnami@nodejsblog:~$ azure role assignment create --objectId object_id  -o Contributor
bitnami@nodejsblog:~$ export azureSubId='<subscriptionId>'
bitnami@nodejsblog:~$ export azureServicePrincipalTenantId='<tenantId>'
bitnami@nodejsblog:~$ export azureServicePrincipalClientId='<servicePrincipalName>'
bitnami@nodejsblog:~$ export azureServicePrincipalPassword='<password>'

Note that I used azure ad app create command additionally - in my case it was not possible to simply create a service principal using only a name and a password.

You can ensure that you've added environment variables using printenv command. Once you configured your credentials, go to the directory of your service and just run:

/
serverless deploy

You should see following output:

/
Serverless: Packaging service...
Serverless: Logging in to Azure
Serverless: Creating resource group: ServerlessExampleKamz-rg
Serverless: Creating function app: ServerlessExampleKamz
Serverless: Waiting for Kudu endpoint...
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Building binding for function: httpjs event: httpTrigger
Serverless: Packaging function: httpjs
Serverless: Building binding for function: queuejs event: queueTrigger
Serverless: Building binding for function: queuejs event: blob
Serverless: Packaging function: queuejs
Serverless: Syncing Triggers....Response statuscode: 200
Serverless: Running Kudu command del package.json...
Serverless: Running Kudu command npm install --production...
Serverless: Successfully created Function App

Now when you go to the portal, you'll see a new resource group created and function deployed.

In the next post we'll try to do more advanced configuration and consider involving VSTS and incorporating the framework into our build/release pipeline.