There is a strong tendency to move web applications to the cloud these days. You could say that serverless computing is becoming the new normal. Why not? It’s quick, cheap, practical, and you can run the app anywhere without the painstaking process of managing the accompanying infrastructure.
Azure Functions enables us to run small chunks of code in the cloud and pay on demand, only when the code runs. Running small solutions can even be free. The development environment is flexible, you can develop your functions directly through the Azure Portal or locally in Visual Studio.
This article focuses on continuous integration through Visual Studio as it would be done for a bigger solution.
What is new in the Azure Functions process?
Azure Functions code has always been tightly linked with the .NET version of Azure Functions Runtime. Unfortunately, this means that you weren’t able to write your code using the latest version of .NET until the Azure Functions team at Microsoft updated their Azure Functions .NET Runtime.
To avoid this inconvenience, Microsoft introduced the Isolated Process model. It allowed us to write the Azure Functions code using any version of .NET, saving developers from future headaches and giving them the flexibility they need.

Phases of support for in-process and isolated process through .NET releases
Running .NET functions in an out-of-process model has many benefits. It gives you complete control of the process so you can manage the app’s start-up, configuration and the middlewares. Assemblies from your app don’t conflict with different versions of the identical assemblies used by host processes.
Isolated processes are becoming the recommended way of creating Azure Functions, as shown in the image above. According to the announcements, in-process functions are to be fully retired in favor of isolated functions, however not before .NET 7 (scheduled to ship in November 2022) becomes available.
How to create an Azure project
If you haven’t already, update .NET to version 6. You can do that by updating to the new shiny Visual Studio 2022 with built-in SDK or downloading it from here: .NET 6.0 SDK.
In Visual Studio 2022, Azure Functions Core Tools, necessary for developing Azure Functions, are available by adding the Azure Cloud Tool in the installation process of VS. But if you’re not using Visual Studio to create an Azure Functions project, you will need to have Azure Functions Core Tools installed. They include the commands to create functions, deploy function projects and connect to Azure.
Furthermore, Azure Functions are interlocked with Azure Storage services. To be able to run functions, we have to provide a general-purpose Azure Storage account that will keep track of function execution state and triggers. However, if we want to run them locally, without specifying a storage account, the Azure storage emulator will be used. The emulator is included in VS22 by default, so there’s no need to install it separately. For exploring storage account data, we can use the Azure Storage Explorer.
Creating a separate Functions project
There are two ways of creating Azure functions, through your IDE or through the Command Prompt. In this article, we are going with the latter. To create an Azure Functions project, we can run the command through the terminal window or the Command Prompt. Navigate to the desired folder and run the following command, you can obviously give it any name you want:
func init ProjectName
This command will create a new folder with the project name to nest your functions.
After that, you may be prompted to choose the runtime for your Azure Functions project. Since we are using .NET with an isolated process, we will choose dotnet (isolated process).
After scaffolding, you should see something like this in your desired folder:

host.json
The global configuration file host.json includes settings that affect all functions deployed under the same function app. Host.json gets published when the functions are deployed. Additional information about settings available in the host.json file can be found on this link.
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
local.settings.json
App settings, connection strings, and settings utilized by local development are all stored in the local.settings.json file. This file is only used when projects are run locally.
{
"IsEncrypted": false,
"Values" : {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"MyConnection": "<connection-string-to-your-databse>",
"MyOptions:Option1": "value",
"MyOptions:Option2": "value"
},
"ConnectionStrings": {
"DefaultConnection": "UseDevelopmentStorage=true"
}
}
UseDevelopmentStorage=true
is equivalent to the connection string for the emulator. If you want to use a value from the local.settings.json file in the function entrance, you can use "%MyOptions.Option1%"
. This is only applicable in the Values field. If you want to read a connection string from the Values field, you don’t need to use the percentage characters. The Connection Strings field is read like this: "ConnectionStrings:DefaultConnection"
.
Program.cs
Dependency registration, options configuration, and db context configuration are all set in the Program.cs class. From there, we use dependency injection the same way we use it in any class.
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddDbContext(options
=> Options.UseSqlServer(
Environment.GetEnvironmentVariable("MyConnection")));
services.AddOptions()
.Configure((settings, configuration)
=> configuration.GetSection("MyOptions").Bind(settings));
services.AddScoped();
services.AddScoped();
}).Build();
host.Run();
}
}
How to create Azure functions
Now that everything is set up, we can create our first Azure function. Again, we come back to the Command Prompt or the terminal window, navigate to the folder we created, and run the command:
func new
You will then be prompted to choose the function template.

There are a couple of templates, so here is a short review of a few most commonly used functions.
Queue Trigger
A function is triggered when a new message in the queue appears. The function “listens” to the queue storage and when a new message comes in, the function logic is executed.
Azure Queue Storage is a service used for storing a large number of messages in the cloud, which would later be processed by applications at their own pace. Primarily it helps with the assured delivery of system messages between and within applications. The maximum message size is 64 kB.
It should be noted that Queue Storage does not support data ordering, so the order of enqueuing is not guaranteed. However, it helps with scaling applications and in managing sudden traffic bursts by offloading background and non-interactive workloads. Queue storage can also be used for decoupling application components.
public static class QueueTrigger
{
[Function("QueueTrigger")]
public static void Run(
[QueueTrigger("myqueue-items", Connection = "")] string myQueueItem, ILogger log)
{
var logger = context.GetLogger("QueueTrigger");
logger.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}
}
The class and the method don’t have to be static.
Http Trigger
HTTP triggers translate the incoming HTTP request message into an HttpRequestData object that is passed to the function. This object provides data from the request, including Headers, Cookies, Identities, URL, and optional message Body. It is a representation of the HTTP request object and not the request itself.
public static class HttpTrigger
{
[Function("HttpTrigger")]
public static HttpResponseData Run(
[HttpTrigger(AuthorizationLevel.Function,"get", "post")] HttpRequestData req,
ILogger log)
{
var logger = executionContext.GetLogger("HttpTrigger");
logger.LogInformation("C# HTTP trigger function processed a request.");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString("Welcome to Azure Functions!");
return response;
}
}
Blob Trigger
The function runs every time a new file is created or updated in a blob container. The blob contents are provided as input to the function.
Blob Containers are used for storing unstructured data (text or binary), so, really, any type of data (audio, video, backups, archiving, text…). Blobs are then grouped into “containers” and tied to storage accounts. They are massively scalable.
public static class BlobTrigger
{
[Function("BlobTrigger")]
public static void Run(
[BlobTrigger("samples-workitems/{name}", Connection = "")] string myBlob,
string name,
ILogger log)
{
var logger = context.GetLogger("BlobTrigger");
logger.LogInformation($"C# Blob trigger function Processed blob\n Name: {name} \n
Data: {myBlob}");
}
}
Timer Trigger
This function enables us to schedule a recurring function execution at a specified time. It uses CRON expressions to provide timing information. The CRON expression consists of six parts – second, minute, hour, day, month, and day of the week and each part is separated by space. More about CRON expressions and their usage can be found at the link.
Triggering is always cyclic; for example, the 0 0 0 * * *
CRON expression triggers the function once every day. There is also a neat Cron expression generator, which makes things easier.
public static class TimerTrigger
{
[Function("TimerTrigger")]
public static void Run([TimerTrigger("0 */5 * * * *")]
MyInfo myTimer,
ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
log.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus.Next}");
}
}
public class MyInfo
{
public MyScheduleStatus ScheduleStatus { get; set; }
public bool IsPastDue { get; set; }
}
public class MyScheduleStatus
{
public DateTime Last { get; set; }
public DateTime Next { get; set; }
public DateTime LastUpdated { get; set; }
}
The example above includes a separate class that contains more info about the execution schedule, including previous and next execution times.
How to run functions
Everything we did up to this point was performed in the terminal or the Command Prompt. Yes, you guessed it, we will run the functions in the same place. We should just navigate to the file where our functions reside and enter the command:
func host start
This command starts all the functions you have in the project.
If you want to run a specific function, enter:
func host start --functions NameOfTheFunction
You can debug your function as you debug any .NET application.
Azure Functions – a cloud-based pioneer
Congratulations, you now know how to create and run Azure Functions in .NET 6! Azure Functions provide us with a very sleek and powerful way of creating lightweight applications. This serverless solution allows us to maintain less infrastructure, write less code, and save on costs. Thanks to continuous improvements and feature enrichment, we will see more and more of them in the future.