Cloud Platforms No. 179

Azure Functions skill for Claude Code

Current models, managed identity and handled poison queues. Claude builds function apps on the current programming model for each language, migrating off in-process .NET and function.json-era code before the support cutoff. It picks a hosting plan from latency and networking needs, connects triggers through managed identity and Key Vault references, tunes host.json per trigger, and makes sure failing messages land in a monitored poison or dead-letter queue.

  • nameazure-functions
  • size204 lines · ≈2,350 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Builds and deploys Azure Functions - choosing a hosting plan (Flex Consumption, Premium, Dedicated, Container Apps), the .NET isolated worker model, Node.js v4 and Python v2 programming models, triggers and bindings, host.json tuning, identity-based connections and Key Vault references, poison messages and retries, Durable Functions basics, local development with Core Tools, and deployment. Use when creating or migrating an Azure Function app, wiring queue, Service Bus, timer or HTTP triggers, fixing cold starts or poison queues, or moving off the in-process .NET model.

Try asking

  • Migrate our in-process .NET functions to the isolated worker model
  • Add a Service Bus triggered function that uses managed identity
  • Our queue function keeps sending messages to the poison queue. Why?

Use it when

  • Creating or migrating an Azure Functions app
  • Wiring HTTP, timer, queue, Service Bus or Event Hubs triggers
  • Choosing between Flex Consumption, Premium and Dedicated plans
  • Replacing connection strings with managed identity

Skip it when

  • The workload runs on AWS Lambda — use aws-lambda
  • You need general .NET or C# language guidance — use csharp-dotnet

How to check the result

Triggers fire locally under func start with Azurite, a failing message reaches the poison or dead-letter queue, and app settings contain identity connections or Key Vault references instead of secrets.

The common mistake it prevents

Starting new .NET work on the in-process model, which loses support in November 2026 and forces a rushed migration later.

~/.claude/skills/azure-functions/SKILL.md

--- name: azure-functions description: Builds and deploys Azure Functions - choosing a hosting plan (Flex Consumption, Premium, Dedicated, Container Apps), the .NET isolated… license: MIT ---

A well-built function app uses the current programming model for its language, runs on a plan that matches its latency and networking needs, connects to storage and queues with managed identity instead of connection strings, handles poison messages deliberately, and deploys from a pipeline with settings in configuration, not in code.

Workflow

- [ ] 1. Identify language/model, hosting plan, triggers, and how it is deployed today
- [ ] 2. Confirm the programming model is current (migrate if not)
- [ ] 3. Pick or validate the hosting plan
- [ ] 4. Implement triggers/bindings; tune host.json for the trigger type
- [ ] 5. Identity-based connections, Key Vault references, app settings
- [ ] 6. Local run with Core Tools and Azurite; tests
- [ ] 7. Deploy, then check Application Insights for failures and cold starts

2. Current programming models

LanguageUseLegacy to migrate from
C# / .NETIsolated worker model (.NET 8+, Microsoft.Azure.Functions.Worker) with ASP.NET Core integrationIn-process model: support ends November 10, 2026
JavaScript / TypeScriptNode.js v4 model: functions registered in code with app.http(...), no function.jsonv3 model with per-function function.json
Pythonv2 model: decorators on func.FunctionApp() in function_app.pyv1 model with function.json
Java, PowerShell, custom handlersSupported; check the language guide-

.NET isolated (Program.cs):

var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();               // ASP.NET Core integration for HTTP
builder.Services.AddApplicationInsightsTelemetryWorkerService()
                .ConfigureFunctionsApplicationInsights();
builder.Build().Run();

public class Orders(ILogger<Orders> log, OrderService svc)
{
    [Function("GetOrder")]
    public async Task<IActionResult> Get(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "orders/{id}")] HttpRequest req, string id)
        => await svc.Find(id) is { } o ? new OkObjectResult(o) : new NotFoundResult();

    [Function("ProcessOrder")]
    public async Task Process([ServiceBusTrigger("orders", Connection = "ServiceBus")] OrderMessage msg)
        => await svc.Process(msg);       // throw to retry; after MaxDeliveryCount it dead-letters
}

Node.js v4:

import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";

app.http("getOrder", {
  methods: ["GET"],
  authLevel: "function",
  route: "orders/{id}",
  handler: async (req: HttpRequest, ctx: InvocationContext): Promise<HttpResponseInit> => {
    const order = await orders.find(req.params.id);
    return order ? { jsonBody: order } : { status: 404 };
  },
});

app.storageQueue("resizeImage", {
  queueName: "images",
  connection: "Storage",
  handler: async (msg: unknown, ctx: InvocationContext) => { await resize(msg as ImageJob); },
});

Python v2:

import azure.functions as func
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="orders/{id}", methods=["GET"])
def get_order(req: func.HttpRequest) -> func.HttpResponse:
    order = repo.find(req.route_params["id"])
    return func.HttpResponse(json.dumps(order), mimetype="application/json") if order else func.HttpResponse(status_code=404)

@app.timer_trigger(schedule="0 */5 * * * *", arg_name="timer", run_on_startup=False)
def sweep(timer: func.TimerRequest) -> None:
    repo.expire_stale_carts()

Timer schedules are six-field NCRONTAB (seconds first): 0 */5 * * * * = every 5 minutes. Times are UTC unless WEBSITE_TIME_ZONE is set (not supported on all Linux plans).

3. Hosting plan

PlanChoose when
Flex ConsumptionDefault for new serverless apps: scale to zero, per-function scaling, always-ready instances option, VNet integration, configurable instance memory. Linux only
Consumption (Y1)Existing apps; Windows-only needs. Linux Consumption is being retired for new features; plan migrations to Flex
Premium (EP1-EP3)Pre-warmed instances (no cold start), long runs, VNet, larger instances
Dedicated (App Service plan)Predictable steady load, reuse existing App Service capacity
Azure Container AppsFunctions in containers next to other container apps, custom images

Flex Consumption specifics: deploy with zip/one-deploy (no WEBSITE_RUN_FROM_PACKAGE app setting), blob triggers must use the Event Grid source, and some legacy settings are rejected. Check the plan’s supported-feature list before migrating.

HTTP requests on Consumption-style plans are cut at 230 seconds by the front-end load balancer regardless of functionTimeout; use Durable Functions or a queue plus status endpoint for longer work.

4. Bindings and host.json

{
  "version": "2.0",
  "functionTimeout": "00:10:00",
  "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } },
  "extensions": {
    "queues": { "batchSize": 16, "newBatchThreshold": 8, "maxDequeueCount": 5, "visibilityTimeout": "00:00:30" },
    "serviceBus": { "maxConcurrentCalls": 16, "autoCompleteMessages": true },
    "http": { "routePrefix": "api" }
  }
}
TriggerFailure behaviorAction
Storage queueRetries up to maxDequeueCount (default 5), then moves to <queue>-poisonMonitor and replay the poison queue
Service BusRedelivered until the queue’s MaxDeliveryCount, then dead-letter subqueueAlert on dead-letter count; set lock duration above processing time
Event Hubs / KafkaCheckpoint advances even on failure unless you add retry policies; messages can be skippedUse function retry policies (fixedDelay/exponentialBackoff) and idempotent handlers
TimerMissed runs are not replayed by default (useMonitor tracks schedule)Make runs idempotent; avoid overlapping long runs
BlobAt-least-once, may be delayed on polling-based triggerPrefer Event Grid source; idempotent processing

All triggers are at-least-once: design handlers to tolerate duplicates.

5. Identity, settings, secrets

  • Prefer identity-based connections: instead of a connection string, set Storage__accountName=acmeprodstore (or AzureWebJobsStorage__accountName) and ServiceBus__fullyQualifiedNamespace=acme.servicebus.windows.net, then grant the function app’s managed identity data-plane roles (Storage Blob Data Owner, Storage Queue Data Contributor, Azure Service Bus Data Receiver) on the specific resources.
  • Remaining secrets via Key Vault references in app settings: @Microsoft.KeyVault(SecretUri=https://acme-kv.vault.azure.net/secrets/StripeKey/).
  • local.settings.json is for local only and is never committed (it is in the default .gitignore).
az functionapp config appsettings set -g rg-orders -n func-orders-prod \
  --settings "ServiceBus__fullyQualifiedNamespace=acme.servicebus.windows.net"
az functionapp identity assign -g rg-orders -n func-orders-prod

6. Local development

func init --worker-runtime node --language typescript   # or dotnet-isolated, python
func new --template "HTTP trigger" --name getOrder
azurite --silent --location .azurite &                  # local storage emulator
func start

Set "AzureWebJobsStorage": "UseDevelopmentStorage=true" in local.settings.json for Azurite. Keep business logic in plain modules/classes and unit-test those; test functions end-to-end with func start and real (emulated) triggers.

7. Deploy

  • CI: build, then func azure functionapp publish <APP> or the Azure/functions-action GitHub Action with OIDC federated credentials (no publish profiles).
  • Use deployment slots (Premium/Dedicated) for swap-based zero-downtime deploys; Flex Consumption uses rolling updates instead of slots.
  • Production deploys and slot swaps require user confirmation.

Durable Functions (when to reach for it)

Use for multi-step workflows, fan-out/fan-in, human approval waits, and long-running HTTP operations (async HTTP API pattern with status URLs). Orchestrator code must be deterministic: no DateTime.Now/Date.now(), random, or direct I/O in orchestrators; use context.CurrentUtcDateTime, activities for I/O, and durable timers.

Gotchas

  • Creating clients (HttpClient, SDK clients) per invocation exhausts sockets; register singletons via DI or module scope.
  • Mixing extension bundle versions and explicit extension packages causes binding load failures.
  • Application Insights sampling can hide rare errors; exclude exceptions and requests from sampling when debugging.
  • Function keys (authLevel: function) are shared secrets, not user auth; use Entra ID (Easy Auth) or validate JWTs for user-facing APIs.

Verification

  1. func start runs locally; each trigger fires against Azurite or a dev namespace.
  2. In Application Insights: failures blade is clean for new deployment; check requests and exceptions for the function names; cold-start durations acceptable.
  3. A deliberately failing queue message lands in <queue>-poison or the dead-letter subqueue after the configured attempts.
  4. az functionapp config appsettings list shows no plaintext connection strings where identity-based connections or Key Vault references were intended.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc