Azure Functions skill for Codex

Triggers, bindings and durable workflows done right. Codex identifies which Azure Functions programming model a project uses, moves it to the current one, and picks a hosting plan from real timeout and networking needs. Bindings connect through managed identity with data-plane roles, Durable Functions orchestrators stay deterministic, and everything runs under Core Tools and Azurite before a confirmed publish.

  • Cloud Platforms
  • $azure-functions
  • 215 lines
  • 2 files
  • ~2,100 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- azure-functions

Installs to ~/.agents/skills/azure-functions/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $azure-functions, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds and deploys Azure Functions on the Flex Consumption or Premium plans - Node.js v4 and Python v2 programming models, .NET isolated worker, triggers and input/output bindings, host.json and extension bundles, identity-based connections, Durable Functions orchestrations, timeouts and Core Tools local runs. Use when writing or deploying an Azure Function, adding a queue, Service Bus, blob, timer or Cosmos DB trigger, building a Durable Functions workflow, migrating off the .NET in-process model, or debugging functions that do not fire.

Use it when

  • Writing, running or publishing an Azure Functions app
  • Adding triggers or bindings for queues, blobs, timers, Service Bus or Cosmos DB
  • Designing a Durable Functions workflow
  • Functions do not show up or do not fire after deploy

Use something else when

How to check the result

Core Tools lists every function locally, the published app shows them in az functionapp function list, and Application Insights records successful test invocations.

The mistake it prevents

Calling Date.now() or doing I/O inside a Durable orchestrator, which breaks replay and produces nondeterminism errors.

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

--- name: azure-functions description: Builds and deploys Azure Functions on the Flex Consumption or Premium plans - Node.js v4 and Python v2 programming models, .NET isolated worker,… license: MIT ---

Done well means: the app uses the current programming model for its language, runs on a plan chosen for its timeout and networking needs, connects to storage and queues with a managed identity instead of connection strings, has idempotent triggers, and runs locally with Core Tools before anything is published.

Inspect first

  1. Read AGENTS.md, host.json, package.json / requirements.txt / .csproj, and any Bicep or Terraform for the app. Never commit local.settings.json; check .gitignore.
  2. Identify the model from the code:
You seeModelStatus
app.http(...) from @azure/functions v4Node.js v4Current
function.json next to each index.jsNode.js v3Migrate to v4
app = func.FunctionApp() with decoratorsPython v2Current
function.json + __init__.pyPython v1Migrate to v2
Microsoft.Azure.Functions.Worker package.NET isolatedCurrent
Microsoft.NET.Sdk.Functions, [FunctionName].NET in-processSupport ends 10 Nov 2026; migrate
  1. Check the live app (needs network and az login):
az functionapp list -g "$RG" -o table
az functionapp show -g "$RG" -n "$APP" --query "{plan:serverFarmId, kind:kind, state:state}"
az functionapp config appsettings list -g "$RG" -n "$APP" -o table   # values are sensitive; do not paste them into chat

Choose the plan

NeedPlan
New serverless app, VNet integration, scale to zeroFlex Consumption
Always-warm instances, deployment slots, no cold startPremium (EP1+)
Already paying for an App Service plan with spare capacityDedicated
Legacy Linux ConsumptionDo not start new apps here; it is being retired

Timeouts: Flex Consumption and Premium default to 30 minutes and can be unbounded; classic Consumption caps at 10. Any HTTP-triggered call is still cut at about 230 seconds by the front-end load balancer, so long HTTP work must become async (queue + status endpoint, or Durable Functions). Flex Consumption does not support deployment slots.

Handlers

Node.js v4 (TypeScript):

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

const queueOut = output.storageQueue({ queueName: "thumbnails", connection: "Storage" });

app.http("upload", {
  methods: ["POST"],
  authLevel: "function",
  extraOutputs: [queueOut],
  handler: async (req: HttpRequest, ctx: InvocationContext): Promise<HttpResponseInit> => {
    const body = (await req.json()) as { blobName?: string };
    if (!body.blobName) return { status: 400, jsonBody: { error: "blobName required" } };
    ctx.extraOutputs.set(queueOut, { blobName: body.blobName });
    return { status: 202 };
  },
});

app.serviceBusQueue("orders", {
  queueName: "orders",
  connection: "ServiceBus",
  handler: async (message: unknown, ctx: InvocationContext) => {
    ctx.log("delivery", ctx.triggerMetadata?.deliveryCount);
    await processOrder(message);              // throw => message retried, then dead-lettered
  },
});

Python v2:

import azure.functions as func
import logging

app = func.FunctionApp()

@app.timer_trigger(schedule="0 */15 * * * *", arg_name="timer", run_on_startup=False)
def sweep(timer: func.TimerRequest) -> None:
    if timer.past_due:
        logging.warning("timer is running late")
    cleanup_expired()

@app.blob_trigger(arg_name="blob", path="uploads/{name}", connection="Storage")
def on_blob(blob: func.InputStream) -> None:
    logging.info("processing %s (%d bytes)", blob.name, blob.length)

Timer schedules are six-field NCRONTAB (seconds first). 0 */15 * * * * is every 15 minutes; a five-field expression fails to index.

host.json essentials

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  },
  "logging": { "logLevel": { "default": "Information", "Host.Results": "Error" } },
  "extensions": {
    "serviceBus": { "maxConcurrentCalls": 16 },
    "queues": { "batchSize": 16, "maxDequeueCount": 5 }
  }
}

.NET isolated apps reference extension NuGet packages instead of the bundle.

Identity-based connections

Replace connection strings with a managed identity. For a binding with connection: "Storage", set app settings with the double-underscore suffixes:

Storage__accountName = mystorageacct
ServiceBus__fullyQualifiedNamespace = mybus.servicebus.windows.net
AzureWebJobsStorage__accountName = mystorageacct

Then grant the app’s identity data-plane roles: Storage Blob Data Owner, Storage Queue Data Contributor (host storage needs both), Azure Service Bus Data Receiver. Control-plane roles such as Contributor do not grant data access.

Durable Functions

Orchestrators must be deterministic: no Date.now(), random numbers, direct I/O or await fetch inside them. Use the context’s current time, call activities for I/O, and use durable timers for waits.

import * as df from "durable-functions";

df.app.orchestration("fulfil", function* (ctx) {
  const order = ctx.df.getInput<{ id: string }>();
  const reserved = yield ctx.df.callActivity("reserveStock", order);
  if (!reserved) return "out-of-stock";
  const approval = ctx.df.waitForExternalEvent("approved");
  const timeout = ctx.df.createTimer(new Date(ctx.df.currentUtcDateTime.getTime() + 24 * 3600e3));
  const winner = yield ctx.df.Task.any([approval, timeout]);
  return winner === approval ? yield ctx.df.callActivity("ship", order) : "expired";
});

df.app.activity("reserveStock", { handler: async (order) => reserve(order) });
PatternUse
Function chainingOrdered steps with retries per step (callActivityWithRetry)
Fan-out/fan-inTask.all over many activities
Human interactionwaitForExternalEvent + timer
Long polling / monitorLoop with createTimer, then continueAsNew to cap history

Changing an orchestrator’s code while instances are in flight can break replay. Version orchestrations by name (fulfil_v2) or drain running instances first.

Local run and deploy

func init --worker-runtime node --language typescript   # new app only
func new --template "HTTP trigger" --name upload
func start                                               # needs Azurite or a real storage account

az functionapp create -g "$RG" -n "$APP" --storage-account "$SA" \
  --flexconsumption-location westeurope --runtime node --runtime-version 22 \
  --assign-identity "[system]"
func azure functionapp publish "$APP"                    # outward-facing: confirm first

Run Azurite (npx azurite --silent) for local storage triggers, with "AzureWebJobsStorage": "UseDevelopmentStorage=true" in local.settings.json. Package installs and publishing need network access.

Gotchas

  • A function that does not appear after deploy usually failed indexing: check func start output locally and the app’s Log stream for “No job functions found”.
  • Queue and Service Bus triggers are at-least-once; make handlers idempotent and rely on poison queues (<queue>-poison) or dead-letter queues rather than infinite retries.
  • authLevel: "function" keys are shared secrets, not user auth. Put Entra ID auth (Easy Auth or API Management) in front for user-facing APIs.
  • Changing app settings restarts the app.
  • Blob triggers on Flex Consumption use the Event Grid source; polling blob triggers are slow and not supported there.

Verification

  • func start lists every function with its trigger; a local request or Azurite message produces the expected output.
  • After publish, az functionapp function list -g "$RG" -n "$APP" -o table shows them all.
  • Application Insights shows successful invocations and no indexing errors.
  • A replayed message produces one side effect.

Report template

**App:** orders-func (Flex Consumption, westeurope, Node 22, v4 model)
**Functions:** upload (HTTP POST), orders (Service Bus queue), sweep (timer 15 min)
**Identity:** system-assigned; Storage Blob Data Owner + Queue Data Contributor, Service Bus Data Receiver
**Durable:** fulfil orchestration, approval wait 24 h, activities idempotent on order id
**Verified:** func start local run, publish OK, 3 test invocations in App Insights

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Azure Functions"
  short_description: "Triggers, bindings and durable workflows done right"
  default_prompt: "$azure-functions Add a Service Bus queue trigger to this Azure Functions app using managed identity"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem