DEV_NET_CORE
GET_STARTED
AzureAzure Functions and Durable Functions

Bindings, connection configuration, dependency injection, logging, and local development

Overview

Production Azure Functions applications depend on more than function methods. The Functions host must discover triggers, resolve binding connections, load host configuration, start a language worker, construct application services, and send useful telemetry. Local tools must reproduce enough of that environment to find configuration and binding errors before deployment.

The main configuration layers are:

  • Function attributes that declare triggers and bindings.
  • host.json for host-wide runtime and extension behavior.
  • Azure application settings for deployed environment values.
  • local.settings.json for local-only settings.
  • Worker configuration for application code in .NET isolated.
  • Dependency injection registrations for application and Azure SDK services.
  • Host and worker logging pipelines.

These layers overlap conceptually but are not interchangeable. A custom configuration provider added to the isolated worker can supply values to application code, but it does not automatically supply trigger configuration to the Functions platform or scale controller. Similarly, changing worker log filters does not change host log filters.

For interviews, candidates should explain:

  • When to use bindings versus Azure SDK clients.
  • How connection names resolve to settings.
  • How managed identity connection groups work.
  • Why host and worker configuration are separate.
  • How dependency injection lifetimes behave under concurrency and scale-out.
  • How host and worker telemetry differ.
  • How to run and debug Functions locally.
  • How host.json, local.settings.json, Azurite, Core Tools, and environment parity fit together.

Core Concepts

Triggers, Input Bindings, and Output Bindings

A trigger starts a function. Input bindings provide additional data, and output bindings publish returned data:

Code
public sealed class CopyInvoice
{
    [Function("CopyInvoice")]
    [BlobOutput(
        "processed/{name}",
        Connection = "Storage")]
    public string Run(
        [BlobTrigger(
            "incoming/{name}",
            Connection = "Storage")]
        string content,
        string name)
    {
        return content.Trim();
    }
}

Bindings remove common client setup and serialization code. They are effective for straightforward reads and writes.

When to Use an Azure SDK Client

Use an SDK client when code needs:

  • Transactions or conditional operations.
  • Explicit retry and timeout behavior.
  • Pagination or streaming.
  • Advanced message settlement.
  • Multiple operations against one service.
  • Detailed request options and diagnostics.
  • Dynamic resource selection.
  • Precise control over when an operation occurs.

An output binding is not automatically transactional with a database update or another output. For cross-resource consistency, use an appropriate pattern such as a transactional outbox.

Binding Extensions

Non-HTTP and non-timer bindings require extension packages or an extension bundle. In .NET isolated, packages use namespaces such as:

Code
Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs
Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues
Microsoft.Azure.Functions.Worker.Extensions.ServiceBus
Microsoft.Azure.Functions.Worker.Extensions.EventGrid

Package versions affect:

  • Available settings.
  • Supported Azure SDK types.
  • Identity-based connections.
  • Runtime compatibility.
  • Bug fixes and support lifecycle.

Avoid referencing older combined storage packages together with newer split packages because duplicate binding definitions can conflict.

Binding Expressions

Binding expressions use trigger metadata in paths and values:

Code
[BlobTrigger("incoming/{tenant}/{name}")]
Stream input,
string tenant,
string name

An output binding can reuse those values:

Code
[BlobOutput("processed/{tenant}/{name}")]

Expressions are useful for declarative routing, but complex resource-selection logic is often clearer in application code with an SDK client.

SDK Binding Types

Current isolated-worker extensions can bind some triggers and inputs directly to Azure SDK types such as:

  • BlobClient.
  • QueueMessage.
  • ServiceBusReceivedMessage.
  • EventGridEvent.
  • CloudEvent.

Support depends on extension and Worker package versions. POCO, string, byte[], stream, and BinaryData types remain useful.

Prefer:

  • POCOs for simple business messages.
  • Streams for large content.
  • SDK message types for metadata and settlement.
  • Injected SDK clients for advanced output operations.

host.json

host.json configures the Functions host for the entire function app:

Code
{
  "version": "2.0",
  "functionTimeout": "00:10:00",
  "extensions": {
    "serviceBus": {
      "maxConcurrentCalls": 8,
      "prefetchCount": 16
    }
  },
  "logging": {
    "logLevel": {
      "Host.Results": "Information"
    }
  }
}

Common concerns include:

  • Trigger concurrency.
  • Retry behavior.
  • Extension settings.
  • Function timeout.
  • Health monitoring.
  • Host telemetry.
  • OpenTelemetry mode.

Settings apply to all relevant functions in the app. Separate function apps when workloads need incompatible host settings.

Azure Application Settings

Azure application settings are environment variables exposed to the function app. Use them for:

  • Connection setting names and endpoints.
  • Feature flags.
  • Environment-specific behavior.
  • Runtime and worker settings.
  • Key Vault and App Configuration references.

Application-setting changes can restart the app. Manage them through infrastructure as code and controlled deployment rather than manual portal edits.

Hierarchical Setting Names

Use double underscores for hierarchical settings because they work across Windows and Linux:

Code
Orders__RetryLimit=5
Orders__Endpoint=https://example.internal

The .NET configuration system interprets them as:

Code
{
  "Orders": {
    "RetryLimit": 5,
    "Endpoint": "https://example.internal"
  }
}

Colon separators are not portable across all hosting operating systems.

Connection Name Resolution

A binding's Connection value is a setting name or setting prefix, not usually the secret itself:

Code
[ServiceBusTrigger(
    "orders",
    Connection = "ServiceBus")]

For a connection string, the app might contain:

Code
ServiceBus=<connection-string>

For a managed identity connection, it can contain a group:

Code
ServiceBus__fullyQualifiedNamespace=contoso.servicebus.windows.net

The binding extension recognizes the grouped settings and uses the application's identity.

Managed Identity Connections

Prefer identity-based connections where supported:

  1. Enable a system-assigned or user-assigned identity.
  2. Configure the service endpoint settings.
  3. Assign the required data-plane role.
  4. Reference the setting prefix in the binding.

Examples of narrowly scoped roles include:

  • Azure Service Bus Data Receiver.
  • Azure Service Bus Data Sender.
  • Storage Blob Data Reader.
  • Storage Blob Data Contributor.
  • Storage Queue Data Message Processor.

Subscription Owner or resource Contributor does not necessarily grant required data-plane access and is usually excessive.

User-Assigned Identity Configuration

A user-assigned identity connection can include:

Code
ServiceBus__fullyQualifiedNamespace=contoso.servicebus.windows.net
ServiceBus__credential=managedidentity
ServiceBus__clientId=<client-id>

Verify the exact extension-specific keys. The platform and SDK conventions can differ, and resource-ID selection is not supported by every binding.

Host Storage

AzureWebJobsStorage is used by the Functions host for coordination, keys, logs, trigger state, and other runtime behavior. Durable Functions and several triggers rely heavily on it.

Treat host storage as production infrastructure:

  • Keep it in the same region.
  • Protect it with appropriate redundancy.
  • Monitor throttling and latency.
  • Use separate accounts when scale or isolation warrants it.
  • Grant required identity roles.
  • Do not delete it during normal application cleanup.

Worker Configuration Versus Host Configuration

In .NET isolated:

Code
var builder =
    FunctionsApplication.CreateBuilder(args);

builder.Configuration
    .AddJsonFile(
        "appsettings.json",
        optional: true)
    .AddEnvironmentVariables();

These sources configure application code in the worker. They do not automatically configure:

  • Trigger discovery.
  • Binding connection resolution.
  • Host extension behavior.
  • Scale-controller access.

Trigger and binding settings must remain visible to the Functions platform through supported application settings, Key Vault references, or App Configuration references.

Dependency Injection

.NET isolated uses standard IServiceCollection registration:

Code
var builder =
    FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Services
    .AddOptions<OrderOptions>()
    .BindConfiguration("Orders")
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddScoped<
    IOrderHandler,
    OrderHandler>();

builder.Build().Run();

Validate critical configuration at startup so invalid deployments fail clearly instead of failing after an event arrives.

Service Lifetimes

Use:

  • Singleton: Thread-safe reusable clients and immutable shared services.
  • Scoped: Invocation-oriented units of work.
  • Transient: Lightweight independent services.

One worker instance can run many invocations concurrently. Mutable singletons must be thread-safe. A singleton exists per worker process, not once across all scaled-out instances.

Registering Azure SDK Clients

Use Microsoft.Extensions.Azure:

Code
builder.Services.AddAzureClients(clients =>
{
    clients.AddBlobServiceClient(
        builder.Configuration
            .GetSection("ArchiveStorage"))
        .WithName("archive");
});

For passwordless access, configure the endpoint and credential strategy supported by the registration. Reuse SDK clients because they manage connections and are intended to be long-lived.

HttpClient

Use IHttpClientFactory instead of constructing a new client for every invocation:

Code
builder.Services.AddHttpClient<
    InventoryClient>(client =>
{
    client.Timeout =
        TimeSpan.FromSeconds(10);
});

Configure:

  • Base address.
  • Timeout.
  • Resilience policies.
  • Authentication handlers.
  • Connection lifetime where DNS behavior requires it.

Retries must be bounded and limited to operations that are safe to retry.

Thin Function Adapters

Keep entry points small:

Code
Trigger adapter
  -> deserialize and validate
  -> establish correlation and authorization
  -> call application service
  -> map output

This allows business services to be unit-tested without FunctionContext or a running Functions host.

Structured Logging

Use message templates:

Code
logger.LogInformation(
    "Processing order {OrderId} for tenant {TenantId}",
    orderId,
    tenantId);

Do not interpolate structured values into one string. Structured fields support queries, alerts, and correlation.

Avoid logging:

  • Access tokens.
  • Connection strings.
  • Full sensitive payloads.
  • Passwords or secrets.
  • Unnecessary personal data.

Host Logs and Worker Logs

The Functions host and isolated worker have separate log configuration:

  • host.json controls host-side categories and behavior.
  • Worker code or appsettings.json controls worker categories.

A filter in one layer does not automatically affect the other. Diagnose missing or noisy telemetry by identifying which process emitted it.

Application Insights and OpenTelemetry

Current .NET isolated guidance supports direct telemetry through OpenTelemetry and the Azure Monitor exporter:

Code
builder.Services.AddOpenTelemetry()
    .UseFunctionsWorkerDefaults()
    .UseAzureMonitorExporter();

The Functions host can use OpenTelemetry mode through host.json.

Do not register overlapping telemetry pipelines without understanding whether they duplicate logs, requests, dependencies, or exceptions. Aspire-managed applications should follow the Aspire telemetry model rather than adding conflicting direct integration.

Correlation

Propagate correlation through:

  • HTTP trace headers.
  • Message application properties.
  • Activity and trace context.
  • Durable orchestration instance IDs.
  • Business identifiers.

Log identifiers as structured fields. Correlation should connect the trigger, application handler, dependencies, outputs, retries, and dead-letter processing.

Sampling

Application Insights sampling controls telemetry cost and volume. Excessive sampling can hide rare failures or distort low-volume metrics.

Keep enough telemetry for:

  • Failures and exceptions.
  • Dependency throttling.
  • Long-duration requests.
  • Poison messages.
  • Cold starts.
  • Scale and backlog diagnosis.

Use metrics and alertable aggregates for high-volume health signals.

Local Development Tools

Local Functions development uses a local Functions host. Current options include:

  • Azure Functions Core Tools v4, which is generally available.
  • Azure Functions CLI v5, which is currently preview and does not yet support every language.
  • Visual Studio Functions tools.
  • Visual Studio Code Azure Functions extension.

Use GA Core Tools for production development workflows that require full support unless the team has explicitly accepted preview-tool limitations.

local.settings.json

Local settings commonly look like:

Code
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "ServiceBus__fullyQualifiedNamespace":
      "dev-bus.servicebus.windows.net"
  }
}

The Values collection corresponds to Azure application settings. The file can contain secrets and should normally be excluded from source control.

Commit a redacted example file such as:

Code
local.settings.example.json

Document how developers acquire credentials and create local resources.

Azurite

Azurite emulates supported Azure Storage services locally:

Code
AzureWebJobsStorage=UseDevelopmentStorage=true

It is useful for host storage and many Blob, Queue, and Table scenarios. It is not identical to Azure:

  • Feature support differs.
  • Authentication differs.
  • Network behavior differs.
  • Scale and throttling differ.
  • Some extension features require live Azure services.

Use local emulation for fast feedback and run integration tests against isolated Azure development resources for platform fidelity.

Local Identity

Identity-based SDK connections can use the developer's Azure credential locally. Developers need:

  • A signed-in Azure CLI, IDE, or other supported credential.
  • Data-plane roles on development resources.
  • Separate nonproduction resources.

Do not grant developers broad production access merely to make local execution convenient.

Local Bindings Can Affect Live Services

Core Tools can connect to actual Azure services. A queue or timer trigger can consume real messages, and output bindings can modify real data.

Use:

  • Dedicated development subscriptions or resource groups.
  • Unique queue and subscription names.
  • Disabled triggers when appropriate.
  • Local feature flags.
  • Clear environment naming.

Never point routine local development at production event sources.

Testing Strategy

Use layers:

  • Unit tests for application services.
  • Adapter tests for parsing and output mapping.
  • Local host tests for binding discovery.
  • Emulator tests for supported storage behavior.
  • Azure integration tests for identity, networking, and real services.
  • Deployment smoke tests for configuration and telemetry.

Mocks cannot validate RBAC, private DNS, scale-controller access, or extension behavior.

Configuration Drift

Keep local, test, and production configuration aligned through:

  • Infrastructure as code.
  • Validated options.
  • Versioned host.json.
  • Automated app-setting deployment.
  • Example local settings.
  • Environment-specific integration tests.
  • Startup diagnostics that report nonsecret configuration state.

Avoid manually copying settings between environments.

Common Mistakes

  • Assuming worker configuration configures triggers.
  • Putting secrets in source control.
  • Using connection strings when managed identity is supported.
  • Granting management roles instead of required data-plane roles.
  • Constructing SDK clients per invocation.
  • Using mutable singleton state without synchronization.
  • Expecting host.json filters to control worker logs.
  • Registering duplicate telemetry providers.
  • Logging sensitive payloads.
  • Treating Azurite as exact Azure behavior.
  • Connecting local triggers to production resources.
  • Mocking every boundary and skipping deployed integration tests.
  • Mixing incompatible extension packages.

Practical Best Practices

  • Keep bindings simple and use SDK clients for advanced operations.
  • Use current isolated-worker extension packages.
  • Prefer identity-based connections and least privilege.
  • Keep host and worker configuration responsibilities explicit.
  • Validate options at startup.
  • Reuse HttpClient and Azure SDK clients through DI.
  • Keep function entry points thin.
  • Configure host and worker logs separately.
  • Use structured telemetry and correlation.
  • Keep local secrets out of Git.
  • Use Azurite for fast storage feedback and Azure resources for fidelity.
  • Define settings and role assignments through infrastructure as code.

Interview Practice

PreviousTrade-offs among simplicity, portability, autoscaling, and operational controlNext UpCold starts, hosting plan choices, timeout configuration, and scaling behavior