⚡ Azure Compute

Azure Functions
Complete Guide

From beginner to architecture level — hosting plans, all trigger and binding types, Durable Functions, Service Bus, Storage, Event Grid, Event Hubs, security, VNet, Managed Identity, performance, CI/CD, and every serverless pattern.

Beginner → Architecture25 SectionsAll Trigger TypesC# / Python / JSDurable Functions

01What Is Azure Functions?

Azure Functions is a serverless compute service that runs event-triggered code without provisioning or managing servers. You write a function, define what triggers it, and Azure handles scaling, patching, and infrastructure — you pay only for what you use.

Event-Driven
React to HTTP, timers, queues, blobs, databases, Event Grid, Event Hubs, and more
📦
Bindings
Declarative connections to Azure services — read/write without SDK boilerplate code
🔄
Auto-Scale
Scales from zero to thousands of instances automatically based on event load
💰
Pay-per-Use
Consumption plan: pay per execution + GB-seconds. Scale to zero when idle.
🌐
Multi-Language
C#, JavaScript, TypeScript, Python, Java, PowerShell, F# — your choice
🔗
Durable Functions
Stateful workflows, fan-out/fan-in, async HTTP, and saga patterns in code

02Core Concepts & Terminology

Understanding the core terminology is essential before building serverless solutions with Azure Functions. These concepts define how your code is organized, triggered, and connected to other Azure services. Mastering them early prevents misconfiguration and helps you design scalable, maintainable function apps.

TermDefinition
Function AppThe deployment and management unit — contains one or more functions
FunctionA single unit of code with one trigger and optional bindings
TriggerThe event that causes a function to execute — exactly one per function
Input BindingDeclarative way to read data from a service (e.g. Blob contents, Table row)
Output BindingDeclarative way to write data to a service (e.g. send queue message, write Blob)
Hosting PlanThe infrastructure model — Consumption, Flex Consumption, Premium, or Dedicated
host.jsonGlobal configuration for the function app — scale, timeouts, logging, extension settings
local.settings.jsonLocal development settings — connection strings, app settings (not deployed)
Durable FunctionsExtension enabling stateful workflows with orchestrators, activities, and entities
Cold StartLatency when a Consumption-plan function scales up from zero instances
Extension BundleNuGet package containing all binding extensions — declared in host.json
Managed IdentityPasswordless authentication to Azure services from Function App code

03Hosting Plans

Choosing the right hosting plan directly impacts cost, latency, and scaling behavior of your serverless workloads. Each plan offers different trade-offs between cold start times, VNet support, and pricing models. Evaluate your traffic patterns and latency requirements to select the plan that best fits your production needs.

PlanScaleCold StartVNetMax TimeoutBest For
Consumption0 → autoYes✗ No5–10 minCost-optimized, sporadic traffic
Flex Consumption0 → auto (faster)Reduced✓ YesUnlimitedBest of both — new recommended default
Premium (EP1/EP2/EP3)Pre-warmed → autoNone✓ YesUnlimitedLow latency, VNet, always-warm
Dedicated (App Service)Manual / autoNone✓ YesUnlimitedPredictable load, existing ASP
Container Apps0 → autoYes✓ YesUnlimitedContainerized functions
Plan RecommendationFor new projects: use Flex Consumption — it combines pay-per-use pricing with faster scale-out and VNet support. Use Premium when you need pre-warmed instances and zero cold starts for critical APIs.

Premium Plan SKUs

SKUvCPUMemoryPre-warmed
EP113.5 GB1 always-warm instance
EP227 GB1 always-warm instance
EP3414 GB1 always-warm instance

04Triggers — All Types

Triggers are the entry point for every Azure Function — they define what event causes your code to execute. Each function has exactly one trigger, and selecting the right trigger type determines how your function integrates with the broader Azure ecosystem. Understanding all available triggers helps you design event-driven architectures that react to changes in real time.

TriggerDescriptionCommon Use Case
HTTPExpose a function as an HTTP endpointAPIs, webhooks, APIM backends
TimerCron-scheduled executionBatch jobs, cleanup, reports
Service Bus QueueFire when a message arrives in a queueOrder processing, async tasks
Service Bus TopicFire when subscription receives a messageEvent fan-out processing
Blob StorageFire when a blob is created/modifiedFile processing pipeline
Queue StorageFire when a Storage Queue message arrivesSimple task queuing
Event GridReact to Event Grid eventsReal-time event routing
Event HubsProcess Event Hub event streamsIoT, telemetry, log analytics
Cosmos DBFire on Change Feed updatesReal-time change propagation
SQL (Preview)Fire on SQL table changesCDC-style change processing
Table StorageNot a trigger — input/output binding only
SignalRNegotiate + broadcast in real-timeLive dashboards, chat apps
RabbitMQReact to RabbitMQ queue messagesOn-prem messaging bridge
KafkaConsume Kafka topic partitionsHigh-throughput stream processing
DaprPub/Sub, service invocation via DaprMicroservice orchestration

05Bindings — Input & Output

Bindings let you connect to Azure services declaratively — no SDK initialization, connection management, or boilerplate. Just annotate your function parameter.

ServiceInput BindingOutput Binding
Blob StorageRead blob content by pathWrite blob content to path
Queue StorageNot availableSend message to queue
Table StorageRead entity or queryInsert/merge entity
Service BusNot availableSend message to queue/topic
Event HubsNot availableSend event to hub
Cosmos DBRead document(s) by queryUpsert document(s)
SQL DatabaseRead rows by queryInsert/upsert rows
SignalRGet connection infoBroadcast message to clients
TwilioNot availableSend SMS
SendGridNot availableSend email
Event GridNot availablePublish event to topic
HTTPRequest bodyResponse to caller

Multiple Output Bindings Example

csharp
[FunctionName("ProcessOrder")]
public static async Task Run(
    [ServiceBusTrigger("orders", Connection = "ServiceBusConn")]
    ServiceBusReceivedMessage message,
    ServiceBusMessageActions msgActions,

    // Input binding — read config from Blob
    [Blob("config/settings.json", FileAccess.Read, Connection = "StorageConn")]
    string configJson,

    // Output binding — write result to Blob
    [Blob("results/{rand-guid}.json", FileAccess.Write, Connection = "StorageConn")]
    TextWriter resultBlob,

    // Output binding — send confirmation to another queue
    [ServiceBus("confirmations", Connection = "ServiceBusConn")]
    IAsyncCollector<ServiceBusMessage> confirmQueue,

    // Output binding — upsert to Cosmos DB
    [CosmosDB("orders-db", "processed", Connection = "CosmosConn")]
    IAsyncCollector<dynamic> cosmosOutput,

    ILogger log)
{
    var order = JsonSerializer.Deserialize<Order>(message.Body.ToString());
    var config = JsonSerializer.Deserialize<Config>(configJson);

    // Write to blob
    await resultBlob.WriteAsync(JsonSerializer.Serialize(order));

    // Send confirmation
    await confirmQueue.AddAsync(new ServiceBusMessage(order.OrderId));

    // Upsert to Cosmos DB
    await cosmosOutput.AddAsync(new { id = order.OrderId, status = "processed" });

    // Complete Service Bus message
    await msgActions.CompleteMessageAsync(message);
}

06Supported Languages & Models

LanguageRuntimeModelNotes
C#.NET 8 / 9Isolated Worker (recommended) or In-ProcessBest performance + full .NET ecosystem
JavaScriptNode 18/20v4 model (recommended)ES modules, TypeScript-friendly
TypeScriptNode 18/20v4 modelCompiled to JS, full type safety
Python3.10/3.11/3.12v2 model (recommended)Decorator-based, great for ML/data
JavaJava 11/17/21StandardMaven/Gradle project structure
PowerShellPS 7.2StandardGreat for ops/automation scripts
F#.NET 8Isolated WorkerFunctional programming on .NET

C# Isolated Worker Model (.NET 8)

csharp
// Program.cs — host setup
var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddSingleton<IOrderService, OrderService>();
        services.AddApplicationInsightsTelemetryWorkerService();
    })
    .Build();

await host.RunAsync();

// OrderFunction.cs
public class OrderFunction
{
    private readonly IOrderService _orderService;

    public OrderFunction(IOrderService orderService)
    {
        _orderService = orderService;
    }

    [Function("ProcessOrder")]
    public async Task Run(
        [ServiceBusTrigger("orders", Connection = "ServiceBusConn")]
        ServiceBusReceivedMessage message,
        ServiceBusMessageActions actions)
    {
        await _orderService.ProcessAsync(message.Body.ToString());
        await actions.CompleteMessageAsync(message);
    }
}

Python v2 Model

python
import azure.functions as func
import json, logging

app = func.FunctionApp()

@app.service_bus_queue_trigger(
    arg_name="msg",
    queue_name="orders",
    connection="ServiceBusConn")
def process_order(msg: func.ServiceBusMessage):
    body = msg.get_body().decode('utf-8')
    order = json.loads(body)
    logging.info(f"Processing order: {order['orderId']}")
    # Process...

@app.route(route="orders", methods=["POST"])
def create_order(req: func.HttpRequest) -> func.HttpResponse:
    body = req.get_json()
    return func.HttpResponse(
        json.dumps({"created": True, "id": "123"}),
        mimetype="application/json",
        status_code=201)

07HTTP Trigger — Deep Dive

HTTP triggers turn your functions into fully-featured REST API endpoints, making them the most common trigger type for building serverless APIs and webhook receivers. Authorization levels control access without external infrastructure, while route parameters enable clean RESTful URL patterns. In production, pair HTTP-triggered functions with APIM for enterprise-grade security and traffic management.

Authorization Levels

LevelKey RequiredUse Case
AnonymousNonePublic APIs, APIM-fronted endpoints (APIM handles auth)
FunctionFunction-specific key in header or queryDirect API access with per-function key
AdminHost-level master keyAdmin operations only

Full HTTP Function Example (C# Isolated)

csharp
[Function("CreateOrder")]
public async Task<HttpResponseData> CreateOrder(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "orders")]
    HttpRequestData req,
    FunctionContext context)
{
    var logger = context.GetLogger<OrderFunction>();

    // Validate content type
    if (!req.Headers.TryGetValues("Content-Type", out var ct)
        || !ct.First().Contains("application/json"))
    {
        var badReq = req.CreateResponse(HttpStatusCode.BadRequest);
        await badReq.WriteAsJsonAsync(new { error = "Content-Type must be application/json" });
        return badReq;
    }

    // Deserialize body
    Order? order;
    try
    {
        order = await req.ReadFromJsonAsync<Order>();
        if (order == null) throw new JsonException("Null body");
    }
    catch (JsonException ex)
    {
        logger.LogError(ex, "Invalid request body");
        var badReq = req.CreateResponse(HttpStatusCode.BadRequest);
        await badReq.WriteAsJsonAsync(new { error = "Invalid JSON" });
        return badReq;
    }

    // Process
    var result = await _orderService.CreateAsync(order);

    // Return 201 Created
    var response = req.CreateResponse(HttpStatusCode.Created);
    response.Headers.Add("Location", $"/api/orders/{result.Id}");
    response.Headers.Add("X-Request-Id", context.InvocationId.ToString());
    await response.WriteAsJsonAsync(result);
    return response;
}

Route Parameters

csharp
// Route: GET /api/orders/{orderId}/items/{itemId}
[Function("GetOrderItem")]
public async Task<HttpResponseData> GetOrderItem(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get",
        Route = "orders/{orderId}/items/{itemId}")]
    HttpRequestData req,
    string orderId,
    string itemId)
{
    var item = await _orderService.GetItemAsync(orderId, itemId);
    if (item == null)
    {
        return req.CreateResponse(HttpStatusCode.NotFound);
    }
    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(item);
    return response;
}

08Timer & Schedule Trigger

Timer triggers use NCRONTAB expression format:{second} {minute} {hour} {day} {month} {day-of-week}.

CRON ExpressionMeaning
0 */5 * * * *Every 5 minutes
0 0 * * * *Every hour (at :00)
0 0 9 * * 1-5Every weekday at 09:00 UTC
0 0 0 * * *Every day at midnight
0 0 0 1 * *1st of every month at midnight
0 30 8 * * 1Every Monday at 08:30 UTC
csharp
[Function("DailyReport")]
public async Task Run(
    [TimerTrigger("0 0 9 * * 1-5")] TimerInfo timer,
    FunctionContext context)
{
    var logger = context.GetLogger<ReportFunction>();

    if (timer.ScheduleStatus?.Last != null)
        logger.LogInformation($"Last run: {timer.ScheduleStatus.Last}");

    if (timer.IsPastDue)
        logger.LogWarning("Timer is past due — missed schedule");

    // Run daily report logic
    var report = await _reportService.GenerateDailyAsync(DateTime.UtcNow);
    await _emailService.SendAsync("team@mycompany.com", "Daily Report", report);
}

09Service Bus Integration

Azure Service Bus is the enterprise messaging backbone for Functions, enabling reliable async communication between microservices with features like dead-lettering, sessions, and scheduled delivery. Functions integrate natively with Service Bus queues and topic subscriptions as both triggers and output bindings. Proper message handling patterns — including explicit completion, abandonment, and dead-letter routing — are critical for building resilient serverless pipelines.

Queue Trigger

csharp
[Function("ProcessOrder")]
public async Task Run(
    [ServiceBusTrigger("orders", Connection = "ServiceBusConn")]
    ServiceBusReceivedMessage message,
    ServiceBusMessageActions actions,
    ILogger log)
{
    try
    {
        string body = message.Body.ToString();
        var order = JsonSerializer.Deserialize<Order>(body)!;

        log.LogInformation($"Processing order {order.OrderId}");
        await _orderService.ProcessAsync(order);

        await actions.CompleteMessageAsync(message); // ✅ Success
    }
    catch (Exception ex)
    {
        log.LogError(ex, "Failed to process order");

        if (message.DeliveryCount >= 3)
        {
            await actions.DeadLetterMessageAsync(message,
                "MaxRetriesExceeded", ex.Message);
        }
        else
        {
            await actions.AbandonMessageAsync(message); // Retry
        }
    }
}

Topic Subscription Trigger

csharp
[Function("HandleBillingEvent")]
public void Run(
    [ServiceBusTrigger("order-events", "billing", Connection = "ServiceBusConn")]
    string messageBody,
    int deliveryCount,
    string messageId,
    ILogger log)
{
    log.LogInformation($"Billing event [{messageId}]: {messageBody}");
}

Session-Enabled Trigger

csharp
[Function("ProcessSessionOrder")]
public async Task Run(
    [ServiceBusTrigger("session-orders",
        IsSessionsEnabled = true,
        Connection = "ServiceBusConn")]
    ServiceBusReceivedMessage message,
    ServiceBusSessionMessageActions sessionActions)
{
    string sessionId = message.SessionId;

    // Get/set session state for ordered processing
    byte[] currentState = await sessionActions.GetSessionStateAsync();

    // Process in order...
    await sessionActions.CompleteMessageAsync(message);
    await sessionActions.SetSessionStateAsync(BinaryData.FromString("step-complete"));
}

Output Binding — Send to Queue

csharp
[Function("CreateOrder")]
[ServiceBusOutput("orders", Connection = "ServiceBusConn")]
public ServiceBusMessage Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]
    HttpRequestData req)
{
    string body = req.ReadAsString();
    return new ServiceBusMessage(body)
    {
        ContentType = "application/json",
        Subject = "OrderCreated",
        ApplicationProperties = { ["Source"] = "API" }
    };
}

DLQ Trigger

csharp
[Function("HandleDLQ")]
public void Run(
    [ServiceBusTrigger("orders/$deadletterqueue", Connection = "ServiceBusConn")]
    ServiceBusReceivedMessage dlqMsg,
    ILogger log)
{
    log.LogError("DLQ: {Id} | Reason: {Reason} | Desc: {Desc}",
        dlqMsg.MessageId,
        dlqMsg.DeadLetterReason,
        dlqMsg.DeadLetterErrorDescription);

    // Store to Blob for analysis, alert on-call team, replay etc.
}

host.json — Service Bus Settings

json
{
  "version": "2.0",
  "extensions": {
    "serviceBus": {
      "prefetchCount": 20,
      "transportType": "amqpWebSockets",
      "messageHandlerOptions": {
        "autoComplete": false,
        "maxConcurrentCalls": 16,
        "maxAutoRenewDuration": "00:10:00"
      },
      "sessionHandlerOptions": {
        "autoComplete": false,
        "messageWaitTimeout": "00:00:30",
        "maxConcurrentSessions": 8
      }
    }
  }
}

10Storage Integration

Azure Storage is deeply integrated with Functions — it serves as both the runtime's backing store and a rich source of triggers and bindings for Blob, Queue, and Table storage. Blob triggers enable file-processing pipelines that react to uploads automatically, while Queue triggers provide simple, cost-effective task distribution. Use input and output bindings to read and write storage data declaratively without managing SDK connections.

Blob Trigger

csharp
[Function("ProcessDocument")]
public async Task Run(
    [BlobTrigger("uploads/{name}", Connection = "StorageConn")]
    Stream blobStream,
    string name,
    BlobClient blobClient,
    ILogger log)
{
    log.LogInformation($"Processing blob: {name}, Size: {blobStream.Length}");

    BlobProperties props = await blobClient.GetPropertiesAsync();
    string contentType = props.ContentType;

    // Process the blob...
    using var reader = new StreamReader(blobStream);
    string content = await reader.ReadToEndAsync();
}

Blob Input + Output Binding

csharp
[Function("TransformDocument")]
public async Task Run(
    [QueueTrigger("transform-jobs", Connection = "StorageConn")]
    string jobId,

    // Read source blob
    [BlobInput("raw/{queueTrigger}.json", Connection = "StorageConn")]
    string sourceJson,

    // Write result blob
    [BlobOutput("processed/{queueTrigger}-result.json", Connection = "StorageConn")]
    TextWriter outputBlob)
{
    var data = JsonDocument.Parse(sourceJson);
    var transformed = TransformData(data);
    await outputBlob.WriteAsync(JsonSerializer.Serialize(transformed));
}

Queue Trigger

csharp
[Function("ProcessQueueTask")]
public void Run(
    [QueueTrigger("task-queue", Connection = "StorageConn")]
    QueueMessage message,
    ILogger log)
{
    log.LogInformation($"Task: {message.MessageText}");
    log.LogInformation($"Dequeue count: {message.DequeueCount}");
}

Table Input/Output Binding

csharp
// Write to Table Storage
[Function("LogEvent")]
[TableOutput("AuditLog", Connection = "StorageConn")]
public TableEntity Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]
    HttpRequestData req)
{
    return new TableEntity("2025-01", Guid.NewGuid().ToString())
    {
        ["EventType"] = "UserAction",
        ["UserId"] = req.Headers.GetValues("X-User-Id").First()
    };
}

// Read from Table Storage
[Function("GetUser")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "users/{id}")]
    HttpRequestData req,
    [TableInput("Users", "Profiles", "{id}", Connection = "StorageConn")]
    UserEntity? user)
{
    if (user == null) return req.CreateResponse(HttpStatusCode.NotFound);
    var res = req.CreateResponse(HttpStatusCode.OK);
    await res.WriteAsJsonAsync(user);
    return res;
}

11Event Grid Integration

Event Grid provides a fully-managed event routing service that delivers events to Functions with high reliability and low latency. It enables reactive architectures where your functions respond to resource-level events across Azure — such as blob creation, resource group changes, or custom application events. Use Event Grid triggers for real-time event processing and output bindings to publish your own domain events.

csharp
[Function("HandleBlobCreated")]
public void Run(
    [EventGridTrigger] CloudEvent cloudEvent,
    ILogger log)
{
    log.LogInformation($"Event type: {cloudEvent.Type}");
    log.LogInformation($"Event subject: {cloudEvent.Subject}");

    if (cloudEvent.Type == "Microsoft.Storage.BlobCreated")
    {
        var data = cloudEvent.Data?.ToObjectFromJson<StorageBlobCreatedEventData>();
        log.LogInformation($"Blob URL: {data?.Url}");
        log.LogInformation($"Content type: {data?.ContentType}");
        // Trigger downstream processing...
    }
}

Output Binding — Publish to Event Grid

csharp
[Function("PublishEvent")]
[EventGridOutput(TopicEndpointUri = "EventGridTopicUri",
    TopicKeySetting = "EventGridTopicKey")]
public EventGridEvent Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
    var body = req.ReadFromJsonAsync<OrderCreatedEvent>().Result;
    return new EventGridEvent(
        subject: $"orders/{body?.OrderId}",
        eventType: "OrderCreated",
        dataVersion: "1.0",
        data: body);
}

12Event Hubs Integration

Event Hubs is Azure's high-throughput streaming platform, and Functions provides native batch-processing triggers that scale with partition count. This integration is ideal for IoT telemetry ingestion, log analytics, and real-time data pipelines where millions of events per second need processing. Configure batch sizes and checkpoint frequency in host.json to balance throughput against processing latency.

csharp
// Process batch of Event Hub events
[Function("ProcessTelemetry")]
public async Task Run(
    [EventHubTrigger("telemetry-hub",
        Connection = "EventHubConn",
        ConsumerGroup = "functions-consumer")]
    EventData[] events,
    ILogger log)
{
    foreach (var evt in events)
    {
        string data = Encoding.UTF8.GetString(evt.Body.ToArray());
        log.LogInformation($"Event: {data}");
        log.LogInformation($"Partition: {evt.PartitionKey}");
        log.LogInformation($"Offset: {evt.Offset}");
        log.LogInformation($"Sequence: {evt.SequenceNumber}");

        // Process telemetry...
    }
}

// host.json — Event Hub batch settings
// "eventHub": {
//   "maxBatchSize": 64,
//   "prefetchCount": 256,
//   "batchCheckpointFrequency": 5
// }

Output Binding — Send to Event Hub

csharp
[Function("ForwardToEventHub")]
[EventHubOutput("output-hub", Connection = "EventHubConn")]
public string Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]
    HttpRequestData req)
{
    return req.ReadAsString();
}

13Durable Functions

Durable Functions extend Azure Functions with stateful workflows — fan-out/fan-in, human approval patterns, async long-running processes, and saga compensations — all in code.

ComponentRole
OrchestratorDefines the workflow — calls activities, handles timers/events (must be deterministic)
Activity FunctionDoes the actual work — calls APIs, reads DB, processes data (can be non-deterministic)
Entity FunctionStateful, actor-like object — accumulate state across calls
Client FunctionStarts, queries, signals, or terminates orchestrations

Fan-Out / Fan-In Pattern

csharp
// Orchestrator — fan-out to parallel activities, fan-in results
[Function("OrderOrchestrator")]
public static async Task<List<string>> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var order = context.GetInput<Order>();

    // Fan-out — start all tasks in parallel
    var tasks = new List<Task<string>>
    {
        context.CallActivityAsync<string>("ValidatePayment",  order),
        context.CallActivityAsync<string>("CheckInventory",   order),
        context.CallActivityAsync<string>("CalculateShipping", order)
    };

    // Fan-in — wait for ALL to complete
    var results = await Task.WhenAll(tasks);

    // Continue with all results available
    await context.CallActivityAsync("FinalizeOrder",
        new { Order = order, Results = results });

    return results.ToList();
}

// Activity Function
[Function("ValidatePayment")]
public static async Task<string> ValidatePayment(
    [ActivityTrigger] Order order, ILogger log)
{
    log.LogInformation($"Validating payment for order {order.OrderId}");
    var result = await _paymentService.ValidateAsync(order);
    return result.Status;
}

Human Approval Pattern

csharp
[Function("ApprovalOrchestrator")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var order = context.GetInput<Order>();

    // Send approval request email
    await context.CallActivityAsync("SendApprovalEmail", order);

    // Wait for external event (up to 48 hours)
    using var timeoutCts = new CancellationTokenSource();
    var deadline = context.CurrentUtcDateTime.AddHours(48);
    var approvalTask = context.WaitForExternalEvent<bool>("ApprovalResult");
    var timeoutTask = context.CreateTimer(deadline, timeoutCts.Token);

    var winner = await Task.WhenAny(approvalTask, timeoutTask);

    if (winner == approvalTask && approvalTask.Result)
    {
        timeoutCts.Cancel();
        await context.CallActivityAsync("ProcessApprovedOrder", order);
    }
    else
    {
        await context.CallActivityAsync("NotifyTimeout", order);
    }
}

Durable Patterns Summary

PatternDescriptionUse Case
Function ChainingSequential activity executionETL pipelines, ordered steps
Fan-Out / Fan-InParallel tasks, collect all resultsParallel API calls, bulk processing
Async HTTP (Polling)Return 202 + status URL, client pollsLong-running API responses
MonitorPolling loop until condition metWatch for status change, price alerts
Human InteractionWait for external event with timeoutApproval workflows, 2FA
Aggregator (Entity)Stateful accumulator per keyEvent counting, real-time totals
Saga / CompensationTrack steps, compensate on failureDistributed transactions

14Logic Apps Integration

Functions and Logic Apps are highly complementary. Functions provide the custom code execution that Logic Apps call as steps, and Logic Apps provide the visual orchestration around functions.

Call Function from Logic App

json
{
  "Call_function": {
    "type": "Function",
    "inputs": {
      "function": {
        "id": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Web/sites/<funcApp>/functions/ProcessData"
      },
      "method": "POST",
      "body": "@triggerBody()"
    }
  }
}

Function Calls Logic App via HTTP

csharp
// Function triggers a Logic App HTTP workflow
[Function("TriggerApproval")]
public async Task Run(
    [ServiceBusTrigger("high-value-orders", Connection = "ServiceBusConn")]
    string orderJson)
{
    using var http = new HttpClient();

    // Logic App HTTP trigger URL stored in Key Vault / App Settings
    var logicAppUrl = Environment.GetEnvironmentVariable("APPROVAL_LOGIC_APP_URL");

    var response = await http.PostAsync(
        logicAppUrl,
        new StringContent(orderJson, Encoding.UTF8, "application/json"));

    response.EnsureSuccessStatusCode();
}

15APIM Integration

APIM is the recommended front door for Function App HTTP triggers in production — it handles auth, rate limiting, versioning, and developer portal, while the Function App stays anonymous at the network level (or secured via Managed Identity).

  1. 1
    Import Function App into APIM
    APIM → APIs → Add API → Function App. Auto-discovers all HTTP-triggered functions.
  2. 2
    Secure with Managed Identity
    Enable Entra ID auth on the Function App. APIM gets a token via its Managed Identity to call the function.
  3. 3
    Validate JWT at APIM layer
    Add validate-jwt policy on APIM operations — Function App never sees unauthenticated requests.
  4. 4
    Rate limit at APIM layer
    Add rate-limit-by-key policy — Function App is protected from abuse without any code changes.
  5. 5
    Strip internal headers
    Remove x-functions-key and other internal headers in APIM outbound policy before returning to clients.

16Security & Authentication

Securing serverless functions requires a layered approach — from HTTP authorization levels and Entra ID authentication to Managed Identity for service-to-service communication. Proper security configuration eliminates hardcoded secrets, prevents unauthorized access, and enables zero-trust architectures. Always use Managed Identity with Key Vault references in production to keep credentials out of your code and configuration.

HTTP Authorization Levels

LevelKeyRecommended Scenario
AnonymousNoneBehind APIM (APIM handles auth), internal VNet apps
FunctionFunction-specific keyDirect access from known clients
AdminHost master keyAdmin/management operations only

Entra ID Authentication (Easy Auth)

bash
# Enable Entra ID auth on Function App
az functionapp auth update \
  --name myFuncApp \
  --resource-group myRG \
  --enabled true \
  --action LoginWithAzureActiveDirectory

# Configure Entra ID provider
az functionapp auth microsoft update \
  --name myFuncApp \
  --resource-group myRG \
  --client-id <entra-app-client-id> \
  --allowed-audiences api://my-func-app-id

Managed Identity — Access Azure Services

csharp
// Enable System Assigned Identity in Portal or CLI
// az functionapp identity assign --name myFuncApp --resource-group myRG

// In function code — no connection strings needed
var credential = new DefaultAzureCredential();

// Access Blob Storage
var blobClient = new BlobServiceClient(
    new Uri("https://mystorageaccount.blob.core.windows.net"), credential);

// Access Service Bus
var sbClient = new ServiceBusClient(
    "mynamespace.servicebus.windows.net", credential);

// Access Key Vault
var kvClient = new SecretClient(
    new Uri("https://mykeyvault.vault.azure.net"), credential);

var secret = await kvClient.GetSecretAsync("my-secret");

App Settings → Key Vault References

bash
# Reference Key Vault secret in App Settings
az functionapp config appsettings set \
  --name myFuncApp \
  --resource-group myRG \
  --settings "DB_CONNECTION=@Microsoft.KeyVault(SecretUri=https://myKeyVault.vault.azure.net/secrets/db-conn)"

# Function reads it as a normal environment variable
string connStr = Environment.GetEnvironmentVariable("DB_CONNECTION")!;

17Network Security & VNet

Network isolation is critical for enterprise serverless deployments that must access private resources or comply with regulatory requirements. VNet integration enables outbound access to private databases and services, while private endpoints restrict inbound traffic to your virtual network. Choose Premium or Flex Consumption plans when VNet connectivity is a requirement for your architecture.

FeatureConsumptionFlex ConsumptionPremium
VNet Integration (outbound)✗ No✓ Yes✓ Yes
Private Endpoints (inbound)✗ No✓ Yes✓ Yes
IP Restrictions✓ Yes✓ Yes✓ Yes
Service Endpoints✗ No✓ Yes✓ Yes
Private DNS resolution✗ No✓ Yes✓ Yes

VNet Integration (Premium/Flex)

bash
# Add VNet integration for outbound traffic
az functionapp vnet-integration add \
  --name myFuncApp \
  --resource-group myRG \
  --vnet myVNet \
  --subnet myFunctionSubnet

# Route ALL outbound traffic through VNet (access private resources)
az functionapp config appsettings set \
  --name myFuncApp --resource-group myRG \
  --settings WEBSITE_VNET_ROUTE_ALL=1

Inbound Private Endpoint

bash
# Create private endpoint for Function App (inbound)
az network private-endpoint create \
  --name myFuncPrivateEndpoint \
  --resource-group myRG \
  --vnet-name myVNet \
  --subnet myPESubnet \
  --private-connection-resource-id $(az functionapp show --name myFuncApp --resource-group myRG --query id -o tsv) \
  --group-ids sites \
  --connection-name myFuncConn

18Configuration & Settings

Proper configuration management is the foundation of reliable serverless applications — host.json controls runtime behavior like timeouts, concurrency, and extension settings, while app settings manage connection strings and feature flags. Understanding these configuration files helps you tune performance, control scaling behavior, and manage environment-specific settings across development and production. Use Managed Identity connection formats to eliminate secrets from your configuration entirely.

host.json

json
{
  "version": "2.0",
  "functionTimeout": "00:10:00",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "default": "Information",
      "Host.Results": "Error",
      "Function": "Information"
    }
  },
  "extensions": {
    "serviceBus": {
      "prefetchCount": 20,
      "messageHandlerOptions": {
        "autoComplete": false,
        "maxConcurrentCalls": 16
      }
    },
    "http": {
      "routePrefix": "api",
      "maxConcurrentRequests": 100,
      "maxOutstandingRequests": 200
    },
    "queues": {
      "maxPollingInterval": "00:00:02",
      "visibilityTimeout": "00:00:30",
      "maxDequeueCount": 5,
      "batchSize": 16
    }
  }
}

local.settings.json

json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "ServiceBusConn": "Endpoint=sb://...",
    "StorageConn": "DefaultEndpointsProtocol=https;AccountName=...",

    // For Managed Identity (no connection string!)
    "ServiceBusConn__fullyQualifiedNamespace": "<ns>.servicebus.windows.net",
    "StorageConn__accountName": "mystorageaccount",
    "CosmosConn__accountEndpoint": "https://myaccount.documents.azure.com:443/"
  }
}

19Monitoring & Diagnostics

Observability is essential for serverless applications where you don't control the infrastructure — Application Insights provides automatic telemetry collection for every function invocation, dependency call, and exception. Structured logging and custom metrics enable deep diagnostics and alerting, while KQL queries help you identify performance bottlenecks and failure patterns. Invest in monitoring early to maintain visibility as your function apps scale.

Application Insights — Auto Collected

Telemetry TypeWhat's Captured
RequestsEach function invocation — duration, status, trigger type
DependenciesOutbound HTTP, SQL, Service Bus, Storage calls
ExceptionsUnhandled exceptions with full stack trace
TracesAll ILogger log entries
Custom EventsVia TelemetryClient.TrackEvent()
Custom MetricsVia TelemetryClient.TrackMetric()
Live MetricsReal-time streaming of all telemetry

Structured Logging

csharp
// Structured logging — queryable in App Insights
_logger.LogInformation(
    "Processing order {OrderId} for customer {CustomerId} amount {Amount:C}",
    order.OrderId, order.CustomerId, order.Amount);

// Custom metric
_telemetryClient.TrackMetric("OrderAmount", order.Amount,
    new Dictionary<string, string> { ["Region"] = order.Region });

// Custom event
_telemetryClient.TrackEvent("OrderProcessed",
    new Dictionary<string, string>
    {
        ["OrderId"] = order.OrderId,
        ["ProcessingTimeMs"] = stopwatch.ElapsedMilliseconds.ToString()
    });

KQL — Function Failures

kql
// Failed function invocations in last hour
requests
| where success == false
| where timestamp > ago(1h)
| where cloud_RoleName == "my-function-app"
| summarize count() by name, resultCode
| order by count_ desc

// P99 latency by function
requests
| where timestamp > ago(24h)
| where cloud_RoleName == "my-function-app"
| summarize p99 = percentile(duration, 99) by name
| order by p99 desc

20CI/CD & DevOps

Automated deployment pipelines are critical for serverless applications where frequent, small releases are the norm. GitHub Actions and Azure DevOps provide native integration with Function Apps for build, test, and deploy workflows. Use deployment slots for blue/green deployments to achieve zero-downtime releases and instant rollback capability in production.

GitHub Actions — Deploy Function App

yaml
name: Deploy Azure Functions
on:
  push:
    branches: [main]

jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '8.0.x'

    - name: Build
      run: dotnet publish src/MyFunctionApp/MyFunctionApp.csproj \
        --configuration Release --output ./publish

    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Deploy to Function App
      uses: Azure/functions-action@v1
      with:
        app-name: myFuncApp
        package: ./publish
        respect-funcignore: true

Slot Deployment (Blue/Green)

bash
# Deploy to staging slot
az functionapp deployment source config-zip \
  --name myFuncApp \
  --resource-group myRG \
  --slot staging \
  --src ./publish.zip

# Run smoke tests against staging slot
# https://myFuncApp-staging.azurewebsites.net/api/...

# Swap staging → production (zero downtime)
az functionapp deployment slot swap \
  --name myFuncApp \
  --resource-group myRG \
  --slot staging \
  --target-slot production

21Performance & Scaling

Performance optimization in serverless requires different thinking than traditional applications — cold starts, connection management, and concurrency settings have outsized impact on latency and throughput. Following these best practices ensures your functions scale efficiently without hitting resource limits or accumulating unnecessary costs. Profile under realistic load to identify bottlenecks before they affect production users.

TechniqueDescription
Avoid cold startsUse Premium (pre-warmed) or Flex Consumption for latency-sensitive HTTP functions
Reuse connectionsDeclare HttpClient, ServiceBusClient as static singletons — not per-invocation
Prefetch messagesSet prefetchCount in host.json for Service Bus and Event Hubs triggers
Increase concurrencyRaise maxConcurrentCalls for Service Bus, batchSize for Queue triggers
Use Managed IdentityAvoids HTTPS token refresh overhead vs connection string auth
Stream large blobsUse Stream instead of byte[] for large blob input to avoid OOM
Async all the wayUse async/await throughout — never .Result or .Wait() in async functions
Right-size the planProfile under load — EP1 may be over/under for your workload

Singleton Pattern for Clients

csharp
// Program.cs — register as singletons via DI
var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        // Registered once, reused across all invocations
        services.AddSingleton(_ =>
            new ServiceBusClient(
                "<namespace>.servicebus.windows.net",
                new DefaultAzureCredential()));

        services.AddSingleton(_ =>
            new BlobServiceClient(
                new Uri("https://account.blob.core.windows.net"),
                new DefaultAzureCredential()));

        services.AddHttpClient("backend", c =>
        {
            c.BaseAddress = new Uri("https://api.mybackend.com");
            c.Timeout = TimeSpan.FromSeconds(30);
        });
    })
    .Build();

22Architecture Patterns

🌐
Serverless API
HTTP-triggered functions behind APIM — full REST API without any servers to manage.
⚙️
Event-Driven Processing
Service Bus → Function → transform → Cosmos DB + Event Hub. Classic async pipeline.
🔄
Saga Orchestration
Durable Orchestrator coordinates Payment, Inventory, Shipping with compensating transactions.
📊
Stream Analytics
Event Hubs trigger → batch processing → aggregate → write to ADLS Gen2 or Synapse.
📎
Claim-Check Pattern
Large payloads stored in Blob. Function receives only URL via Service Bus, fetches payload.
⏱️
Async HTTP Long-Running
POST → 202 Accepted + statusUrl. Durable Functions AsyncHttp pattern. Client polls for result.

23Functions vs Logic Apps vs APIM

Azure offers three complementary serverless services that are often confused — Functions for custom code execution, Logic Apps for visual workflow orchestration, and APIM for API gateway management. Understanding their strengths and boundaries helps you compose them effectively rather than forcing one tool to do everything. In practice, most enterprise solutions use all three together in a layered architecture.

DimensionAzure FunctionsLogic AppsAPIM
NatureServerless code executionVisual workflow orchestrationAPI gateway / proxy
ProgrammingCode — C#, Python, JS, JavaDeclarative JSON + expressionsXML policies + config
StateStateless (Durable = stateful)Stateful runs built-inStateless per request
ConnectorsVia SDK bindings (~20)500+ managed connectorsHTTP via send-request
Long runningOnly with DurableDays/weeks nativelyNot applicable
Best forCustom logic, data transforms, APIsSaaS integration, approvals, ETLSecurity, routing, rate limiting
Local devFull local emulation (func start)VS Code + Azurite (Standard)Limited
Cold startConsumption: yes / Premium: noMinimalNone
🏗️
They're ComplementaryThe power trio: APIM (gateway + security) → Logic Apps (orchestration + SaaS connectors) → Functions (custom code execution). Each does what the others can't.

24Pricing Overview

Azure Functions pricing varies significantly by hosting plan — from true pay-per-execution on Consumption to fixed monthly costs on Premium. Understanding the pricing model helps you estimate costs accurately and choose the right plan for your workload profile. Monitor GB-seconds consumption closely, as memory-intensive functions can accumulate costs faster than expected.

Consumption Plan

ResourceFree Grant/MonthPrice After
Executions1 million/month~$0.20 per million
GB-seconds400,000 GB-s/month~$0.000016 per GB-s

Premium Plan (Always-Warm)

SKUvCPUMemoryPrice (approx)
EP11 vCPU3.5 GB~$140/month (1 instance)
EP22 vCPU7 GB~$280/month (1 instance)
EP34 vCPU14 GB~$560/month (1 instance)
💰
Cost OptimizationConsumption plan is cheapest for sporadic workloads. Avoid memory-heavy operations — GB-seconds accumulate fast. Use Flex Consumption for VNet + pay-per-use. Share App Service Plan with App Services for consistent-load workloads.

25Quick Reference Cheat Sheet

Keep this cheat sheet handy for the most commonly referenced Azure Functions syntax, CLI commands, and configuration patterns. These quick references cover CRON expressions, Managed Identity connection formats, and essential Azure CLI commands for daily serverless development. Bookmark this section for fast lookups during development and troubleshooting.

CRON Expressions (6-field NCRONTAB)
text
Format: {second} {minute} {hour} {day} {month} {weekday}

0 */5 * * * *       → Every 5 minutes
0 0 * * * *         → Every hour
0 0 9 * * 1-5       → Weekdays at 09:00 UTC
0 0 0 * * *         → Daily at midnight
0 0 0 1 * *         → Monthly (1st day)
0 30 8 * * 1        → Every Monday at 08:30
Managed Identity Connection Settings
json
// local.settings.json — use these for Managed Identity (no secrets)
{
  "ServiceBusConn__fullyQualifiedNamespace": "<ns>.servicebus.windows.net",
  "StorageConn__accountName": "mystorageaccount",
  "CosmosConn__accountEndpoint": "https://myaccount.documents.azure.com:443/",
  "EventHubConn__fullyQualifiedNamespace": "<ns>.servicebus.windows.net"
}
Common CLI Commands
bash
# Create Function App (Flex Consumption)
az functionapp create \
  --name myFuncApp --resource-group myRG \
  --storage-account mystorageaccount \
  --environment myContainerAppEnv \
  --runtime dotnet-isolated --runtime-version 8

# Deploy via zip
func azure functionapp publish myFuncApp

# Stream live logs
az functionapp log tail --name myFuncApp --resource-group myRG

# Set app setting
az functionapp config appsettings set \
  --name myFuncApp --resource-group myRG \
  --settings "MY_SETTING=myvalue"

# Enable Managed Identity
az functionapp identity assign \
  --name myFuncApp --resource-group myRG

# Scale out Premium plan
az functionapp plan update \
  --name myPremiumPlan --resource-group myRG \
  --max-burst 20
LimitConsumptionPremium
Max timeout5 min (10 min configurable)Unlimited
Max memory1.5 GB3.5–14 GB (by SKU)
Max scale-out (Consumption)200 instances100 instances
Max HTTP request size100 MB100 MB
Max function app size1 GB (deployed)No limit
Min instances0 (scale to zero)1 pre-warmed
Durable Functions✓ Yes✓ Yes
VNet✗ No✓ Yes