Azure Functions just went through its biggest hosting change in years. Microsoft now steers every new serverless app toward the Flex Consumption plan instead of the original Consumption plan, and the older Functions runtime versions are being retired on a hard schedule: version 1.x support ended September 14, 2026, and the legacy Linux Consumption plan itself is on a path to full retirement by September 30, 2028, according to Microsoft’s own runtime version comparison page. If you built a function app a couple of years ago and haven’t touched it since, there’s a decent chance it’s running on infrastructure Microsoft is actively walking away from. This tutorial builds a secure, production-shaped Order Status API from scratch on the current Flex Consumption plan: an HTTP-triggered endpoint, a timer-triggered cleanup job, a Durable Functions orchestration for a multi-step checkout flow, Microsoft Entra ID authentication on every route, secrets pulled from Key Vault through a managed identity instead of sitting in plaintext, and a GitHub Actions pipeline that deploys on every push to main. You’ll need about 75 minutes and a terminal. No prior Azure experience required, but comfort with the command line helps.

What Changed in Azure Functions for 2026

Three shifts matter if you’re setting up Azure Functions today instead of relying on an old bookmark or a tutorial written two years ago. First, Functions runtime 4.x is now the only version Microsoft fully supports across every language. Runtime 1.x stopped getting support on September 14, 2026, and versions 2.x and 3.x are being phased out on their own retirement timelines, per Microsoft’s runtime versions comparison. If your app targets anything other than 4.x, migrating the host version comes before anything else in this guide.

Second, and the bigger practical shift, Flex Consumption has replaced the original Consumption plan as Microsoft’s recommended default for new serverless function apps. Flex Consumption keeps the pay-as-you-go billing model people liked about Consumption, but adds configurable instance sizes, virtual network integration without the Premium plan’s always-on cost, and faster horizontal scaling. Function apps still running on the older Linux Consumption plan with the end-of-life v3 runtime stopped running after September 30, 2026, which means this isn’t a someday migration for a lot of teams. It’s already overdue.

Third, language support has widened in ways that change which workloads make sense on Functions at all. Go now runs on Azure Functions, but only on the Flex Consumption plan, not on Consumption or Premium. Python 3.13 is supported across Flex Consumption, Premium, and Dedicated plans. None of this changes what Azure Functions fundamentally is: an event-driven serverless compute service where you write a handler and Azure manages the infrastructure around it. What’s changed is that the infrastructure itself has a new default, and building on the old default is now building on borrowed time. That’s the version of Functions this tutorial uses throughout.

Prerequisites

Install and verify each of these before Step 1. Most stalled Azure Functions tutorials fail here, not in the actual function code.

  • An Azure subscription with billing enabled (the free tier’s monthly grants cover everything built in this tutorial)
  • Azure CLI, latest version, authenticated against your subscription
  • Azure Functions Core Tools v4 (the only runtime version with full support)
  • Node.js 20.x LTS and npm, for the function code in this tutorial
  • Azure Developer CLI (azd), latest version, used in the GitHub Actions step
  • A terminal with curl or a tool like Postman for testing HTTP endpoints
  • Basic familiarity with JSON and command-line tools
  • About 75 minutes of uninterrupted time

Confirm your tooling before moving on:

az --version
# azure-cli 2.x.x

az account show
# {
#   "id": "00000000-0000-0000-0000-000000000000",
#   "name": "Your Subscription Name",
#   "user": { "name": "[email protected]", "type": "user" }
# }

func --version
# 4.x.x

node --version
# v20.x.x

If az account show returns an error instead of your subscription details, run az login first. Nothing in the steps below will work without an authenticated session.

Step 1: Create the Resource Group and Storage Backend

Every Azure Functions app needs a resource group to live in and a storage account for its internal state, trigger bookkeeping, and deployment artifacts. Create both together so you can delete the whole project in one command later.

az group create \
  --name rg-order-status-api \
  --location eastus2

az storage account create \
  --name stgorderstatusapi \
  --resource-group rg-order-status-api \
  --location eastus2 \
  --sku Standard_LRS

Storage account names must be globally unique and lowercase alphanumeric only, so if stgorderstatusapi is taken, append a few random digits. Standard_LRS (locally redundant storage) is the cheapest replication tier and is fine for a tutorial or dev environment. Switch to zone-redundant or geo-redundant storage if you take this into production and need the app to survive a datacenter-level outage.

Step 2: Scaffold the Function App Project Locally

With Core Tools installed, initialize a new project using the Node.js v4 programming model, which uses a single flat file structure instead of the older folder-per-function layout.

func init order-status-api --javascript --model V4
cd order-status-api
npm install @azure/functions

The V4 model means you no longer edit a separate function.json bindings file for every trigger. Instead, each function registers itself in code by calling app.http(), app.timer(), or similar, which keeps your trigger configuration next to the logic that handles it. This is the model Microsoft now points new projects toward, and it’s what the rest of this tutorial uses.

Step 3: Write Your First HTTP-Triggered Function

Create src/functions/getOrderStatus.js with a route that takes an order ID and returns its status. Keep authLevel set to anonymous for now. You’ll lock this down properly with Entra ID in Step 8, and testing an authenticated endpoint before it exists just adds friction.

const { app } = require('@azure/functions');

app.http('getOrderStatus', {
    methods: ['GET'],
    authLevel: 'anonymous',
    route: 'orders/{orderId}/status',
    handler: async (request, context) => {
        const orderId = request.params.orderId;
        context.log(`Checking status for order ${orderId}`);

        return {
            status: 200,
            jsonBody: {
                orderId,
                status: 'processing',
                checkedAt: new Date().toISOString()
            }
        };
    }
});

Run it locally and confirm it responds before deploying anything:

func start

# In a second terminal:
curl http://localhost:7071/api/orders/A1001/status
# {"orderId":"A1001","status":"processing","checkedAt":"2026-10-17T14:02:11.000Z"}

Step 4: Add a Timer-Triggered Background Function

Not every function responds to a request. Add a timer trigger that runs on a schedule to clear out orders stuck in a stale state, the kind of job that used to require a cron server somewhere.

const { app } = require('@azure/functions');

app.timer('cleanupStaleOrders', {
    schedule: '0 */15 * * * *',
    handler: async (myTimer, context) => {
        context.log('Timer triggered: scanning for stale orders');
        // Query and update stale order records here
        context.log('Cleanup pass complete');
    }
});

The schedule string uses the same six-field NCRONTAB expression Azure Functions has always used for timer triggers: seconds, minutes, hours, day of month, month, day of week. 0 */15 * * * * fires every 15 minutes on the minute. Test it locally the same way as the HTTP function, by running func start and watching the console for the log line on each tick.

Step 5: Create the Flex Consumption Function App in Azure

With the local project working, provision the actual Azure resource. This is where Flex Consumption differs from the legacy Consumption plan at the command level: you pass a --flexconsumption-location flag instead of the older app service plan parameters.

az functionapp create \
  --resource-group rg-order-status-api \
  --name order-status-api-func \
  --storage-account stgorderstatusapi \
  --flexconsumption-location eastus2 \
  --runtime node \
  --runtime-version 20

Function app names are globally unique across all of Azure, so expect to adjust order-status-api-func if it’s taken. Flex Consumption currently offers instance sizes of 512 MB, 2,048 MB, and 4,096 MB, and a single app can scale out to as many as 1,000 instances, according to Microsoft’s Functions scale and hosting documentation. The default instance size on creation is usually fine for a tutorial, and you’ll revisit sizing deliberately in Step 12. Double-check the exact accepted --runtime-version string against the current language support matrix before you run this against a production subscription, since Azure occasionally adjusts which minor versions a given plan accepts.

Step 6: Deploy Your Code

Core Tools handles packaging and pushing your code to the Flex Consumption app in a single command.

func azure functionapp publish order-status-api-func

# Expected output (trimmed):
# Getting site publishing info...
# Uploading package...
# Upload completed successfully.
# Deployment completed successfully.
# Functions in order-status-api-func:
#     getOrderStatus - [httpTrigger]
#     cleanupStaleOrders - [timerTrigger]

Grab the live HTTP endpoint from that output, or pull it directly with the CLI, and confirm the deployed function responds the same way the local one did:

az functionapp function show \
  --resource-group rg-order-status-api \
  --name order-status-api-func \
  --function-name getOrderStatus \
  --query "invokeUrlTemplate" -o tsv

curl https://order-status-api-func.azurewebsites.net/api/orders/A1001/status

Step 7: Lock Secrets Behind Managed Identity and Key Vault

Plaintext connection strings in application settings are the single most common thing that turns a demo into an incident. Assign the function app a system-managed identity, create a Key Vault, and let the app pull secrets through that identity instead of storing them directly.

az functionapp identity assign \
  --name order-status-api-func \
  --resource-group rg-order-status-api

az keyvault create \
  --name kv-order-status-api \
  --resource-group rg-order-status-api \
  --location eastus2

PRINCIPAL_ID=$(az functionapp identity show \
  --name order-status-api-func \
  --resource-group rg-order-status-api \
  --query principalId -o tsv)

az keyvault set-policy \
  --name kv-order-status-api \
  --object-id $PRINCIPAL_ID \
  --secret-permissions get list

az keyvault secret set \
  --vault-name kv-order-status-api \
  --name OrdersDbConnectionString \
  --value ""

az functionapp config appsettings set \
  --name order-status-api-func \
  --resource-group rg-order-status-api \
  --settings OrdersDbConnectionString="@Microsoft.KeyVault(SecretUri=https://kv-order-status-api.vault.azure.net/secrets/OrdersDbConnectionString/)"

That last command is the part people skip. The @Microsoft.KeyVault(SecretUri=...) syntax tells Azure Functions to resolve the actual secret value from Key Vault at runtime using the function app’s managed identity, rather than storing the raw value in application settings where anyone with portal read access could see it. Microsoft’s security guidance for Functions specifically calls out managed identity plus Key Vault references as the replacement for hardcoded secrets, and it costs nothing extra beyond the Key Vault itself.

Step 8: Require Microsoft Entra ID on Every HTTP Endpoint

Anonymous access was fine for local testing. It is not fine for a production order status API. Microsoft’s own security concepts documentation for Azure Functions recommends App Service Authentication and Authorization, commonly called Microsoft Entra authentication (Easy Auth), for protecting HTTP-triggered endpoints, with Azure API Management as an alternative when you also need request mediation and routing in front of the function.

az functionapp auth microsoft update \
  --name order-status-api-func \
  --resource-group rg-order-status-api \
  --client-id  \
  --client-secret  \
  --issuer https://login.microsoftonline.com//v2.0

az functionapp auth update \
  --name order-status-api-func \
  --resource-group rg-order-status-api \
  --enabled true \
  --action LoginWithAzureActiveDirectory

You’ll need an app registration in Microsoft Entra ID first, which gives you the client ID, client secret, and tenant ID those commands expect. Once this is applied, every request to your function app’s endpoints needs a valid Entra ID token in the Authorization header, and unauthenticated requests get redirected or rejected before they ever reach your handler code. Enforce HTTPS alongside this, since Functions security guidance treats HTTPS-only traffic and protected function keys as baseline controls, not optional hardening.

Step 9: Add a Durable Functions Orchestration

A real order flow is rarely a single operation. It validates, charges a payment, and schedules a shipment, and each of those steps can fail independently and needs to retry without losing track of what already succeeded. That’s exactly what Durable Functions is for: it checkpoints orchestration state so a crash or a transient failure doesn’t force you to restart the whole workflow from scratch.

npm install durable-functions

const { app } = require('@azure/functions');
const df = require('durable-functions');

df.app.orchestration('processOrderOrchestrator', function* (context) {
    const order = context.df.getInput();
    const validated = yield context.df.callActivity('validateOrder', order);
    const charged = yield context.df.callActivity('chargePayment', validated);
    const shipped = yield context.df.callActivity('scheduleShipment', charged);
    return shipped;
});

df.app.activity('validateOrder', {
    handler: async (order) => ({ ...order, validated: true })
});

df.app.activity('chargePayment', {
    handler: async (order) => ({ ...order, charged: true })
});

df.app.activity('scheduleShipment', {
    handler: async (order) => ({ ...order, shipped: true })
});

app.http('startOrderProcessing', {
    methods: ['POST'],
    authLevel: 'function',
    route: 'orders/process',
    extraInputs: [df.input.durableClient()],
    handler: async (request, context) => {
        const client = df.getClient(context);
        const body = await request.json();
        const instanceId = await client.startNew('processOrderOrchestrator', { input: body });
        context.log(`Started orchestration with ID = ${instanceId}`);
        return client.createCheckStatusResponse(request, instanceId);
    }
});

Deploy this alongside your existing functions and start an orchestration to see the checkpointing behavior directly:

curl -X POST https://order-status-api-func.azurewebsites.net/api/orders/process \
  -H "Content-Type: application/json" \
  -d '{"orderId": "A1001", "amount": 42.50}'

# {"id":"...","statusQueryGetUri":"https://order-status-api-func.azurewebsites.net/runtime/webhooks/durabletask/instances/...","..."}

Poll the statusQueryGetUri from that response and you’ll see the orchestration move through Running to Completed, with each activity’s output preserved along the way, which is the checkpointing in action.

Step 10: Wire Up Application Insights Monitoring

Microsoft’s own documentation names Application Insights as the recommended way to monitor Durable Functions. The Durable extension emits tracking events for every orchestration and activity, and those events are queryable through Application Insights Analytics once the connection is wired up, which turns an opaque multi-step workflow into something you can actually trace end to end.

az monitor app-insights component create \
  --app order-status-api-insights \
  --location eastus2 \
  --resource-group rg-order-status-api

INSTRUMENTATION_KEY=$(az monitor app-insights component show \
  --app order-status-api-insights \
  --resource-group rg-order-status-api \
  --query connectionString -o tsv)

az functionapp config appsettings set \
  --name order-status-api-func \
  --resource-group rg-order-status-api \
  --settings APPLICATIONINSIGHTS_CONNECTION_STRING="$INSTRUMENTATION_KEY"

Once that setting lands, every context.log() call in your functions, every orchestration tracking event from Step 9, and every HTTP request to getOrderStatus shows up in the Application Insights Logs blade, searchable with a Kusto query. This is also where you’ll spot cold starts, failed activities, and the timer function’s 15-minute cadence if it ever stops firing on schedule.

Step 11: Automate Releases with GitHub Actions

Hand-running func azure functionapp publish every time you change a line of code doesn’t scale past the first week. Add a GitHub Actions workflow that deploys automatically on every push to main.

# .github/workflows/deploy.yml
name: Deploy Azure Function
on:
  push:
    branches: [main]

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

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Deploy to Azure Functions
        uses: azure/functions-action@v1
        with:
          app-name: order-status-api-func
          package: '.'
          publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}

Download the publish profile from the Azure portal (or with az functionapp deployment list-publishing-profiles) and store it as the AZURE_FUNCTIONAPP_PUBLISH_PROFILE secret in your GitHub repository settings. If you’d rather provision infrastructure and deploy in one motion, the Azure Developer CLI (azd) wraps both the resource creation from Steps 1 and 5 and the deployment from Step 6 into a single azd up command driven by an azure.yaml manifest, which is worth adopting once you’re managing more than one function app.

Step 12: Load-Test and Tune Instance Memory

Flex Consumption bills by provisioned memory while an instance is actively executing a function, measured in GB-seconds, plus a per-execution count once you exceed the monthly free grant, according to Microsoft’s Flex Consumption plan documentation. The minimum billable execution period is 1,000 milliseconds, so a function that runs for 50ms still gets billed for a full second. Before you call this project done, send it some real traffic and watch how it behaves under load.

# A simple burst test with curl in a loop; swap in a proper load
# testing tool like k6 or Apache Bench for anything more rigorous
for i in $(seq 1 50); do
  curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \
    https://order-status-api-func.azurewebsites.net/api/orders/A1001/status &
done
wait

Watch the response times and status codes in that output, then cross-reference them against the Application Insights traces from Step 10. If a memory-hungry handler is consistently slow at the 512 MB instance size, redeploy at 2,048 MB or 4,096 MB (the two larger Flex Consumption sizes) rather than assuming the code itself needs a rewrite. Check the current Azure CLI reference for the exact flag that sets instance memory at creation time in your CLI version, since Azure has been iterating on Flex Consumption’s configuration surface since launch, and prefer the Azure portal’s Flex Consumption blade if you want to confirm a setting visually before committing to a CLI flag.

Your Complete Working Project

At the end of these 12 steps, you have a function app named order-status-api-func running on Flex Consumption in the eastus2 region, backed by the following pieces:

  • getOrderStatus, an HTTP-triggered function protected by Microsoft Entra ID, returning order status by ID
  • cleanupStaleOrders, a timer-triggered function running every 15 minutes
  • processOrderOrchestrator plus three activity functions, a Durable Functions workflow that validates, charges, and ships an order with checkpointed state
  • startOrderProcessing, the HTTP endpoint that kicks off the orchestration
  • A Key Vault holding the database connection string, accessed only through the function app’s managed identity
  • An Application Insights resource capturing logs, traces, and Durable Functions tracking events
  • A GitHub Actions workflow that redeploys on every push to main

That’s a genuinely production-shaped skeleton, not a toy that stops at “hello world.” Swap the in-memory activity functions for real database and payment-provider calls, and the architecture underneath doesn’t need to change.

Flex Consumption vs Consumption vs Premium Pricing

Picking the right hosting plan matters more than almost any other decision in this tutorial, since it determines both your bill and your scaling ceiling. The table below reflects the current plan-level mechanics documented by Microsoft. Always confirm exact regional rates on the live Azure pricing page before budgeting, since published figures change independently of this article.

PlanBilling ModelInstance SizesMax Scale-OutVNet Integration
Flex ConsumptionPay-as-you-go: GB-seconds while executing, plus per-execution count512 MB, 2,048 MB, 4,096 MBUp to 1,000 instancesYes, included
Consumption (legacy)Executions, execution time, and memory usedFixed, not configurableVaries by regionNo
PremiumCapacity-based, pre-warmed instancesConfigurable vCPU/memory tiersHigher ceiling than ConsumptionYes, included
Dedicated / App Service planFlat rate for reserved App Service capacityMatches the underlying App Service planManual scale rulesYes, included

Legacy Consumption still works today, but it’s the plan Microsoft is actively steering teams away from, and Linux Consumption apps stuck on the end-of-life v3 runtime already stopped running after September 30, 2026. If you’re starting a new project, Flex Consumption is the plan this tutorial deploys to and the one Microsoft currently recommends by default. Premium remains the right call when you need instances kept warm around the clock to eliminate cold starts entirely, which Flex Consumption’s on-demand model doesn’t guarantee the same way.

Azure Functions vs AWS Lambda vs Google Cloud Run

Teams evaluating serverless compute rarely pick Azure Functions in isolation. Here’s how the Flex Consumption model built in this tutorial compares structurally to AWS Lambda and Google Cloud Run, focused on architecture rather than price, since current cross-provider pricing and cold-start benchmarks vary by region, memory configuration, and measurement method and shouldn’t be reduced to a single number without a controlled, same-conditions test.

CapabilityAzure Functions (Flex Consumption)AWS LambdaGoogle Cloud Run
Deployment unitFunction app with multiple triggers in one codebaseIndividual function per deployment packageContainer image, can host multiple routes
Durable, checkpointed workflowsDurable Functions, built inStep Functions, a separate serviceRequires an external orchestrator
Max scale-out (this tutorial’s plan)Up to 1,000 instancesAccount/region concurrency poolConfigurable per service
Native identity-based secrets accessManaged identity plus Key Vault referencesIAM roles plus Secrets ManagerService accounts plus Secret Manager
Timer-based schedulingBuilt-in timer trigger with NCRONTABEventBridge Scheduler, a separate serviceCloud Scheduler, a separate service

The practical takeaway: Azure Functions keeps more of the workflow (timers, durable orchestration, identity-based secrets) inside one platform instead of composing several separate services the way AWS and Google Cloud often require. That’s a genuine convenience if you’re staying inside the Azure ecosystem, and a reason some teams specifically pick Functions for exactly this kind of multi-step order-processing workload.

Common Pitfalls When Deploying Azure Functions

These are the mistakes that derail most first attempts at a secure Azure Functions deployment, based on the steps above:

  • Deploying to the legacy Consumption plan out of habit. Older tutorials and cached muscle memory default here. Use --flexconsumption-location in Step 5, not the older plan parameters, unless you have a specific reason to stay on Consumption.
  • Leaving authLevel set to anonymous in production. It’s the right setting for local testing in Step 3, and the wrong one the moment this ships. Step 8’s Entra ID configuration has to actually get applied, not just written down as a TODO.
  • Storing the database connection string as a plain application setting. If you skip Step 7’s Key Vault reference and paste a raw connection string into app settings instead, anyone with portal read access to the function app can see it in plaintext.
  • Forgetting the function app needs its managed identity assigned before the Key Vault policy makes sense. Run az functionapp identity assign before az keyvault set-policy, not after. The policy command needs a principal ID that doesn’t exist until the identity is assigned.
  • Treating the timer trigger’s cron syntax as five fields. Azure Functions NCRONTAB expressions use six fields, starting with seconds. A five-field cron string copied from a Linux crontab will either fail to parse or fire at the wrong time.
  • Assuming Durable Functions state survives without Application Insights wired up. The orchestration state itself persists regardless, but without Step 10’s monitoring in place you’re debugging a multi-step workflow blind, with no tracking events to query when an activity fails silently.
  • Hardcoding the instance memory size and never revisiting it. Shipping at the smallest Flex Consumption size and never running Step 12’s load test means you find out about a memory bottleneck from angry users instead of from a controlled test.

Troubleshooting Guide

Work through these when a step doesn’t behave the way this tutorial describes.

  • “Storage account name already taken”: Storage account names are globally unique across all of Azure, not just your subscription. Append a few random digits and retry Step 1.
  • “func: command not found”: Azure Functions Core Tools isn’t installed or isn’t on your PATH. Reinstall it and confirm with func --version before continuing.
  • func azure functionapp publish hangs or times out: Check that the function app name in the command exactly matches the one created in Step 5, and confirm your Azure CLI session hasn’t expired with az account show.
  • HTTP function returns 401 unexpectedly during local testing: Double check authLevel is still set to anonymous in your local code. If you already applied Step 8’s Entra ID settings to the deployed app, that’s expected behavior there, not a bug.
  • Key Vault reference shows as #REF! in application settings: The function app’s managed identity doesn’t have get and list secret permissions on the vault, or the secret URI in the reference has a typo. Re-run the az keyvault set-policy command from Step 7.
  • Timer trigger never fires: Confirm the NCRONTAB string has exactly six space-separated fields. A common mistake is pasting a standard five-field cron expression and leaving off the leading seconds field.
  • Durable orchestration instance stuck in “Running” forever: Check Application Insights for a failed activity function. An unhandled exception inside an activity can leave the orchestration waiting on a result that will never arrive.
  • GitHub Actions deployment fails with an authentication error: The publish profile secret stored in your repository settings has likely expired or doesn’t match the current function app. Download a fresh one with az functionapp deployment list-publishing-profiles and update the GitHub secret.
  • Entra ID login redirects to an error page instead of your app: The redirect URI configured on your Entra ID app registration doesn’t match the function app’s actual URL. Confirm they’re identical, including the https:// prefix and trailing path.

Advanced Tips for Production Workloads

Once the tutorial version works end to end, a handful of changes separate a demo from something you’d actually run with real customer traffic. Move from the system-assigned managed identity used in Step 7 to a user-assigned managed identity once you have more than one function app sharing the same Key Vault, since a user-assigned identity can be provisioned independently of any single app’s lifecycle and reused across several resources without recreating the Key Vault access policy each time.

For the Durable Functions layer, add explicit retry policies to each callActivity call instead of relying on the default behavior, since a transient failure in the payment-charging activity deserves a different retry strategy than a validation failure that will never succeed no matter how many times you retry it. Also consider private endpoints: Microsoft’s current security guidance confirms private endpoint support for Function Apps on Flex Consumption, Elastic Premium, and Dedicated or App Service plans, which lets you keep the entire function app off the public internet except for whatever gateway you explicitly expose.

Finally, treat Application Insights as more than a log viewer. Set up alert rules on failed orchestration instances and on the timer function’s execution count dropping to zero, since a silently stopped cleanup job is the kind of bug that doesn’t announce itself until stale data has piled up for weeks. Both alert types cost very little relative to the operational pain they prevent.

Frequently Asked Questions

Is Azure Functions free to use for this tutorial?
The free tier’s monthly grants for Functions, storage, and Key Vault operations comfortably cover everything built in this walkthrough. Application Insights has its own free data ingestion allowance, which is also enough for development-scale traffic.

Should I use Flex Consumption or the legacy Consumption plan for a new project?
Flex Consumption. Microsoft’s current documentation recommends it for new serverless function apps, and the legacy Consumption plan is on a retirement path, with Linux Consumption apps stuck on the end-of-life v3 runtime already shut off after September 30, 2026.

Do I need Durable Functions for a simple API?
No. Durable Functions earns its complexity for multi-step workflows that need to survive retries, like the order-processing orchestration in Step 9. A single-purpose endpoint like getOrderStatus from Step 3 doesn’t need it.

Can I build this tutorial in Python or .NET instead of Node.js?
Yes. Python 3.13 is supported across Flex Consumption, Premium, and Dedicated plans, and .NET’s isolated worker model is fully supported on Flex Consumption as well. The resource creation, Key Vault, and Entra ID steps are identical regardless of language. Only the function handler syntax changes.

What happens to function apps still running Functions runtime 1.x?
Support for runtime 1.x ended September 14, 2026. Apps still on that version should migrate to runtime 4.x, the only version Microsoft currently fully supports across all languages, as soon as possible.

Is Go a viable language choice for this kind of project?
Go is now supported on Azure Functions, but only on the Flex Consumption plan, not on Consumption or Premium. If your team is standardizing on Go, that constraint alone may decide your hosting plan for you.

Why use Entra ID authentication instead of Azure API Management?
Entra ID authentication, configured directly on the function app in Step 8, is the simpler path when you just need to gate access by identity. API Management is worth adding on top when you also need request routing, rate limiting, or a unified API surface across multiple backend services.

How do I tear down everything from this tutorial when I’m done?
Delete resources in reverse order of creation: the Application Insights resource, the Key Vault, the function app itself, the storage account, and finally the resource group, which removes anything left over in one step. Leaving the Key Vault and Application Insights running is what quietly generates the most unexpected charges if you forget to clean up.