Share via


Visual Studio Azure Function Template (preview)

Overview

This wiki post uses the simple scenario of using an Azure Function to post messages to Azure Table Storage to illustrate the Visual Studio Azure Function template. The Azure Table template is available for download here and requires Azure 2.9.6 .NET SDK to be installed.
 
Note: The template is in preview and does lack some features. Please see the announcement for more details.

Scenario

The scenario does have a couple of interesting points.  First, it uses message definitions from the IndieGamesLab Common NuGet package which illustrates how a NuGet package can be added to a function. Secondly, the Table Storage and Service Bus require configuration to be set and deployed with the project.  Also, the ability to include additional files during deployment is explored.

Adding an Azure Function project to a Solution using the template

After installing the template, the Azure Function (Preview) is available as a new project type in Visual Studio.

The template generates the project with some default files mirroring what will be deployed in Azure:

The Project readme provides some insight as to what to do next:

Adding a New Azure Function to the project

The option New Azure Function... is now available in the Add menu.  

Nice! In Visual Studio now there is a form for creating the Trigger that will start the function.  For this scenario, a new Azure Service Bus trigger will be used. This will start our function when a new queue item is added to the specified queue.

The run.csx is generated as well the function.json is updated to reflect the new trigger.

01.{
02.  "disabled": false,
03.  "bindings": [
04.    {
05.      "name": "message",
06.      "type": "serviceBusTrigger",
07.      "direction": "in",
08.      "queueName": "arxevent",
09.      "connection": "indiegameslabbackbone_SB",
10.      "accessRights": "Manage"
11.    }
12.  ]
13.}

Application Settings

The appsettings.json file is updated also to include the new settings.  

01.{
02.  "IsEncrypted": false,
03.  "Values": {
04.    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=...",
05.    "AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=...",
06.    "indiegameslabbackbone_SB": "Endpoint=sb://indiegameslab.servicebus.windows.net...",
07.    "indiegameslabbackbone_STORAGE": "DefaultEndpointsProtocol=https;AccountName=..."
08.  }
09.}

Referencing Nuget Packages

One great feature of Azure Functions is the simplicity of referencing NuGet packages. This is done in the project.json file. Below shows the references to the required packages:

01.{
02.  "frameworks": {
03.    "net46":{
04.      "dependencies": {
05.        "WindowsAzure.Storage": "8.0.1",
06.        "WindowsAzure.ServiceBus": "3.4.0",
07.        "Newtonsoft.Json": "4.0.1",
08.        "IGL.Common": "1.0.5"
09.      }
10.    }
11.  }
12.}

Implementing the Scenario

In the preview template, intellisense only works with a limited number of libraries (re. System) and not with referenced libraries.  For this scenario, the service bus message is read and parsed as a IGL.GamePacket.  Using the information in the GamePacket, it is written to table storage.  

Note: the assumption here is a message will not come in more frequently than every couple of seconds otherwise there will be a collision on the rowkey.

01.using IGL;
02. 
03.using Microsoft.WindowsAzure.Storage.Table;
04.using Microsoft.ServiceBus.Messaging;
05.using System.Diagnostics;
06.using System.Runtime.Serialization;
07. 
08.using System;
09.using System.Threading.Tasks;
10. 
11.public static  void Run(BrokeredMessage message, ICollector<Arx> tableBinding, TraceWriter log)
12.{
13.    GamePacket packet = null;
14. 
15.    try
16.    {
17.        log.Info(string.Format("Processing message {0}", message.SequenceNumber));
18. 
19.        if (!message.Properties.ContainsKey(GamePacket.VERSION))
20.            throw new  ApplicationException(string.Format("Message {0} does not have a valid {1} property.", message.SequenceNumber, GamePacket.VERSION));
21. 
22.        packet = message.GetBody<GamePacket>(new DataContractSerializer(typeof(GamePacket)));
23. 
24.        tableBinding.Add(new Arx()
25.        {
26.            PartitionKey = packet.PlayerId,
27.            RowKey = packet.PacketCreatedUTCDate.ToString("yyyyMMdd hh:mm:ss"),
28.            Content = packet.Content
29.        });
30. 
31.        message.Complete();
32. 
33.        log.Info(string.Format("Processed message {0} {1} {2}", message.SequenceNumber, packet.PlayerId, packet.PacketCreatedUTCDate.ToString("yyyyMMdd hh:mm:ss")));
34.    }
35.    catch (Exception ex)
36.    {
37.        message.DeadLetter(ex.Message, ex.GetFullMessage());
38. 
39.        log.Error(string.Format("Failed to process {1} with {0}", ex.GetFullMessage(), message.SequenceNumber));
40.    }
41.}
42. 
43.public class  Arx : TableEntity
44.{
45.    public string  Content { get; set; }    
46.}

Debugging

Debugging of Azure Functions is supported in Visual Studio as well as attaching to a deployed instance.

Note: the Azure Functions CLI tools will be downloaded when run for the first time.

Additional Files

One cumbersome task of working with Azure Functions is deploying additional files to Azure that are referenced by the project. This is now relatively trivial. As an example, the following steps were performed to include an XML file to the Azure Function deployment.

  1. A new folder was created in the function folder as shown below:
  2. Though there currently is not any support for different file types, an empty file can be added to the folder and renamed as an XML file (e.g., TempInfo.xml). 
  3.    Simple as that. After a deploy, the file is shown.

Summary

As a preview, the Visual Studio Tools for Azure Functions template provides a great step towards creating and managing Azure Functions in Visual Studio.

Issues experienced while creating the wiki

Most of the functionality explored above worked as the announcement stated except for the following:

  • After the deployment to Azure, the NuGet packages were not automatically downloaded. In order to trigger the download, the project.json had to be remotely *touched *to download and re-compile the Function.
  • After the deployment, the Application Settings were not correctly updated and required to be set using the portal.