Azure Functions: Triggers + Bindings
Serverless APIs and event handlers without writing the plumbing. Trigger types, input and output bindings, the binding expression syntax, and how AI workloads use Functions for thin glue between Azure services.
What Functions actually does for you
Azure Functions runs small pieces of code in response to events, with the boilerplate written for you. Want to react to a Service Bus message? Write a function with a Service Bus trigger β the runtime handles connection, locking, retries, even parallelism.
The big productivity win is bindings. An input binding reads from a service (e.g., Cosmos DB) and hands your function the result. An output binding takes a value your function returns and writes it somewhere (e.g., a Service Bus queue). You write business logic; the framework writes the plumbing.
For AI back-ends Functions sit between Azure services as thin glue β embed-on-blob-create, summarise-on-cosmos-update, schedule-nightly-cleanup.
A function in three flavours
HTTP trigger (the canonical serverless API)
# Python (v2 programming model)
import azure.functions as func
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
@app.route(route="embed", methods=["POST"])
def embed(req: func.HttpRequest) -> func.HttpResponse:
body = req.get_json()
text = body["text"]
embedding = generate_embedding(text)
return func.HttpResponse(json.dumps({"embedding": embedding}), mimetype="application/json")
Service Bus queue trigger
@app.service_bus_queue_trigger(
arg_name="msg",
queue_name="image-jobs",
connection="ServiceBusConnection",
)
def process_image_job(msg: func.ServiceBusMessage):
payload = json.loads(msg.get_body().decode())
embed_image(payload["url"])
The runtime opens the connection, holds the message lock, calls your function, and (depending on disposition) completes/abandons the message automatically. No Receive/Complete boilerplate.
Cosmos DB change-feed trigger + output binding
@app.cosmos_db_trigger(
arg_name="docs",
container_name="articles",
database_name="knowledge",
connection="CosmosConnection",
lease_container_name="articles-leases",
)
@app.cosmos_db_output(
arg_name="output_doc",
container_name="article_embeddings",
database_name="knowledge",
connection="CosmosConnection",
)
def embed_articles(docs: func.DocumentList, output_doc: func.Out[func.Document]):
for doc in docs:
if "embedding" not in doc:
embedding = generate_embedding(doc["body"])
output_doc.set(func.Document.from_dict({
"id": f"{doc['id']}-emb",
"article_id": doc["id"],
"embedding": embedding,
}))
Three Azure services touched (Cosmos read trigger, Cosmos write output) β zero SDK calls in your code.
Trigger types you must know
| Feature | HTTP | Timer | Service Bus | Event Grid | Cosmos DB | Blob |
|---|---|---|---|---|---|---|
| Fires when | Request hits the function URL | On a CRON schedule | Message arrives in queue or topic subscription | Event Grid event is delivered to the function | Document created or updated (change feed) | Blob created or updated |
| Auth | Function key, Microsoft Entra (Easy Auth), or anonymous | N/A | Connection string or managed identity | WebHook endpoint with validation | Connection string or managed identity | Connection string or managed identity |
| Best for | Web APIs | Scheduled jobs (cleanup, refresh) | Reliable async work | Reactive integration across Azure | React to data changes | File ingestion (use with Event Grid) |
Input and output bindings
@app.route(route="article/{id}")
@app.cosmos_db_input(
arg_name="article",
container_name="articles",
database_name="knowledge",
connection="CosmosConnection",
id="{id}", # bind to the route parameter
partition_key="{id}",
)
@app.queue_output(
arg_name="qout",
queue_name="audit",
connection="StorageConnection",
)
def get_article(req, article: func.Document, qout: func.Out[str]):
qout.set(json.dumps({"action": "read", "id": req.route_params["id"]}))
return func.HttpResponse(article.to_json())
Notice the binding expressions:
id="{id}"β the route parameter namedidpartition_key="{id}"β same value bound twice- The handler itself receives the doc as
article, no SDK call
Common binding expression patterns:
| Expression | Resolves to |
|---|---|
{id} | A route parameter |
{queueTrigger} | The current message body |
{rand-guid} | A random GUID per execution |
{datetime:yyyy-MM-dd} | The current UTC date in the format |
{sys.utcNow} | UTC timestamp |
%MyAppSetting% | A value from app settings |
Authentication options
| Auth | Where it applies | When |
|---|---|---|
| Function key | HTTP triggers | Quick start, simple keys per function |
| App-wide host key | All HTTP triggers | Convenience |
| Anonymous | HTTP triggers | Public webhooks |
| Microsoft Entra (Easy Auth) | HTTP triggers | Production β sign-in, JWT validation, role checks |
| Managed identity for service connections | Triggers and bindings | Recommended β avoid connection strings |
# Use managed identity for the Service Bus trigger connection
az functionapp config appsettings set \
--name roo-fn -g roo-prod \
--settings "ServiceBusConnection__fullyQualifiedNamespace=roo-sb.servicebus.windows.net"
The __fullyQualifiedNamespace suffix tells the host to use Microsoft Entra credentials (the functionβs managed identity) instead of a connection string.
Function chaining vs Durable Functions
For multi-step orchestration (call A, then B, with retries and state), Functions has a sibling: Durable Functions. It adds:
- Orchestrator functions that describe a workflow as code
- Activity functions that do the actual work
- Durable Entities for stateful actors
- Built-in fan-out/fan-in, retries, timers, sub-orchestrations
Durable is overkill for βreact to event β do one thingβ workflows β thatβs plain Functions territory. It shines when an AI workflow is a multi-step chain (extract β embed β enrich β store β notify) with retries and possible long waits.
Key terms
Knowledge check
Mira wants a function that reads a Cosmos document by route parameter, modifies it, and writes the updated doc back. What's the cleanest pattern?
Theo's function processes Service Bus messages. The connection currently uses a connection string in app settings. He wants to switch to managed identity. What's the right pattern?
Lin's AI workflow is: extract text from PDF β call OpenAI to summarise β write summary to Cosmos β notify Slack. The total can take 30-90 seconds with multiple retry-able steps. Which Functions pattern fits best?