Azure Functions — A Developer’s Guide
What are Azure Functions?
Azure Functions is Microsoft’s serverless compute platform that lets you run small pieces of code (“functions”) without managing servers, VMs, or containers. As a developer, you focus purely on the logic — Azure takes care of hosting, scaling, patching, and infrastructure behind the scenes.
One of the biggest advantages of Azure Functions is how they help you lighten your main application. Instead of keeping everything inside a single API or service, you can decouple specific pieces of logic into small, independent functions. These functions can run asynchronously and fit naturally into event‑driven, pub‑sub architectures
A simple metal model:
This makes them perfect for modern cloud‑native systems where you want clean boundaries, minimal overhead, and automatic scaling without operational noise.
Why we as developers should use Azure Functions?
As Developers we could adapt Azure Functions to gain below benefits:
No infrastructure management
No servers, no containers, no patching. Azure takes care of hosting and runtime for you.Automatic scaling
Functions scale up and down based on demand. Whether you get 10 requests or 10,000, Azure scales automatically.Pay only for what you use
On the Consumption plan, you’re billed per execution. If your function doesn’t run, you don’t pay.Deep integration with Azure services
Functions plug directly into Service Bus, Event Grid, Cosmos DB, Blob Storage, and more — with almost no boilerplate.Perfect for asynchronous and event‑driven workflows
This lets you offload heavy or background logic from your main API, making your core application lighter and more responsive.
When to Use Azure Functions?
Azure Functions shine in scenarios where something happens and you need code to respond. They fit naturally into event‑driven, asynchronous, cloud‑native workflows. Here are the most common (and practical) use cases:
✔ Event‑driven processing
Perfect when an external event should trigger a piece of logic:
Blob uploaded → resize or validate the file
Queue message → process an order or background task
Event Grid event → react to side effects in your system
This is where Functions feel the most natural.
✔ Lightweight APIs
Azure Functions can also act as small, focused HTTP endpoints:
Quick HTTP APIs
Webhooks
Slack/Teams integrations
Great for scenarios where you don’t need a full-blown API project.
✔ Background jobs
Functions are ideal for scheduled or recurring tasks:
Timer-triggered jobs
Cleanup routines
Maintenance tasks
Daily/weekly summaries
No need for cron servers or background worker services.
✔ Decoupling logic from your main app
You can offload heavy or asynchronous logic from your API into Functions:
Long-running tasks
Notification pipelines
Data processing
Integrations
This keeps your main application lighter, faster, and easier to maintain.
How Azure Functions Work?
The simplest way to understand Azure Functions is to imagine a worker who sleeps when there’s nothing to do.
When something happens — an event, a message, a file upload — that worker gets triggered, wakes up, completes the job, and goes back to sleep until the next request arrives.
This is the core idea behind Azure Functions:
So what are these Triggers that Azure Functions wake up to?
Triggers are the events that cause a Function to run.
Azure provides a wide range of built‑in triggers so your Function can react to almost anything happening inside your system.
Common triggers include:
HTTP requests
(e.g., lightweight APIs, webhooks)Timer schedules
(e.g., daily jobs, cleanup tasks)Blob uploads
(e.g., process a file when it lands in storage)Queue messages
(e.g., background processing)Service Bus messages
(e.g., domain events like TaskAssigned)Cosmos DB change feed
(e.g., react to inserts/updates in a container)Event Grid events
(e.g., system events, custom events, resource changes)
Bindings
Bindings allow Azure Functions to connect to other Azure resources in a simple, declarative way — without writing a ton of boilerplate code. They make it easy to read from or write to external services during a Function’s execution.
Instead of manually creating SDK clients, handling authentication, or wiring up plumbing code, you simply declare the binding in your function signature and Azure takes care of the rest.
Azure Functions support two main types of bindings:
Input Bindings
Input bindings let your Function read data from another Azure resource.
For example:
If your Function needs to read a file from Blob Storage or fetch a document from Cosmos DB, you can declare an Input Binding. Azure will automatically pass that resource into your Function as a parameter.
This keeps your code clean and focused on logic, not infrastructure.
Output Bindings
Output bindings let your Function write data to another Azure resource.
For example:
If your Function needs to write a document into a Cosmos DB container or upload a file to Blob Storage, you can declare an Output Binding. Azure will handle the write operation for you.
Again — no SDK setup, no connection management, no boilerplate.
📌 Simple Example: HTTP Trigger → Read from Blob → Write to Queue
This example does three things:
Trigger: Runs when someone calls an HTTP endpoint
Input Binding: Reads a text file from Blob Storage
Output Binding: Sends a message to a Queue
Where Do we Store Configurations in Azure Functions?
Local Development
When running Functions locally, you store your settings in a file called: local.settings.json
Production
In production, you typically split your configuration into two categories.
Non Secret Values
These are non‑secret values such as:
Feature flags
Environment names
API base URLs
Logging levels
Toggle values
Any non-sensitive config
These live in:
Azure Portal → Function App → Settings → Environment Variables
Azure injects them as environment variables at runtime.
Secret Values
Secrets include:
Connection strings
API keys
Access tokens
Private keys
Sensitive URLs
Anything you wouldn’t commit to Git
These should never be stored directly in Function App settings.
Instead, you:
Store the secret in Azure Key Vault
Enable Managed Identity on the Function App
Use a Key Vault reference inside Function App Environment Variables
This way:
The Function App never stores the secret
The secret rotates safely
Access is controlled via Azure AD
No secrets appear in logs, code, or CI/CD pipelines
Hosting Plans
Azure Functions can run on 3 hosting plans.
| Plan | Suitable for | Details |
|---|---|---|
| Consumption | Event Driven Workloads | Auto-Scales based on demand, We pay-per-executions, cold starts possible and has 230 second timeout |
| Premium | High Performance Scenarios with no cold starts | They are always warmed up, allows Azure VNet support, usually good for long running operations, does not have 230 seconds timeout |
| Dedicated | This is ideal when Functions are part of a larger App Service‑based architecture. | Azure Function is hosted on your existing App Service Plan, Always on, no cold starts |
As a rule of thumb we can start with Consumption plan and based on the load we can always move into Premium or Dedicated plans.
Common Pitfalls
Even though Azure Functions are powerful and developer‑friendly, there are a few common pitfalls to be aware of — especially when running on the Consumption plan.
1. Cold Starts on the Consumption Plan
If your Function isn’t triggered frequently, it may go idle.
The first request after an idle period can experience a cold start, which adds noticeable latency.
This mainly affects:
HTTP-triggered functions
Functions with large dependencies
Functions deployed in regions far from the caller
Premium and Dedicated plans eliminate cold starts.
2. 230‑Second Timeout for HTTP Functions (Consumption Plan)
On the Consumption plan, HTTP-triggered functions have a hard timeout of ~230 seconds.
If your operation is long‑running, you should:
Move the logic into a Durable Function, or
Use an asynchronous workflow (publish a message to Queue/Service Bus and process it in the background)
This keeps your API responsive and avoids timeouts.
3. Built‑in Retry Policies
Azure Functions automatically retry failed executions depending on the trigger type.
For example:
Queue and Service Bus triggers retry multiple times
Event Grid retries with exponential backoff
HTTP triggers do not retry automatically
Developers often wonder:
“Can we use Polly retry policies inside Azure Functions?”
You can, but it’s usually unnecessary because the platform already handles retries for you.
Polly is more useful inside your business logic (e.g., retrying an external API call), not for retrying the Function itself.
4. Use Durable Functions for Complex Orchestration
If your workflow needs:
Fan‑out / fan‑in
Chaining multiple steps
Waiting for external events
Long‑running stateful operations
Then Durable Functions is the right tool.
Trying to implement these patterns manually inside a normal Function often leads to complexity, timeouts, or inconsistent state.
Real World Example
📌Scenario
When a manager assigns a task to a Team member:
The API (Domain Event Handler class) publishes a lightweight TaskAssigned message (with TaskId only) to a Service Bus Topic.
The API (Domain Event Handler class) also writes a full assignment record into Cosmos DB → TaskAssigned container.
An Azure Function reacts to the Service Bus message, fetches the full record from Cosmos DB, and publishes a TaskAssignedNotification event to Event Grid for Fan Out.
Downstream systems (UI dashboards, notifications, analytics) react to the Event Grid event.
📌Trigger
Service Bus Topic Trigger
The Azure Function is triggered when a new message arrives in the TaskAssigned topic.
Why Service Bus?
Because this is a domain event that must not be lost — Service Bus guarantees delivery, retries, and durability.
📌Input Binding
Cosmos DB Input Binding
The Function reads the full task assignment record from:
- Cosmos DB TaskAssigned Container
The record contains:
taskId
assignedUserId
assignedByManagerId
timestamps
metadata
This avoids passing large payloads through Service Bus and keeps the message lightweight.
📌Output Binding
Event Grid Output Binding
The Function publishes a new event:
eventType: TaskAssignedNotification
subject: /tasks/{taskId}
data: { taskId, assignedUserId, assignedByManagerId, timestamp }
Why Event Grid?
Because it enables fan-out to multiple consumers:
Real-time UI updates (via SignalR)
Email/SMS notifications
Teams/Slack messages
Analytics pipelines
Audit logs
Mobile app updates
No changes to the Function are needed when new consumers are added.
📌Example (C#)
public static class TaskAssignedNotificationFunction
{
[FunctionName("TaskAssignedNotificationFunction")]
public static async Task Run(
// 1️⃣ Trigger: Fired when a message arrives in the Service Bus Topic
[ServiceBusTrigger("task-assigned", "notification-sub", Connection = "ServiceBusConnection")]
TaskAssignedMessage message,
// 2️⃣ Input Binding: Fetch the full assignment record from Cosmos DB
// - Uses the TaskId from the Service Bus message
[CosmosDB(
databaseName: "devpulse",
containerName: "TaskAssigned",
Connection = "CosmosDbConnection",
Id = "{taskId}",
PartitionKey = "{taskId}")]
TaskAssignmentRecord assignment,
// 3️⃣ Output Binding: Publish a new event to Event Grid
[EventGrid(TopicEndpointUri = "EventGridTopicUri", TopicKeySetting = "EventGridKey")]
IAsyncCollector<EventGridEvent> eventGridOutput,
ILogger log)
{
// Log the start of processing
log.LogInformation($"Processing TaskAssigned event for TaskId: {message.TaskId}");
// Build the event payload that will be sent to Event Grid
var notificationEvent = new EventGridEvent(
subject: $"task/{assignment.TaskId}", // Logical grouping of events
eventType: "TaskAssignedNotification", // Custom event type
data: new {
assignment.TaskId,
assignment.AssignedUserId,
assignment.AssignedByManagerId,
assignment.Timestamp
},
dataVersion: "1.0"
);
// Publish the event to Event Grid
await eventGridOutput.AddAsync(notificationEvent);
// Log completion
log.LogInformation($"TaskAssignedNotification published for TaskId: {assignment.TaskId}");
}
}
Summary
Azure Functions is one of the easiest ways to build event-driven, serverless applications on Azure. It removes infrastructure overhead, scales automatically, and integrates deeply with Azure services.
If you’re building modern cloud-native systems — Functions should be in your toolkit.