DEV_NET_CORE
GET_STARTED
AzureAzure Functions and Durable Functions

HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers

Overview

An Azure Functions trigger defines the event that starts a function. Every function has exactly one trigger, although it can also use input and output bindings or Azure SDK clients to read and write other resources.

The trigger determines important runtime behavior:

  • How work arrives.
  • Whether a caller waits for a response.
  • How failed work is retried.
  • Whether delivery can be duplicated.
  • How concurrency and scale are calculated.
  • Which authentication and network paths are required.
  • How poison work is isolated.
  • Which ordering guarantees are available.

Common trigger choices include:

  • HTTP: Synchronous APIs and webhooks.
  • Timer: Scheduled work.
  • Azure Queue Storage: Simple asynchronous work queues.
  • Azure Service Bus: Enterprise messaging with queues, topics, sessions, dead-lettering, and richer broker semantics.
  • Azure Event Grid: Push-based notification of discrete events.
  • Azure Blob Storage: Processing newly created or updated blobs.

These triggers are not interchangeable. An HTTP trigger is a poor fit for a process that can take several minutes because the caller and HTTP infrastructure impose response limits. A timer trigger is not a durable work queue. Event Grid notifies subscribers that an event occurred but is not designed as a command queue. A Blob trigger is convenient for file processing, but the event-based implementation should normally be preferred for lower latency and is required by Flex Consumption.

For interviews, a strong candidate should explain:

  • Trigger and binding differences.
  • At-least-once delivery and idempotency.
  • Retry and poison-message behavior.
  • Concurrency and scale implications.
  • Queue Storage versus Service Bus.
  • Event Grid versus messaging.
  • Polling-based versus event-based blob detection.
  • Authentication through managed identity.
  • Why function-app grouping affects deployment, configuration, scaling, and failure isolation.

Core Concepts

Trigger Versus Binding

A trigger starts a function and can provide trigger data. A function must have exactly one trigger.

An input binding reads additional data without requiring the function to create a client manually. An output binding writes data after successful execution. Bindings are optional.

Code
Trigger -> starts execution
Input binding -> provides additional data
Function code -> applies behavior
Output binding -> writes a result

Bindings reduce integration code, but Azure SDK clients are often better when code needs transactions, conditional operations, pagination, advanced retries, or precise control over calls.

Choose by Delivery Semantics

TriggerPrimary modelTypical use
HTTPSynchronous request-responseAPIs and webhooks
TimerScheduleCleanup, reporting, periodic synchronization
Queue StorageSimple pull-based work queueBackground commands and buffering
Service BusBrokered enterprise messagingCommands, topics, sessions, transactions
Event GridPush-based event notificationReacting to Azure or domain events
BlobFile creation or updateImage, document, and data-file processing

The choice should follow the required semantics rather than the team familiarity with a service.

HTTP Triggers

HTTP triggers support APIs and webhook receivers. Their binding configuration defines:

  • Allowed HTTP methods.
  • Route template.
  • Authorization level.
  • Request and response types.

In .NET isolated Functions with ASP.NET Core integration:

Code
public sealed class GetOrderFunction
{
    [Function("GetOrder")]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get",
            Route = "orders/{id:guid}")] HttpRequest request,
        Guid id)
    {
        return new OkObjectResult(new { id, status = "Pending" });
    }
}

HTTP functions should validate input, authorize access, use appropriate status codes, and return consistent error contracts.

Function Keys Are Not Complete Authorization

HTTP authorization levels include values such as anonymous, function, and admin. Function and host keys can restrict invocation, but a shared key does not provide user identity, object-level authorization, or fine-grained permissions.

Production APIs commonly use:

  • Microsoft Entra ID.
  • App Service authentication.
  • API Management policies.
  • OAuth access tokens.
  • Application-level authorization.

The function must still enforce authorization for the requested resource and operation.

Keep HTTP Work Bounded

Regardless of a function app's functionTimeout, an HTTP-triggered function has a platform response limit of approximately 230 seconds because of the Azure Load Balancer idle timeout.

For longer work:

  1. Validate the request.
  2. Persist or enqueue a command.
  3. Return 202 Accepted with an operation identifier.
  4. Process asynchronously.
  5. Expose status or send completion notification.

Durable Functions provides a built-in asynchronous HTTP pattern for orchestrated workflows.

HTTP Idempotency

Clients, proxies, and gateways can retry requests. Design state-changing operations with:

  • Client-provided idempotency keys.
  • Unique business identifiers.
  • Conditional writes.
  • Duplicate-result storage.
  • Safe retry status behavior.

Do not assume a request executes exactly once because it arrived over HTTP.

Timer Triggers

Timer triggers execute on a schedule. Azure Functions commonly uses a six-field NCRONTAB expression:

Code
[Function("NightlyCleanup")]
public Task Run(
    [TimerTrigger("0 0 2 * * *")] TimerInfo timer,
    CancellationToken cancellationToken)
{
    return cleanupService.RunAsync(cancellationToken);
}

The fields represent:

Code
second minute hour day month day-of-week

Store environment-specific schedules in application settings rather than recompiling code.

Time Zones and Daylight Saving

Schedules are evaluated in UTC by default. A configurable time-zone setting is available in supported hosting and operating-system combinations, but UTC is usually easier to reason about.

If a business schedule depends on local time:

  • Confirm hosting-plan support.
  • Test daylight-saving transitions.
  • Decide how skipped or repeated local times should behave.
  • Record the effective time zone in telemetry.

Timer Coordination

When a function app scales to multiple instances, only one instance runs a given timer-triggered function. The timer trigger uses host storage locks for coordination. It also does not start another scheduled invocation while the previous invocation remains outstanding.

Function apps sharing the same host storage need distinct host identities. Incorrect host-ID collisions can prevent one app's timer from running.

Timer Failure Behavior

Unlike a queue trigger, a timer trigger does not automatically retry a failed invocation. The next invocation occurs at the next scheduled time.

For important scheduled work:

  • Make the operation idempotent.
  • Persist execution checkpoints.
  • Alert on failure.
  • Consider having the timer enqueue durable work.
  • Use Durable Functions when workflow state and retry policy are required.

Avoid RunOnStartup for normal production schedules because restarts and scale events can cause unexpected executions.

Azure Queue Storage Triggers

Queue Storage triggers are useful for simple asynchronous commands and buffering:

Code
[Function("ProcessThumbnail")]
public Task Run(
    [QueueTrigger("thumbnail-requests",
        Connection = "StorageConnection")] ThumbnailRequest request,
    CancellationToken cancellationToken)
{
    return processor.ProcessAsync(request, cancellationToken);
}

Queue messages are delivered at least once. A message becomes temporarily invisible while being processed. If execution fails, it becomes visible again after the configured visibility timeout.

Queue Retries and Poison Messages

The Queue Storage extension uses settings such as:

  • batchSize.
  • newBatchThreshold.
  • visibilityTimeout.
  • maxDequeueCount.
  • maxPollingInterval.
  • messageEncoding.

After the message reaches maxDequeueCount, the runtime moves it to a queue named <original-queue>-poison.

The poison queue needs:

  • Monitoring and alerts.
  • Diagnostic context.
  • A correction and replay process.
  • Access controls for sensitive message data.

Moving a message does not resolve the underlying defect.

Queue Concurrency

The runtime retrieves messages in batches and processes them concurrently. Per-instance concurrency is influenced by batchSize and newBatchThreshold, and total concurrency increases when the app scales out.

Setting batchSize to one reduces concurrency on one instance, but multiple scaled-out instances can still process messages in parallel. Global serialization needs a different design, such as partitioning, broker sessions, or an external lock.

Queue Storage Limitations

Queue Storage is intentionally simple. It does not provide the full set of Service Bus capabilities such as:

  • Topics and subscriptions.
  • Sessions for ordered processing.
  • Broker transactions.
  • Scheduled delivery with the same messaging model.
  • Duplicate detection.
  • Rich dead-letter metadata and forwarding.

Use Queue Storage when those features are unnecessary and simplicity and cost are priorities.

Azure Service Bus Triggers

Service Bus triggers process messages from:

  • Queues.
  • Topic subscriptions.

Service Bus is useful when the solution needs richer broker semantics:

  • Competing consumers.
  • Publish-subscribe.
  • Dead-letter queues.
  • Scheduled delivery.
  • Message deferral.
  • Sessions.
  • Duplicate detection.
  • Transactions.
Code
[Function("ProcessPayment")]
public Task Run(
    [ServiceBusTrigger(
        "payment-commands",
        Connection = "ServiceBusConnection")]
    ServiceBusReceivedMessage message,
    CancellationToken cancellationToken)
{
    return handler.HandleAsync(message, cancellationToken);
}

Current isolated-worker extensions can bind to types from Azure.Messaging.ServiceBus.

Peek-Lock and Settlement

The common processing mode is Peek-Lock:

  1. The consumer receives and locks a message.
  2. Successful execution completes the message.
  3. Failure or lock expiration makes it available again.
  4. Repeated failure can move it to the dead-letter queue.

Automatic lock renewal has limits. Long processing should either complete within the lock-renewal design or be decomposed into durable steps.

Advanced handlers can use ServiceBusMessageActions to complete, abandon, defer, or dead-letter explicitly. When manual settlement is used, automatic completion must be configured consistently.

Service Bus Sessions

Sessions group related messages and enable ordered, exclusive processing for one session at a time. A session ID can represent an order, workflow, tenant, or aggregate.

Sessions help when ordering is required within a group, but they affect scale:

  • One session is processed by one receiver at a time.
  • A hot session can become a bottleneck.
  • Session concurrency and message concurrency require separate tuning.

Do not require global ordering unless the business requirement genuinely needs it.

Service Bus Concurrency

Host settings include values such as:

  • maxConcurrentCalls.
  • maxConcurrentSessions.
  • prefetchCount.
  • maxAutoLockRenewalDuration.

Higher concurrency improves throughput only until CPU, memory, connections, or downstream services saturate. Prefetching can improve throughput but consumes message locks before processing and can increase lock expiration when configured too aggressively.

Queue Storage Versus Service Bus

ConcernQueue StorageService Bus
Simple work queueStrong fitSupported
Topic subscriptionsNoYes
Sessions and ordered groupsNoYes
Duplicate detectionNoYes
Broker transactionsNoYes
Dead-letter featuresPoison queueNative dead-letter queue
Operational complexityLowerHigher
Typical choiceSimple bufferingEnterprise messaging

Both normally provide at-least-once delivery. Consumers must be idempotent.

Event Grid Triggers

Event Grid is a push-based event-routing service. It delivers discrete event notifications from Azure resources, custom topics, domains, or partner sources to subscriptions.

The trigger receives events through a webhook endpoint managed by the Functions integration. The function can bind to:

  • EventGridEvent.
  • CloudEvent.
  • BinaryData.
  • A custom deserializable type.
  • Arrays for batched delivery where supported.

An event subscription is required. Filters can select event type, subject, and other event properties.

Event Grid Is Notification, Not Command Queuing

Event Grid is a good fit for:

  • Blob-created notifications.
  • Resource lifecycle events.
  • Domain event fan-out.
  • Lightweight event-driven integration.

It is not the preferred tool when consumers need:

  • Long retention and pull-based control.
  • Sessions or strict ordered groups.
  • Broker transactions.
  • Command semantics.
  • Complex message settlement.

Use Service Bus for durable brokered commands and workflows. Use Event Grid for event notification and fan-out.

Event Grid Reliability

Event Grid retries failed delivery according to subscription policy and can dead-letter undeliverable events when configured. Delivery is at least once, and ordering is not generally guaranteed.

Consumers should:

  • Deduplicate by event ID or business key.
  • Tolerate out-of-order events.
  • Validate event type and schema version.
  • Make handlers idempotent.
  • Monitor delivery failures and dead-letter destinations.

Returning success before durable processing is secured can lose work. For heavier processing, validate the event, enqueue durable work, and then return success.

Blob Triggers

Blob triggers start a function when a blob is created or updated. Common scenarios include:

  • Image resizing.
  • Document extraction.
  • Malware scanning.
  • Import pipelines.
  • Data transformation.
  • Media processing.

The path can capture binding expressions:

Code
[Function("ProcessInvoice")]
public Task Run(
    [BlobTrigger(
        "incoming-invoices/{name}",
        Source = BlobTriggerSource.EventGrid,
        Connection = "StorageConnection")]
    Stream content,
    string name,
    CancellationToken cancellationToken)
{
    return processor.ProcessAsync(name, content, cancellationToken);
}

Event-Based Versus Polling Blob Triggers

Two Blob trigger implementations exist:

  • Polling-based: Scans storage and uses control queues and receipts.
  • Event-based: Uses Event Grid notifications and has lower detection latency.

Microsoft recommends the event-based implementation. Flex Consumption supports only the event-based Blob trigger.

Use a direct Event Grid trigger instead when only event metadata is needed or when custom routing and filtering are central. Use the Blob trigger when binding directly to the blob content or SDK type is convenient.

Large Blob Processing

Avoid loading a large blob into a byte[] because it can consume substantial memory and reduce per-instance concurrency. Prefer:

  • A stream.
  • BlobClient.
  • Chunked processing.
  • External batch systems for very large or compute-intensive workloads.

Estimate memory as total concurrent executions multiplied by per-execution working set, not just one blob's size.

Blob Idempotency and Reprocessing

Blob updates and event retries can cause repeated processing. Use:

  • Blob URI plus ETag or version ID.
  • A processing-state record with a unique constraint.
  • Deterministic output names.
  • Conditional writes.
  • Safe overwrite or compare-and-swap behavior.

Do not rely solely on trigger receipts as the business guarantee of exactly-once processing.

Managed Identity Connections

Modern binding extensions support identity-based connections. A connection name can represent a group of application settings rather than a connection string.

Use managed identity and grant the narrowest data-plane role:

  • Storage Queue Data Message Processor or appropriate storage data roles.
  • Azure Service Bus Data Receiver.
  • Storage Blob Data Reader or Contributor as required.

Management roles such as Owner do not automatically provide required data-plane access, and they are usually excessive.

Function App Grouping

Functions in one function app share:

  • Deployment package.
  • Configuration and identity.
  • Host runtime and language worker.
  • Hosting plan.
  • host.json settings.
  • Scale behavior, with plan-specific exceptions.
  • Failure and deployment lifecycle.

Separate functions when they need different security boundaries, deployment cadence, resource behavior, or scale limits. Avoid one large function app containing unrelated HTTP APIs, high-volume queues, and memory-heavy blob processors.

Reliable Event Processing

For queue, Service Bus, Event Grid, and Blob triggers:

  1. Assume duplicate delivery.
  2. Validate the message or event.
  3. Establish an idempotency key.
  4. Perform business state changes transactionally where possible.
  5. Publish follow-up events through a reliable pattern such as an outbox.
  6. Throw or fail when processing must be retried.
  7. Isolate poison work after bounded attempts.
  8. Provide replay tooling and audit information.

Logging an exception and returning success can acknowledge work that was not completed.

Observability

Monitor trigger-specific signals:

  • HTTP request rate, latency, status, and authorization failures.
  • Timer lateness, duration, and missed business outcomes.
  • Queue depth, oldest-message age, dequeue count, and poison messages.
  • Service Bus active, dead-lettered, deferred, and locked messages.
  • Event Grid delivery failures and dead-letter events.
  • Blob processing latency, size distribution, duplicates, and failure queues.
  • Function executions, exceptions, retries, scale, and dependency calls.

Include event IDs, message IDs, correlation IDs, subject, queue or subscription, and business identifiers without logging secrets or sensitive payloads.

Common Mistakes

  • Using function keys as user-level authorization.
  • Holding an HTTP request open for long processing.
  • Assuming timer failures retry automatically.
  • Assuming queue or event delivery is exactly once.
  • Ignoring poison queues and dead-letter queues.
  • Increasing concurrency without checking dependencies.
  • Depending on global message ordering.
  • Treating Event Grid as a command queue.
  • Using polling Blob triggers for new low-latency designs.
  • Loading large blobs fully into memory.
  • Swallowing exceptions and accidentally acknowledging failed work.
  • Sharing one function app across unrelated scale and security profiles.
  • Storing connection strings when managed identity is supported.

Practical Best Practices

  • Select triggers by lifecycle and delivery requirements.
  • Keep HTTP work bounded and asynchronous when necessary.
  • Make all event handlers idempotent.
  • Tune concurrency through end-to-end load testing.
  • Monitor poison and dead-letter destinations.
  • Prefer managed identity and least privilege.
  • Use Service Bus only when its richer features provide value.
  • Prefer event-based Blob triggers.
  • Stream large payloads.
  • Separate functions with different operational profiles.
  • Test retries, duplicates, lock loss, and replay.
  • Keep extension packages current and compatible with the worker model.

Interview Practice

PreviousFunction chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensationNext UpIn-process vs isolated worker model for .NET Functions