<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged azurefunctions</title>
<link>https://coderlegion.com/tag/azurefunctions</link>
<description>Powered by Question2Answer</description>
<item>
<title>Multi-Cloud Resilience: The Event-Driven Cellular Architecture Blueprint across AWS and Azure</title>
<link>https://coderlegion.com/16402/multi-cloud-resilience-the-event-driven-cellular-architecture-blueprint-across-aws-azure</link>
<description>&lt;h5 id=&quot;1-introduction&quot;&gt;1. Introduction&lt;/h5&gt;
&lt;p&gt;As enterprise systems reach massive scale, relying on a single cloud provider introduces systemic risk. Regional outages, vendor-specific API degradations, or severe misconfigurations can result in global downtime. Multi-cloud cellular architecture solves this by deploying isolated, self-contained failure domains (cells) across different cloud providers. In this tutorial, you will architect a unified, event-driven system where AWS and Azure serve as the physical hosts for identical logical cells. &lt;/p&gt;
&lt;p&gt;Instead of building a &quot;stretched&quot; application that queries data across clouds—an anti-pattern that guarantees high latency and fragile dependencies—you will deploy complete, asynchronous processing pipelines on both AWS and Azure. A global, cloud-agnostic edge routing layer will inspect incoming traffic and forward it to either an AWS cell or an Azure cell based on a partition key (such as &lt;code&gt;TenantId&lt;/code&gt;). By the end of this guide, you will understand how to orchestrate this ultimate fault-isolation boundary using Terraform, ensuring your application survives even a total cloud provider outage.&lt;/p&gt;
&lt;h5 id=&quot;2-prerequisites&quot;&gt;2. Prerequisites&lt;/h5&gt;
&lt;p&gt;To build a multi-cloud environment, your operational tooling must be strictly cloud-agnostic. You will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Active accounts on both Amazon Web Services (AWS) and Microsoft Azure with administrative privileges.&lt;/li&gt;
&lt;li&gt;The AWS CLI and Azure CLI installed and authenticated on your local machine.&lt;/li&gt;
&lt;li&gt;Terraform (version 1.3.0 or higher) installed. Terraform is crucial here, as it is the singular control plane capable of deploying resources to both clouds simultaneously.&lt;/li&gt;
&lt;li&gt;Understanding of Global Server Load Balancing (GSLB) or Edge Compute platforms (like Cloudflare Workers or Fastly) to act as the multi-cloud router.&lt;/li&gt;
&lt;li&gt;A Domain-Driven Design (DDD) approach to your data, ensuring that a specific tenant's data lives entirely within one cell and never needs to cross the multi-cloud boundary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;3-step-by-step&quot;&gt;3. Step-by-Step&lt;/h5&gt;
&lt;h6 id=&quot;step-1-configuring-multi-cloud-providers-in-terraform&quot;&gt;Step 1: Configuring Multi-Cloud Providers in Terraform&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Create a root Terraform configuration that initializes providers for both AWS and Azure simultaneously. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do it:&lt;/strong&gt; Infrastructure as Code (IaC) is the only sustainable way to manage multi-cloud environments. By declaring both providers in the same root module, you can orchestrate the deployment of AWS cells, Azure cells, and the DNS records that tie them together from a single pipeline, preventing configuration drift between your cloud environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Example (&lt;code&gt;main.tf&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;terraform {
  required_version = &quot;&amp;gt;= 1.3.0&quot;
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&amp;gt; 5.0&quot;
    }
    azurerm = {
      source  = &quot;hashicorp/azurerm&quot;
      version = &quot;~&amp;gt; 3.0&quot;
    }
  }
}

provider &quot;aws&quot; {
  region = &quot;us-east-1&quot;
}

provider &quot;azurerm&quot; {
  features {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6 id=&quot;step-2-deploying-the-aws-and-azure-data-plane-cells&quot;&gt;Step 2: Deploying the AWS and Azure Data Plane Cells&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Utilize the modular approach to instantiate independent cells on both clouds. The AWS cell utilizes DynamoDB, SNS, and SQS. The Azure cell utilizes Cosmos DB, Service Bus Topics, and Subscriptions. Both rely on serverless compute (Lambda and Azure Functions).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do it:&lt;/strong&gt; While the underlying managed services differ, the architectural pattern is identical: an HTTP entry point writes to a NoSQL database, which triggers a change stream, which is fanned out via a message bus to consumer workers. Encapsulating these into Terraform modules abstracts the cloud-specific complexities, allowing you to treat &quot;AWS&quot; and &quot;Azure&quot; simply as deployment targets for your business logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Example (&lt;code&gt;data_plane.tf&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;# AWS Cell (East Coast)
module &quot;cell_aws_alpha&quot; {
  source  = &quot;./modules/aws_event_cell&quot;
  cell_id = &quot;aws-alpha&quot;
  region  = &quot;us-east-1&quot;
}

# Azure Cell (East Coast)
module &quot;cell_azure_beta&quot; {
  source   = &quot;./modules/azure_event_cell&quot;
  cell_id  = &quot;azure-beta&quot;
  location = &quot;East US&quot;
}

# Azure Cell (Europe)
module &quot;cell_azure_gamma&quot; {
  source   = &quot;./modules/azure_event_cell&quot;
  cell_id  = &quot;azure-gamma&quot;
  location = &quot;North Europe&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6 id=&quot;step-3-implementing-the-cloud-agnostic-edge-router&quot;&gt;Step 3: Implementing the Cloud-Agnostic Edge Router&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Deploy a global routing layer that sits entirely outside of AWS and Azure. This is typically achieved using an Edge Compute platform like Cloudflare Workers. The Worker intercepts the HTTP request, reads the &lt;code&gt;TenantId&lt;/code&gt;, checks a globally distributed Key-Value store to find the assigned cell's API endpoint, and forwards the payload.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do it:&lt;/strong&gt; If your global router is hosted on AWS (e.g., using API Gateway), and AWS experiences an outage, your Azure cells become unreachable, defeating the purpose of multi-cloud. The routing layer must be cloud-agnostic. Cloudflare Workers execute at the edge, providing sub-millisecond lookups for the routing map and directing traffic to the respective AWS API Gateway or Azure Function HTTP trigger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conceptual Edge Router Logic (JavaScript for Edge Worker):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const tenantId = request.headers.get(&quot;X-Tenant-ID&quot;);

    if (!tenantId) {
      return new Response(&quot;Missing Tenant ID&quot;, { status: 400 });
    }

    // Lookup cell assignment from global edge KV store
    const cellEndpoint = await env.ROUTING_MAP.get(tenantId);

    if (!cellEndpoint) {
      return new Response(&quot;Tenant mapping not found&quot;, { status: 404 });
    }

    // Proxy the request to the target cloud (AWS or Azure)
    const targetUrl = new URL(url.pathname, cellEndpoint);
    const modifiedRequest = new Request(targetUrl, request);
    
    return fetch(modifiedRequest);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6 id=&quot;step-4-automating-disaster-recovery-and-traffic-shifting&quot;&gt;Step 4: Automating Disaster Recovery and Traffic Shifting&lt;/h6&gt;
&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Establish a process to update the global routing map. If the AWS region hosting &lt;code&gt;cell_aws_alpha&lt;/code&gt; goes offline, you update the Edge KV store to point those affected tenants to a standby cell on Azure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do it:&lt;/strong&gt; Cellular architecture drastically reduces the blast radius of an outage, but you still need a mechanism to restore service for the degraded cell. By decoupling the routing logic from the compute infrastructure, shifting traffic between clouds becomes a simple Key-Value update at the DNS/Edge layer, resulting in near-instantaneous failover.&lt;/p&gt;
&lt;h5 id=&quot;4-common-troubleshooting&quot;&gt;4. Common Troubleshooting&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Multi-Cloud Data Split Brain:&lt;/strong&gt; The biggest mistake in multi-cloud architecture is attempting synchronous database replication between AWS and Azure. This introduces severe latency and massive egress costs. Ensure strict cellular isolation: a tenant's data must live and be processed entirely within their assigned cell. Data should only cross clouds during a deliberate, asynchronous disaster recovery migration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI/CD Pipeline Complexity:&lt;/strong&gt; Deploying to multiple clouds means your deployment pipelines must authenticate with two different IAM systems. Utilize OpenID Connect (OIDC) in platforms like GitHub Actions or GitLab CI. OIDC allows your pipelines to assume temporary, secure roles in both AWS (via IAM Identity Center) and Azure (via Microsoft Entra Workload ID) without storing long-lived, vulnerable access keys.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability Fragmentation:&lt;/strong&gt; AWS logs live in CloudWatch; Azure logs live in Log Analytics. Troubleshooting a multi-cloud environment requires a unified view. You must export logs and metrics from both cloud providers into a centralized, agnostic observability platform (like Datadog, New Relic, or a self-hosted ELK stack) to effectively monitor the health of your global cells.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id=&quot;5-conclusion&quot;&gt;5. Conclusion&lt;/h5&gt;
&lt;p&gt;Building an event-driven cellular architecture across multiple clouds is the pinnacle of infrastructure resilience. By treating AWS and Azure as interchangeable hosts for isolated data planes, and governing them with a cloud-agnostic edge router and Terraform, you eliminate single points of failure at the vendor level. While this approach introduces operational complexity, it guarantees that a regional outage or provider degradation remains a contained incident rather than a business-ending catastrophe. As a next step, focus on standardizing your application packaging by utilizing containers (ECS on AWS, Container Apps on Azure) to ensure your worker code executes identically regardless of the host cloud.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16402/multi-cloud-resilience-the-event-driven-cellular-architecture-blueprint-across-aws-azure</guid>
<pubDate>Sat, 02 May 2026 12:42:02 +0000</pubDate>
</item>
<item>
<title>Your First Azure Function: HTTP Triggers Step-by-Step</title>
<link>https://coderlegion.com/11565/your-first-azure-function-http-triggers-step-by-step</link>
<description>&lt;p&gt;Theory doesn't ship features. You learned the why in &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11426/why-azure-functions-serverless-for-net-developers&quot;&gt;Part 1&lt;/a&gt;—when serverless makes sense, the economics, how it compares to App Service and Container Apps. Now let's build something.&lt;/p&gt;
&lt;p&gt;In this second part of the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11426/why-azure-functions-serverless-for-net-developers&quot;&gt;Azure Functions for .NET Developers&lt;/a&gt; series, we'll create a project from scratch, understand every line of generated code, and build three HTTP trigger patterns that cover most real-world API scenarios. By the end, you'll have a working HTTP API running locally on your machine. No Azure subscription required.&lt;/p&gt;
&lt;p&gt;All code from this article is available in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MO2k4/azure-functions-samples&quot;&gt;azure-functions-samples&lt;/a&gt; repository.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;You need four tools installed before we start. Here's what to check and where to get them:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Tool &lt;/th&gt;&lt;th&gt;Verify &lt;/th&gt;&lt;th&gt;Expected&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;.NET 10 SDK &lt;/td&gt;&lt;td&gt;&lt;code&gt;dotnet --version&lt;/code&gt; &lt;/td&gt;&lt;td&gt;10.x&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Azure Functions Core Tools &lt;/td&gt;&lt;td&gt;&lt;code&gt;func --version&lt;/code&gt; &lt;/td&gt;&lt;td&gt;4.x&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VS Code + Azure Functions extension &lt;/td&gt;&lt;td&gt;— &lt;/td&gt;&lt;td&gt;Latest&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Azurite &lt;/td&gt;&lt;td&gt;&lt;code&gt;npx azurite --version&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Any&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;.NET 10 SDK&lt;/strong&gt; is required to build and run our function app. It's an LTS release, supported through November 2028. Grab it from &lt;a rel=&quot;nofollow&quot; href=&quot;https://dot.net/download&quot;&gt;dot.net/download&lt;/a&gt; if you don't have it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Azure Functions Core Tools v4&lt;/strong&gt; lets you create, run, and debug functions locally. Install via npm, Homebrew, or Chocolatey:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# npm (any OS)
npm install -g azure-functions-core-tools@4

# Homebrew (macOS)
brew tap azure/functions
brew install azure-functions-core-tools@4

# Chocolatey (Windows)
choco install azure-functions-core-tools-4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;VS Code with the Azure Functions extension&lt;/strong&gt; gives you project templates, debugging, and deployment in one place. Visual Studio and Rider work too—use whatever you're comfortable with. The CLI commands in this article work regardless of editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Azurite&lt;/strong&gt; emulates Azure Storage locally. The Functions runtime needs a storage connection even for HTTP triggers (it uses storage internally for features like function key management and, in production, for coordinating across instances). Install it globally or run it on demand:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Install globally
npm install -g azurite

# Or run on demand without installing
npx azurite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the verification commands all return version numbers and you have VS Code with the Azure Functions extension installed, you're ready.&lt;/p&gt;
&lt;h3 id=&quot;creating-your-first-project&quot;&gt;Creating Your First Project&lt;/h3&gt;
&lt;p&gt;With your tools in place, open a terminal and scaffold a new function app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;func init HttpTriggerDemo --worker-runtime dotnet-isolated --target-framework net10.0
cd HttpTriggerDemo
func new --template &quot;HTTP trigger&quot; --name Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first command creates the project. &lt;code&gt;--worker-runtime dotnet-isolated&lt;/code&gt; selects the isolated worker model (the modern default—we'll explain what &quot;isolated&quot; means in Part 5). &lt;code&gt;--target-framework net10.0&lt;/code&gt; targets .NET 10.&lt;/p&gt;
&lt;p&gt;Once inside the project directory, &lt;code&gt;func new&lt;/code&gt; adds an HTTP-triggered function called &lt;code&gt;Hello&lt;/code&gt; from a built-in template.&lt;/p&gt;
&lt;p&gt;Your project now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HttpTriggerDemo/
├── Hello.cs              # Your function code
├── Program.cs            # Application entry point
├── HttpTriggerDemo.csproj # Project file
├── host.json             # Runtime configuration
└── local.settings.json   # Local environment variables
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-project-file&quot;&gt;The Project File&lt;/h4&gt;
&lt;p&gt;Open &lt;code&gt;HttpTriggerDemo.csproj&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;AzureFunctionsVersion&amp;gt;v4&amp;lt;/AzureFunctionsVersion&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;FrameworkReference Include=&quot;Microsoft.AspNetCore.App&quot; /&amp;gt;
    &amp;lt;PackageReference Include=&quot;Microsoft.Azure.Functions.Worker&quot; Version=&quot;2.51.0&quot; /&amp;gt;
    &amp;lt;PackageReference Include=&quot;Microsoft.Azure.Functions.Worker.Sdk&quot; Version=&quot;2.0.7&quot; /&amp;gt;
    &amp;lt;PackageReference Include=&quot;Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore&quot; Version=&quot;2.1.0&quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;OutputType: Exe&lt;/code&gt;&lt;/strong&gt; — the isolated worker model runs as a standalone executable, not a class library loaded into the Functions host. This is what gives you full control over the process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;FrameworkReference: Microsoft.AspNetCore.App&lt;/code&gt;&lt;/strong&gt; — brings in ASP.NET Core types like &lt;code&gt;HttpRequest&lt;/code&gt; and &lt;code&gt;IActionResult&lt;/code&gt;. Without this, you'd use the lower-level &lt;code&gt;HttpRequestData&lt;/code&gt; API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Extensions.Http.AspNetCore&lt;/code&gt;&lt;/strong&gt; — the bridge that maps ASP.NET Core's HTTP abstractions to the Functions runtime. This is what makes the developer experience feel like writing a regular web API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ImplicitUsings: enable&lt;/code&gt;&lt;/strong&gt; — the compiler automatically includes common &lt;code&gt;using&lt;/code&gt; statements (&lt;code&gt;System&lt;/code&gt;, &lt;code&gt;System.Collections.Generic&lt;/code&gt;, &lt;code&gt;System.Linq&lt;/code&gt;, &lt;code&gt;System.Threading.Tasks&lt;/code&gt;, and others). That's why our code files don't start with a block of &lt;code&gt;using&lt;/code&gt; directives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Nullable: enable&lt;/code&gt;&lt;/strong&gt; — turns on nullable reference types. The compiler warns you when code might dereference &lt;code&gt;null&lt;/code&gt; without checking. You'll see this in action when we read query string values—&lt;code&gt;req.Query[&quot;name&quot;]&lt;/code&gt; returns &lt;code&gt;string?&lt;/code&gt;, not &lt;code&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-entry-point&quot;&gt;The Entry Point&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Program.cs&lt;/code&gt; is minimal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using Microsoft.Azure.Functions.Worker.Builder;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Build().Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ConfigureFunctionsWebApplication()&lt;/code&gt; is the key line. It enables ASP.NET Core integration—without it, you'd need to use &lt;code&gt;HttpRequestData&lt;/code&gt; and &lt;code&gt;HttpResponseData&lt;/code&gt; instead of the familiar &lt;code&gt;HttpRequest&lt;/code&gt; and &lt;code&gt;IActionResult&lt;/code&gt;. For HTTP-triggered functions, this is almost always what you want.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FunctionsApplication.CreateBuilder(args)&lt;/code&gt; is a convenience factory introduced in Worker SDK 2.0. It returns a pre-configured &lt;code&gt;IHostApplicationBuilder&lt;/code&gt; with Functions defaults already wired up—logging, JSON serializer options, and middleware. The key &lt;code&gt;using&lt;/code&gt; to remember is &lt;code&gt;Microsoft.Azure.Functions.Worker.Builder&lt;/code&gt;, which brings the factory method into scope. You may also see an older pattern that starts with &lt;code&gt;new HostBuilder()&lt;/code&gt; and calls &lt;code&gt;.ConfigureFunctionsWorkerDefaults()&lt;/code&gt;—it still works, but requires more manual setup (for example, explicit &lt;code&gt;ConfigureAppConfiguration()&lt;/code&gt; calls to load &lt;code&gt;appsettings.json&lt;/code&gt;). &lt;code&gt;func init&lt;/code&gt; scaffolds &lt;code&gt;FunctionsApplication.CreateBuilder&lt;/code&gt; by default, so stick with it for new projects.&lt;/p&gt;
&lt;p&gt;This is also where you'd register services for dependency injection, add middleware, or configure logging. We'll keep it simple for now.&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-generated-code&quot;&gt;Understanding the Generated Code&lt;/h3&gt;
&lt;p&gt;With the project structure in place, let's look at the function itself. The &lt;code&gt;func new&lt;/code&gt; command created a file called &lt;code&gt;Hello.cs&lt;/code&gt; with a complete working function. Before we run it, let's understand what each part does. Open &lt;code&gt;Hello.cs&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class Hello(ILogger&amp;lt;Hello&amp;gt; logger)
{
    [Function(&quot;Hello&quot;)]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;, &quot;post&quot;)] HttpRequest req)
    {
        logger.LogInformation(&quot;C# HTTP trigger function processed a request.&quot;);
        return new OkObjectResult(&quot;Welcome to Azure Functions!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's break this apart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;public class Hello(ILogger&amp;lt;Hello&amp;gt; logger)&lt;/code&gt;&lt;/strong&gt; uses a C# 12 primary constructor. The &lt;code&gt;ILogger&amp;lt;Hello&amp;gt;&lt;/code&gt; parameter is injected by the dependency injection container automatically—no need to write a separate constructor or field assignment. The logger is available throughout the class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;[Function(&quot;Hello&quot;)]&lt;/code&gt;&lt;/strong&gt; registers this method with the Functions runtime. The string &lt;code&gt;&quot;Hello&quot;&lt;/code&gt; becomes the function's name and appears in the URL path: &lt;code&gt;http://localhost:7071/api/Hello&lt;/code&gt;. If you rename the class but keep the attribute string the same, the URL stays the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;[HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;, &quot;post&quot;)]&lt;/code&gt;&lt;/strong&gt; is where the behavior is defined:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AuthorizationLevel.Function&lt;/code&gt; means callers need a function-specific API key to invoke this endpoint. Other options are &lt;code&gt;Anonymous&lt;/code&gt; (no key required) and &lt;code&gt;Admin&lt;/code&gt; (requires the master host key). When running locally, authorization is bypassed regardless of the level you set.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;get&quot;, &quot;post&quot;&lt;/code&gt; lists the allowed HTTP methods. Requests using other methods receive a 405 Method Not Allowed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;HttpRequest req&lt;/code&gt;&lt;/strong&gt; is the full ASP.NET Core request object. Query strings, headers, body, route values—it's all there. If you've written ASP.NET Core controllers or minimal APIs, this is the same type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;IActionResult&lt;/code&gt;&lt;/strong&gt; is the return type. &lt;code&gt;OkObjectResult&lt;/code&gt; maps to a 200 response. The runtime serializes the object to JSON (or returns it as plain text for strings) and sends it back to the caller. All the familiar result types work: &lt;code&gt;BadRequestResult&lt;/code&gt;, &lt;code&gt;NotFoundResult&lt;/code&gt;, &lt;code&gt;CreatedResult&lt;/code&gt;, and so on.&lt;/p&gt;
&lt;h4 id=&quot;run-it&quot;&gt;Run It&lt;/h4&gt;
&lt;p&gt;Before we modify anything, let's see this function in action. Start Azurite in a separate terminal (or in the background):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx azurite --silent --location /tmp/azurite &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then start the Functions runtime from your project directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;func start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output listing your function's URL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Functions:

        Hello: [GET,POST] http://localhost:7071/api/Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test it in another terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl http://localhost:7071/api/Hello
# → Welcome to Azure Functions!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's a working HTTP endpoint, running locally, no Azure subscription needed. Keep &lt;code&gt;func start&lt;/code&gt; running—we'll build on this function next.&lt;/p&gt;
&lt;h3 id=&quot;http-trigger-deep-dive&quot;&gt;HTTP Trigger Deep Dive&lt;/h3&gt;
&lt;p&gt;With a running function under your belt, let's build three patterns that cover the majority of real-world HTTP function scenarios. After each change, stop &lt;code&gt;func start&lt;/code&gt; with &lt;strong&gt;Ctrl+C&lt;/strong&gt; and restart it to pick up the new code.&lt;/p&gt;
&lt;h4 id=&quot;pattern-1-get-with-query-string&quot;&gt;Pattern 1: GET with Query String&lt;/h4&gt;
&lt;p&gt;The simplest pattern—read a value from the URL and return a response:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class HelloFunction(ILogger&amp;lt;HelloFunction&amp;gt; logger)
{
    [Function(&quot;Hello&quot;)]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;)] HttpRequest req)
    {
        logger.LogInformation(&quot;Hello function triggered&quot;);

        string? name = req.Query[&quot;name&quot;];
        return new OkObjectResult($&quot;Hello, {name ?? &quot;world&quot;}!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;req.Query[&quot;name&quot;]&lt;/code&gt; reads the &lt;code&gt;name&lt;/code&gt; parameter from the query string. If it's missing, the value is &lt;code&gt;null&lt;/code&gt;, so we fall back to &lt;code&gt;&quot;world&quot;&lt;/code&gt; with the null-coalescing operator. Note the &lt;code&gt;string?&lt;/code&gt; — the query collection returns a nullable type, and with &lt;code&gt;&amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;/code&gt; in the project file, the compiler enforces this.&lt;/p&gt;
&lt;p&gt;Test it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl &quot;http://localhost:7071/api/Hello?name=Azure&quot;
# → Hello, Azure!

curl &quot;http://localhost:7071/api/Hello&quot;
# → Hello, world!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This pattern works well for simple lookups—search queries, filtering lists, or any GET request where the parameters are short and cacheable.&lt;/p&gt;
&lt;h4 id=&quot;pattern-2-post-with-json-body&quot;&gt;Pattern 2: POST with JSON Body&lt;/h4&gt;
&lt;p&gt;For creating or updating resources, you'll typically accept a JSON request body. C# records and ASP.NET Core model binding make this clean:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute;

public record CreateOrderRequest(string ProductId, int Quantity);

public class OrderFunction(ILogger&amp;lt;OrderFunction&amp;gt; logger)
{
    [Function(&quot;CreateOrder&quot;)]
    public IActionResult CreateOrder(
        [HttpTrigger(AuthorizationLevel.Function, &quot;post&quot;, Route = &quot;orders&quot;)] HttpRequest req,
        [FromBody] CreateOrderRequest order)
    {
        logger.LogInformation(&quot;Order for {ProductId} x{Quantity}&quot;, order.ProductId, order.Quantity);
        return new CreatedResult($&quot;/orders/{Guid.NewGuid()}&quot;, order);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Several things happening here:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;record CreateOrderRequest&lt;/code&gt;&lt;/strong&gt; defines an immutable data type. Records are ideal for request and response DTOs—they give you value equality, a clean &lt;code&gt;ToString()&lt;/code&gt;, and immutability out of the box. No need to write a class with properties and a constructor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;[FromBody] CreateOrderRequest order&lt;/code&gt;&lt;/strong&gt; tells the ASP.NET Core model binder to deserialize the JSON request body into a &lt;code&gt;CreateOrderRequest&lt;/code&gt; instance. If the JSON doesn't match (missing required fields, wrong types), the runtime returns a 400 Bad Request automatically. One gotcha: with ASP.NET Core integration enabled, &lt;code&gt;FromBody&lt;/code&gt; exists in two namespaces—&lt;code&gt;Microsoft.Azure.Functions.Worker.Http&lt;/code&gt; and &lt;code&gt;Microsoft.AspNetCore.Mvc&lt;/code&gt;. You need the Functions Worker version. The &lt;code&gt;using&lt;/code&gt; alias at the top of the file resolves the ambiguity so you can use &lt;code&gt;[FromBody]&lt;/code&gt; without a fully qualified name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Route = &quot;orders&quot;&lt;/code&gt;&lt;/strong&gt; overrides the default route. Without it, the URL would be &lt;code&gt;/api/CreateOrder&lt;/code&gt; (derived from the function name). With it, the URL becomes &lt;code&gt;/api/orders&lt;/code&gt;—cleaner and more RESTful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;CreatedResult&lt;/code&gt;&lt;/strong&gt; returns HTTP 201 with a &lt;code&gt;Location&lt;/code&gt; header pointing to the new resource. Standard REST semantics.&lt;/p&gt;
&lt;p&gt;Test it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -X POST http://localhost:7071/api/orders \
  -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;productId&quot;:&quot;SKU-001&quot;,&quot;quantity&quot;:3}'
# → {&quot;productId&quot;:&quot;SKU-001&quot;,&quot;quantity&quot;:3}
# (with 201 status and Location header)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Content-Type: application/json&lt;/code&gt; header is required. Without it, model binding won't kick in and you'll get a deserialization error.&lt;/p&gt;
&lt;h4 id=&quot;pattern-3-route-parameters&quot;&gt;Pattern 3: Route Parameters&lt;/h4&gt;
&lt;p&gt;For resource-oriented APIs, you'll want parameters embedded in the URL path rather than the query string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class ProductFunction(ILogger&amp;lt;ProductFunction&amp;gt; logger)
{
    [Function(&quot;GetProduct&quot;)]
    public IActionResult GetProduct(
        [HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;,
            Route = &quot;products/{category:alpha}/{id:int?}&quot;)] HttpRequest req,
        string category, int? id)
    {
        logger.LogInformation(&quot;Looking up {Category}, id={Id}&quot;, category, id);
        return new OkObjectResult(new { category, id });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Route parameters&lt;/strong&gt; are defined in curly braces inside the &lt;code&gt;Route&lt;/code&gt; string and captured as method parameters matched by name. The runtime extracts &lt;code&gt;category&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; from the URL and passes them directly to your method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Route constraints&lt;/strong&gt; validate parameters before your code runs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Constraint &lt;/th&gt;&lt;th&gt;Meaning &lt;/th&gt;&lt;th&gt;Example&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:alpha&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Letters only &lt;/td&gt;&lt;td&gt;&lt;code&gt;electronics&lt;/code&gt; matches, &lt;code&gt;123&lt;/code&gt; doesn't&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:int&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Integer &lt;/td&gt;&lt;td&gt;&lt;code&gt;42&lt;/code&gt; matches, &lt;code&gt;abc&lt;/code&gt; doesn't&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:int?&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Optional integer &lt;/td&gt;&lt;td&gt;Parameter can be omitted&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:guid&lt;/code&gt; &lt;/td&gt;&lt;td&gt;GUID format &lt;/td&gt;&lt;td&gt;&lt;code&gt;{id:guid}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:length(5)&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Exact string length &lt;/td&gt;&lt;td&gt;&lt;code&gt;{code:length(5)}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:min(1)&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Minimum integer value &lt;/td&gt;&lt;td&gt;&lt;code&gt;{page:min(1)}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If a constraint fails, the runtime returns a 404—your function never executes.&lt;/p&gt;
&lt;p&gt;Test it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl http://localhost:7071/api/products/electronics/42
# → {&quot;category&quot;:&quot;electronics&quot;,&quot;id&quot;:42}

curl http://localhost:7071/api/products/electronics
# → {&quot;category&quot;:&quot;electronics&quot;,&quot;id&quot;:null}

curl http://localhost:7071/api/products/123/42
# → 404 (constraint :alpha rejects &quot;123&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have three working patterns that cover most real-world HTTP function scenarios.&lt;/p&gt;
&lt;h3 id=&quot;running-and-testing-locally&quot;&gt;Running and Testing Locally&lt;/h3&gt;
&lt;p&gt;Restart &lt;code&gt;func start&lt;/code&gt; now that all three functions are in place. You should see all of them listed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Azure Functions Core Tools
Core Tools Version: 4.6.0

Functions:

        CreateOrder: [POST] http://localhost:7071/api/orders

        GetProduct: [GET] http://localhost:7071/api/products/{category:alpha}/{id:int?}

        Hello: [GET] http://localhost:7071/api/Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For quick reference, here are all three test commands together:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Pattern 1: GET with query string
curl &quot;http://localhost:7071/api/Hello?name=Azure&quot;

# Pattern 2: POST with JSON body
curl -X POST http://localhost:7071/api/orders \
  -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;productId&quot;:&quot;SKU-001&quot;,&quot;quantity&quot;:3}'

# Pattern 3: Route parameters
curl http://localhost:7071/api/products/electronics/42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use Postman, the REST Client extension for VS Code, or HTTPie—whatever fits your workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A note on authorization:&lt;/strong&gt; when running locally, &lt;code&gt;AuthorizationLevel.Function&lt;/code&gt; is bypassed. You don't need to pass a function key. This makes local development frictionless. Once deployed, clients will need to include the key as a query parameter (&lt;code&gt;?code=&amp;lt;key&amp;gt;&lt;/code&gt;) or in the &lt;code&gt;x-functions-key&lt;/code&gt; header.&lt;/p&gt;
&lt;h3 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h3&gt;
&lt;p&gt;If things don't work on the first try, check these common issues:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;Can't determine project language&quot;&lt;/strong&gt; or &lt;strong&gt;&quot;No job functions found&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.csproj&lt;/code&gt; file is missing required settings. Verify it targets &lt;code&gt;net10.0&lt;/code&gt;, has &lt;code&gt;&amp;lt;AzureFunctionsVersion&amp;gt;v4&amp;lt;/AzureFunctionsVersion&amp;gt;&lt;/code&gt;, and includes the Worker SDK packages. Run &lt;code&gt;dotnet build&lt;/code&gt; separately to check for compilation errors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Port 7071 already in use&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another Functions host (or another process) is using the default port. Either kill it or start on a different port:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;func start --port 7072
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&quot;Value cannot be null: provider&quot; or storage connection errors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Azurite isn't running. The Functions runtime needs a storage emulator even for HTTP triggers. Start Azurite, or verify that &lt;code&gt;local.settings.json&lt;/code&gt; contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;IsEncrypted&quot;: false,
  &quot;Values&quot;: {
    &quot;AzureWebJobsStorage&quot;: &quot;UseDevelopmentStorage=true&quot;,
    &quot;FUNCTIONS_WORKER_RUNTIME&quot;: &quot;dotnet-isolated&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;UseDevelopmentStorage=true&lt;/code&gt; tells the SDK to connect to Azurite on its default ports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JSON deserialization errors on POST requests&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Two common causes: missing the &lt;code&gt;Content-Type: application/json&lt;/code&gt; header, or property name mismatches between your JSON and your C# record. By default, the serializer is case-insensitive, so &lt;code&gt;productId&lt;/code&gt; and &lt;code&gt;ProductId&lt;/code&gt; both work. But the property names must exist on the target type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;The listener for function X was unable to start&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Usually a runtime version mismatch. Confirm &lt;code&gt;func --version&lt;/code&gt; returns 4.x and your &lt;code&gt;.csproj&lt;/code&gt; has &lt;code&gt;&amp;lt;AzureFunctionsVersion&amp;gt;v4&amp;lt;/AzureFunctionsVersion&amp;gt;&lt;/code&gt;. If you recently upgraded the Core Tools, also run &lt;code&gt;dotnet clean&lt;/code&gt; and rebuild.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;We went from an empty directory to a running HTTP API with three production patterns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Query strings&lt;/strong&gt; for simple reads (&lt;code&gt;GET /api/Hello?name=Azure&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON bodies&lt;/strong&gt; for mutations (&lt;code&gt;POST /api/orders&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Route parameters&lt;/strong&gt; for resource-oriented endpoints (&lt;code&gt;GET /api/products/electronics/42&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All running locally, no Azure subscription needed.&lt;/p&gt;
&lt;p&gt;The isolated worker model with ASP.NET Core integration gives you familiar types—&lt;code&gt;HttpRequest&lt;/code&gt;, &lt;code&gt;IActionResult&lt;/code&gt;, &lt;code&gt;[FromBody]&lt;/code&gt;—so writing Azure Functions feels like writing any other .NET web API. The main difference is that Azure handles the hosting, scaling, and infrastructure.&lt;/p&gt;
&lt;p&gt;The code samples in this article are deliberately simple. In a real project, you'd add input validation, error handling, and probably connect to a database or external service. But the patterns are the same—the &lt;code&gt;[HttpTrigger]&lt;/code&gt; attribute, the route configuration, the model binding. Once you understand these building blocks, everything else is regular C#.&lt;/p&gt;
&lt;p&gt;All code from this article is available in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MO2k4/azure-functions-samples&quot;&gt;azure-functions-samples&lt;/a&gt; repository—clone it, run &lt;code&gt;func start&lt;/code&gt;, and experiment.&lt;/p&gt;
&lt;h3 id=&quot;what-s-next-in-this-series&quot;&gt;What's Next in This Series&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Part 3: Beyond HTTP Triggers&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; covers timer triggers for scheduled jobs, queue triggers for message processing, and blob triggers for reacting to file uploads. Same project structure, new event sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 4: Local Development Setup&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; digs into the day-to-day workflow—debugging in VS Code, hot reload, and productivity tips.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 5: Understanding the Isolated Worker Model&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; explains what &quot;isolated&quot; means, why Microsoft created this model, how it differs from the legacy in-process model, and what it means for your code.&lt;/p&gt;
&lt;p&gt;And yes—we'll cover &lt;strong&gt;deploying to Azure with CI/CD via GitHub Actions&lt;/strong&gt; later in the series, so everything you're building locally will make it to the cloud.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/11565/your-first-azure-function-http-triggers-step-by-step</guid>
<pubDate>Fri, 13 Feb 2026 07:31:47 +0000</pubDate>
</item>
<item>
<title>Why Azure Functions? Serverless for .NET Developers</title>
<link>https://coderlegion.com/11426/why-azure-functions-serverless-for-net-developers</link>
<description>&lt;p&gt;You've just finished building a webhook handler. Maybe fifty lines of code—parse some JSON, hit a database, return a response. Now comes the fun part: provisioning a VM, configuring IIS, setting up SSL certificates, writing health checks, and figuring out how to scale when traffic spikes at 3 AM. Sound familiar?&lt;/p&gt;
&lt;p&gt;This is the problem serverless computing solves. Instead of managing infrastructure, you write the code that matters—your business logic—and let the platform handle everything else. In 2026, with cloud costs under constant scrutiny, the pay-per-execution model makes even more sense: why pay for idle servers when you can pay only for the milliseconds your code actually runs?&lt;/p&gt;
&lt;p&gt;For .NET developers, Azure Functions has matured into a first-class serverless platform. The isolated worker model, combined with .NET 10's performance improvements, means you get the full power of modern .NET without compromise.&lt;/p&gt;
&lt;p&gt;Every major cloud provider offers a serverless compute platform—AWS has Lambda, Google Cloud has Cloud Functions. For .NET developers, Azure Functions is the most natural choice: first-class support in the .NET SDK, seamless integration with Visual Studio and Rider, and deep ties to the broader Azure ecosystem you're likely already using. If your team runs on .NET, you won't fight the tooling here.&lt;/p&gt;
&lt;p&gt;In this series, we'll explore Azure Functions from the ground up—when to use them, how they work, and the patterns that make serverless development practical. Let's start with the fundamentals.&lt;/p&gt;
&lt;h3 id=&quot;what-azure-functions-actually-is&quot;&gt;What Azure Functions Actually Is&lt;/h3&gt;
&lt;p&gt;Azure Functions is Microsoft's event-driven serverless compute service. Instead of managing servers or worrying about infrastructure, you write small, focused pieces of code that run in response to events. Azure handles scaling, patching, and availability automatically.&lt;/p&gt;
&lt;h4 id=&quot;triggers-what-starts-your-code&quot;&gt;Triggers: What Starts Your Code&lt;/h4&gt;
&lt;p&gt;A trigger defines what event causes your function to execute. Common triggers include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HTTP Trigger&lt;/strong&gt; - Respond to web requests (APIs, webhooks)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timer Trigger&lt;/strong&gt; - Run on a schedule (cron expressions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Bus Trigger&lt;/strong&gt; - Process messages from queues or topics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cosmos DB Trigger&lt;/strong&gt; - React to document changes in your database&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blob Trigger&lt;/strong&gt; - Execute when files are uploaded to storage&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;bindings-declaring-inputs-and-outputs&quot;&gt;Bindings: Declaring Inputs and Outputs&lt;/h4&gt;
&lt;p&gt;Bindings connect your function to other Azure services declaratively. Instead of writing SDK code to read from a queue or write to a database, you declare the connection through attributes—and Azure Functions handles the plumbing.&lt;/p&gt;
&lt;p&gt;There are two types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Input bindings&lt;/strong&gt; - Read data when your function executes (e.g., fetch a document from Cosmos DB, read a blob from storage)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output bindings&lt;/strong&gt; - Write data after your function completes (e.g., add a message to a queue, insert a row into Table Storage)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your function's return value can serve as an output binding directly. Return an object, and Azure Functions writes it to the configured destination—no explicit SDK calls needed.&lt;/p&gt;
&lt;h4 id=&quot;your-first-function&quot;&gt;Your First Function&lt;/h4&gt;
&lt;p&gt;Here's a minimal HTTP-triggered function using the .NET 10 isolated worker model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class HelloFunction(ILogger&amp;lt;HelloFunction&amp;gt; logger)
{
    [Function(&quot;Hello&quot;)]
    public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, &quot;get&quot;)] HttpRequest req)
    {
        logger.LogInformation(&quot;Processing request&quot;);
        return new OkObjectResult(&quot;Hello from Azure Functions!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[HttpTrigger]&lt;/code&gt; attribute defines this as an HTTP GET endpoint with function-level authentication (we'll cover authentication levels in the next article). The &lt;code&gt;OkObjectResult&lt;/code&gt; you return becomes the HTTP response sent back to the caller—Azure Functions maps ASP.NET Core's familiar result types to actual HTTP responses for you. That's all you need to deploy a working API endpoint.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use-azure-functions-and-when-not-to-&quot;&gt;When to Use Azure Functions (and When Not To)&lt;/h3&gt;
&lt;p&gt;Azure Functions excels in specific scenarios, but it's not a universal solution. Here's an honest breakdown to help you choose wisely.&lt;/p&gt;
&lt;h4 id=&quot;where-functions-shine&quot;&gt;Where Functions Shine&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;APIs with unpredictable traffic.&lt;/strong&gt; If your endpoint gets 100 requests one hour and 10,000 the next, you pay only for what you use. No idle VMs burning budget overnight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event-driven workloads.&lt;/strong&gt; Processing messages from Service Bus, reacting to blob uploads, or handling Event Grid notifications—Functions integrates natively with Azure's eventing ecosystem. A few lines of code and you're processing events at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scheduled jobs.&lt;/strong&gt; Timer triggers replace fragile cron servers. Need to clean up stale data every night or send weekly reports? Functions handles the scheduling infrastructure for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Glue code between services.&lt;/strong&gt; Moving data from one system to another, transforming payloads, or orchestrating simple workflows—Functions keeps these small integrations from becoming maintenance burdens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prototypes and MVPs.&lt;/strong&gt; When validating an idea quickly, Functions lets you ship without configuring infrastructure. You can always migrate to containers later.&lt;/p&gt;
&lt;h4 id=&quot;when-to-look-elsewhere&quot;&gt;When to Look Elsewhere&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Latency-critical paths.&lt;/strong&gt; Cold starts on the Consumption plan can add seconds of delay. If you need consistent sub-10ms response times, consider App Service or Container Apps with always-on instances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Long-running processes.&lt;/strong&gt; The 10-minute timeout (or 230 seconds on HTTP triggers) won't work for heavy batch processing. Look at Durable Functions for orchestration or Container Apps Jobs for extended compute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Heavy, sustained compute.&lt;/strong&gt; If you're running CPU-intensive workloads continuously, reserved container instances often cost less than millions of function executions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stateful applications.&lt;/strong&gt; WebSocket connections, in-memory caches, or persistent sessions don't fit the ephemeral Functions model.&lt;/p&gt;
&lt;h4 id=&quot;a-note-on-cost-protection&quot;&gt;A Note on Cost Protection&lt;/h4&gt;
&lt;p&gt;Pay-per-execution pricing has a flip side: you pay for &lt;em&gt;every&lt;/em&gt; request, including malicious ones. A DDoS attack against an unprotected Function endpoint means you're footing the bill for traffic you never wanted.&lt;/p&gt;
&lt;p&gt;For public-facing APIs, consider these safeguards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Azure API Management&lt;/strong&gt; — rate limiting, throttling, and request quotas&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Front Door or Application Gateway&lt;/strong&gt; — WAF rules and DDoS protection&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function authorization levels&lt;/strong&gt; — &lt;code&gt;AuthorizationLevel.Function&lt;/code&gt; or &lt;code&gt;AuthorizationLevel.Admin&lt;/code&gt; require keys, blocking anonymous traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Consumption plan's cost model works best when you control who can call your endpoints. For truly public APIs expecting untrusted traffic, budget for a protection layer—or consider the Premium plan's predictable pricing.&lt;/p&gt;
&lt;h3 id=&quot;quick-comparison-functions-vs-app-service-vs-container-apps&quot;&gt;Quick Comparison: Functions vs App Service vs Container Apps&lt;/h3&gt;
&lt;p&gt;Choosing the right Azure compute option can significantly impact your costs and application architecture. Here's how Azure Functions stacks up against the other popular choices for .NET workloads:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Feature &lt;/th&gt;&lt;th&gt;Azure Functions &lt;/th&gt;&lt;th&gt;App Service &lt;/th&gt;&lt;th&gt;Container Apps&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Scale trigger &lt;/td&gt;&lt;td&gt;Events (HTTP, queue, timer) &lt;/td&gt;&lt;td&gt;CPU/memory rules &lt;/td&gt;&lt;td&gt;Events (KEDA), CPU/memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pricing &lt;/td&gt;&lt;td&gt;Pay-per-execution* &lt;/td&gt;&lt;td&gt;Fixed/reserved &lt;/td&gt;&lt;td&gt;Per-resource (vCPU/memory)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cold starts &lt;/td&gt;&lt;td&gt;Yes (Consumption) &lt;/td&gt;&lt;td&gt;No &lt;/td&gt;&lt;td&gt;Configurable&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scale-out limit &lt;/td&gt;&lt;td&gt;200 (Consumption) / 1,000 (Flex) &lt;/td&gt;&lt;td&gt;Up to 30 instances &lt;/td&gt;&lt;td&gt;300–1,000 replicas&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Best for &lt;/td&gt;&lt;td&gt;Event-driven, APIs &lt;/td&gt;&lt;td&gt;Web apps, APIs &lt;/td&gt;&lt;td&gt;Microservices&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;\&lt;/em&gt;Consumption plan. Premium and Dedicated plans use fixed or memory/CPU-based pricing, similar to Container Apps.*&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choose Azure Functions&lt;/strong&gt; when you have event-driven workloads, sporadic traffic patterns, or want to minimize costs for low-volume APIs. &lt;strong&gt;Opt for App Service&lt;/strong&gt; when you need predictable performance without cold starts for traditional web applications. &lt;strong&gt;Consider Container Apps&lt;/strong&gt; when building microservices architectures that require more control over the runtime environment while still benefiting from managed scaling.&lt;/p&gt;
&lt;h3 id=&quot;a-note-on-net-10&quot;&gt;A Note on .NET 10&lt;/h3&gt;
&lt;p&gt;Throughout this series, we'll use .NET 10 with the isolated worker model—the recommended stack for new Azure Functions projects. It's an LTS release (supported through November 2028) and gives you full ASP.NET Core integration with familiar types like &lt;code&gt;HttpRequest&lt;/code&gt; and &lt;code&gt;IActionResult&lt;/code&gt;. We'll cover the setup in Part 2 and what &quot;isolated worker model&quot; means in detail in Part 5.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-s-next-in-this-series-1&quot;&gt;What's Next in This Series&lt;/h3&gt;
&lt;p&gt;This article covered the &quot;why&quot; of Azure Functions—now it's time for the &quot;how.&quot; Over the next several parts, we'll build real-world skills you can apply immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 2: &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/11565/your-first-azure-function-http-triggers-step-by-step&quot;&gt;Your First Azure Function — HTTP Triggers Step-by-Step&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; walks through project creation, running locally, and testing with curl. You'll have a working HTTP endpoint in minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 3: Beyond HTTP Triggers&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; explores timer, queue, and blob triggers—the event-driven patterns that make Functions powerful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 4: Local Development Setup&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; covers the tools, debugging techniques, and productivity tips that make daily development smooth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 5: Understanding the Isolated Worker Model&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; explains the architecture behind modern Azure Functions—why Microsoft created it, how it works, and what it means for your code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Part 6: Production Realities — When Serverless Stops Being Serverless&lt;/strong&gt; &lt;em&gt;(coming soon)&lt;/em&gt; covers what actually happens when enterprise requirements like VNet integration and private endpoints push you off the Consumption plan—and when to consider alternatives.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Azure Functions has become a practical choice for .NET developers who want to ship features without managing infrastructure. The isolated worker model with .NET 10 gives you modern language features, familiar patterns, and genuine pay-per-use economics.&lt;/p&gt;
&lt;p&gt;Start small. Create an HTTP-triggered function, deploy it to Azure, and see how quickly you can go from code to production. The barrier to entry has never been lower.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Follow this series&lt;/strong&gt; to build your serverless skills step by step. Next up: setting up your development environment and deploying your first function. Let's build something.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11426/why-azure-functions-serverless-for-net-developers</guid>
<pubDate>Wed, 11 Feb 2026 07:29:29 +0000</pubDate>
</item>
</channel>
</rss>