Dennis Tretyakov

Walk-through creating first azure function with dotnet

Heard a lot, but haven’t used before. First impression - quite handy. In-fact I know some large fintek’s that are using serverless azure functions fore core functionality of a platform. Starting .NET 6 the cold start is close to python (eg. minimal) and scalability is not a question at all.

I gave it a try, and writing down the things I had to google. Will be glad if it will help to save your time.

In case of any issues refer to git repository of this example.

Things you’ll need #

I’d assume .NET SKD and ASPNET runtime are installed.

For Arch uses things are simple yay azure-cli and yay azure-functions-core-tools-bin. For other distros, Windows and macOS click on a links above for detailed instructions.

Creating Function Project #

After things were installed, there should be a func command available in terminal (at this moment I have version 4.0.4736 installed).

$ func --version
4.0.4736

Create project directory (in my case AzureHelloFn), navigate to it and run func init. It will ask you about runtime and language, like on the example below. For this example I’m choosing dotnet and C#

$ mkdir AzureHelloFn
$ cd AzureHelloFn
$ func initSelect a number for worker runtime:
1. dotnet2. dotnet (isolated process)
3. node
4. python
5. powershell
6. custom
Choose option: 1
dotnet
Select a number for language:
1. c#2. f#
Choose option: 1
c#

Writing /home/den/Projects/samples/AzureHelloFn/.vscode/extensions.json

To avoid questions you can use --dotnet option. Eg. func init --dotnet.

The project is created. You can open it with editor of your choice. However so far it’s blank. It has only csproj and some metadata files.

To add a function run func new (obviously in a project directory)

$ func newSelect a number for template:
1. QueueTrigger
2. HttpTrigger3. BlobTrigger
4. TimerTrigger
5. KafkaTrigger
6. KafkaOutput
7. DurableFunctionsOrchestration
8. SendGrid
9. EventHubTrigger
10. ServiceBusQueueTrigger
11. ServiceBusTopicTrigger
12. EventGridTrigger
13. CosmosDBTrigger
14. IotHubTrigger
Choose option: 2
Function name: HttpHello
HttpHello

The function "HttpHello" was created successfully from the "HttpTrigger" template.

The function is now created. Might seem a bit overcomplicated for start, but is quite self-explanatory.

For the sake of simplicity, let’s also change the AuthorizationLevel to Anonymous on line 16

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace AzureHelloFn;

public static class HttpHello
{
    [FunctionName("HttpHello")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        string name = req.Query["name"];

        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        dynamic data = JsonConvert.DeserializeObject(requestBody);
        name = name ?? data?.name;

        string responseMessage = string.IsNullOrEmpty(name)
            ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
            : $"Hello, {name}. This HTTP triggered function executed successfully.";

        return new OkObjectResult(responseMessage);
    }
}

To run function, just run func start. In output, you’ll see local http address where to access the function.

$ func start
...

Functions:

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

With following plugins Azure Toolkit for Rider and Azure Functions for VSCode, you should also be able to run project from IDE, as well as add new functions without command line. I believe Visual Studio have some support as well.

Paths and api prefix #

The path name for function is specified in FunctionName attribute. The name is NOT CASE-SENSITIVE. The supported HTTP VERBs are defined in HttpTrigger attribute (in this case “get”, “post”).

Somehow at the moment function names don’t support slash (/). In other words you cannot name function like users/get

You may also notice that by default path(s) has api prefix. This one can be modified or removed in host.json you can find in the root folder. The example below removes prefix.

{
    "version": "2.0",
    "extensions": {        "http": {            "routePrefix": ""        }    },    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            }
        }
    }
}

Dependency Injection #

So far looks good, but can’t make it far without DI.
For example we want to remove message compose logic from http function to a dedicated service MessageService.

// MessageService.cs

namespace AzureHelloFn;

public class MessageService
{
    public string ComposeMessage(string name) =>
        string.IsNullOrEmpty(name)
            ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
            : $"Hello, {name}. This HTTP triggered function executed successfully.";
}

To configure DI we’ll need Microsoft.Azure.Functions.Extensions package.

$ dotnet add package Microsoft.Azure.Functions.Extensions

To register service, let’s add Startup class that must inherit FunctionsStartup

// Startup.cs

using Microsoft.Azure.Functions.Extensions.DependencyInjection;using Microsoft.Extensions.DependencyInjection;

namespace AzureHelloFn;

public class Startup: FunctionsStartup{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<MessageService>();    }
}

The next thing to do , is to tell azure functions to use StartupClass we defined. Therefore, let’s add Assembly.cs with following code.

using AzureHelloFn;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Startup))]

As you may see, the names are not conventional. You can use whatever class name for Startup, as well as you can add FunctionStartup assembly attribute anywhere you wish (out of namespace) this is how you actually specify which class will be used at start up.

Finally, let’s update our function to use MessageService. To do so, remove static marker from class itself and method.

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace AzureHelloFn;

public class HttpHello{
    readonly MessageService _messageService;
    public HttpHello(MessageService messageService) => _messageService = messageService;
    [FunctionName("HttpHello")]
    public async Task<IActionResult> Run(        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        string name = req.Query["name"];

        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        dynamic data = JsonConvert.DeserializeObject(requestBody);
        name = name ?? data?.name;

        return new OkObjectResult(_messageService.ComposeMessage(name));    }
}

Use func start or some fancy IDE feature to run the function. The behaviour shouldn’t have changed.

Deploy to azure #

Deploy is even easier, all you need is to run func azure functionapp publish <FUNCTION-APP-NAME>, however function app must be created. Below will be instructions how to create function app using Azure CLI, be if you prefer clicking, below will be gui instructions for clickers.

In this example I’ll be creating resource group playground azure storage account dennisplaygroundstorage and function app AzureHelloFn The names for storage account and function app MUST BE GLOALLY UNIQUE, therefore you’ll need to come up with your own names!

Creating Azure function app #

You can check if you are logged in using az account show command. If you are not, use az login command and follow instructions.

$ az login

Below will be instructions how to create Function App and necessary resources using command line, but you can do it using Azure Portal as well.

Next we’ll create a resource group. If you already have a resource group created, you can skip it, otherwise use the command below with RESOURCEGROUPNAME of your choice. You can refer to List of azure region names choosing region.

$ az group create --name <RESOURCEGROUPNAME> --location <REGION>

Next, creating storage account. Once again if you already have a storage account you with to use you can skip it, Otherwise use the command below giving it an existing resource group name and STORAGEACCOUNTNAME of your choice (alfa-numeric, staring with letter). You can refer to Azure SKU types if you want to choose SKU other than Standard_LRS give on example.

$ az storage account create --name <STORAGEACCOUNTNAME> --location <REGION> --resource-group <RESOURCEGROUPNAME> --sku Standard_LRS

Finally, creating function app. You’ll need also to specify --os-type which can be windows or linux, when not specified defaults to windows. You will also need to give it a functions version, to check the version of your SDK you can run func --version (use major version, eg. first number).

$ az functionapp create --resource-group <RESOURCEGROUPNAME> \
    --storage-account <STORAGEACCOUNTNAME> \
    --consumption-plan-location <REGION> \
    --os-type <windows|linux> \
    --runtime dotnet \
    --functions-version 4 \
    --name <FUNCTIONAPPNAME>

Be patient, it might take a while.

Publishing Azure Function #

Assuming you are already logged-in in Azure CLI, you can test it using az account show command and if not login using az login command.

In the root directory of a project, just run $ func azure functionapp publish <FUNCTIONAPPNAME> like on example below. In the output it will also print http endpoint where the function can be accessed. In my example the function app name was azurehellofn.

$ func azure functionapp publish <FUNCTIONAPPNAME>
...

Functions in AzureHelloFn:
    HttpHello - [httpTrigger]
        Invoke url: https://azurehellofn.azurewebsites.net/httphello

That’s it happy coding :)

Useful resources #

In case of any issues compare your code with my git repository for this example.

© 2020 - 2024, Dennis Tretyakov