Azure Functions Input & Output Bindings

Connecting to Azure Services Without SDK Code


Introduction

Azure Functions bindings provide a declarative way to connect your function code to external services without writing boilerplate SDK code. By simply adding attributes to your function parameters, you can read from or write to Azure Storage, Service Bus, Cosmos DB, and many other services.

This comprehensive guide covers:

  • Input bindings — Reading data from external sources
  • Output bindings — Writing data to external destinations
  • Multiple bindings — Using multiple bindings in one function
  • Custom bindings — Creating your own bindings
  • Best practices — Efficient and secure binding usage

Understanding Bindings

How Bindings Work

┌─────────────────────────────────────────────────────────────────────┐
│                    BINDING ARCHITECTURE                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌──────────────────────────────────────────────────────────────┐  │
│   │                    AZURE FUNCTION                            │  │
│   │                                                              │  │
│   │  [Blob("data/{name}")] ← INPUT BINDING                       │  │
│   │         │                                                    │  │
│   │         │  Function Logic                                    │  │
│   │         ▼                                                    │  │
│   │  [Queue("output")] ← OUTPUT BINDING                          │  │
│   │                                                              │  │
│   └────────────────────────────┬─────────────────────────────────┘  │
│                                │                                    │
│                                ▼                                    │
│   ┌──────────────────────────────────────────────────────────────┐  │
│   │                   BINDING CONTAINER                          │  │
│   │                                                              │  │
│   │   ┌──────────┐    ┌──────────┐    ┌──────────┐               │  │
│   │   │   Blob   │    │   Queue  │    │ CosmosDB │               │  │
│   │   │  Storage │    │   Service │    │          │              │  │
│   │   └──────────┘    └──────────┘    └──────────┘               │  │
│   └──────────────────────────────────────────────────────────────┘  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Binding Types

TypeDescriptionExamples
TriggerStarts function executionQueue message, HTTP request, Timer
InputReads data into functionBlob content, Table entity, Cosmos DB document
OutputWrites data from functionBlob storage, Queue message, HTTP response

Input Bindings

Blob Storage Input

[FunctionName("ReadBlob")]
public async Task<string> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req,
    [Blob("containers/{name}")] BlobClient blob)
{
    var content = await blob.DownloadContentAsync();
    return content.Value.Content.ToString();
}

Table Storage Input

[FunctionName("GetOrder")]
public IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    [Table("orders", "2024-01", "order-123")] OrderEntity order)
{
    return new OkObjectResult(order);
}

Cosmos DB Input

[FunctionName("GetDocument")]
public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    [CosmosDB("database", "collection", 
        Id = "{id}", 
        PartitionKey = "{partition}")] 
    MyDocument document)
{
    return document == null
        ? new NotFoundResult()
        : new OkObjectResult(document);
}

Multiple Input Bindings

[FunctionName("ProcessData")]
public async Task Run(
    [QueueTrigger("orders")] OrderMessage order,
    [Blob("archive")] BlobContainerClient archiveContainer,
    [Table("orders")] TableClient tableClient)
{
    // Use both bindings
    await archiveContainer.CreateIfNotExistsAsync();
    await tableClient.CreateIfNotExistsAsync();
}

Output Bindings

Blob Storage Output

[FunctionName("WriteBlob")]
public async Task Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    [Blob("output/{DateTime.UtcNow:yyyy-MM-dd}/result.txt", FileAccess.Write)] Stream output)
{
    await req.Body.CopyToAsync(output);
}

Queue Output

[FunctionName("ProcessAndQueue")]
public async Task Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
    [Queue("processed-orders")] IAsyncCollector<string> queueCollector)
{
    var order = await req.Body.ReadAsStringAsync();
    await queueCollector.AddAsync(order);
}

Table Storage Output

[FunctionName("SaveToTable")]
[return: Table("orders")]
public OrderEntity CreateOrder(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
{
    var order = new OrderEntity
    {
        PartitionKey = DateTime.UtcNow.ToString("yyyy-MM"),
        RowKey = Guid.NewGuid().ToString(),
        CustomerName = "John Doe",
        Total = 99.99
    };
    return order;
}

Cosmos DB Output

[FunctionName("CreateDocument")]
[return: CosmosDB("database", "collection")]
public MyDocument CreateDoc(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
{
    return new MyDocument
    {
        Id = Guid.NewGuid().ToString(),
        Data = "Sample data"
    };
}

HTTP Response Output

[FunctionName("GetResponse")]
public IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req)
{
    return new OkObjectResult(new { status = "success" });
}

Multiple Bindings Example

Complete Function with Multiple Bindings

[FunctionName("ProcessOrderWorkflow")]
public async Task Run(
    // Trigger
    [ServiceBusTrigger("orders", Connection = "ServiceBusConnection")] 
    ServiceBusReceivedMessage message,
    
    // Output - Archive to blob
    [Blob("archive/orders/{DateTime.UtcNow:yyyy-MM-dd}", FileAccess.Write, Connection = "StorageConnection")] 
    Stream archiveStream,
    
    // Output - Dead letter queue
    [Queue("deadletter", Connection = "StorageConnection")] 
    IAsyncCollector<ServiceBusMessage> deadLetterQueue,
    
    // Output - Results table
    [Table("results", Connection = "StorageConnection")] 
    TableClient resultsTable,
    
    ILogger log)
{
    log.LogInformation("Processing order...");
    
    try
    {
        var order = JsonSerializer.Deserialize<Order>(message.Body);
        
        // Process the order (business logic)
        var result = await ProcessOrderAsync(order);
        
        // Write to archive
        await archiveStream.WriteAsync(message.Body);
        
        // Add to results table
        await resultsTable.AddEntityAsync(new TableEntity
        {
            RowKey = order.OrderId,
            PartitionKey = DateTime.UtcNow.ToString("yyyy-MM-dd"),
            ["Status"] = "Processed",
            ["Result"] = JsonSerializer.Serialize(result)
        });
        
        log.LogInformation("Order processed successfully");
    }
    catch (Exception ex)
    {
        log.LogError(ex, "Failed to process order");
        
        // Send to dead letter
        var deadLetter = new ServiceBusMessage(message.Body);
        deadLetter.ApplicationProperties["Error"] = ex.Message;
        await deadLetterQueue.AddAsync(deadLetter);
    }
}

Binding Expressions

Using Query Parameters

[FunctionName("GetFile")]
public async Task<string> Run(
    [HttpTrigger("get", Route = "files/{filename}")] HttpRequest req,
    [Blob("documents/{filename}")] string content)
{
    return content;
}

Using Headers

[FunctionName("GetByHeader")]
public async Task<IActionResult> Run(
    [HttpTrigger("get")] HttpRequest req,
    [Blob("documents", Connection = "Storage")] BlobContainerClient container,
    ILogger log)
{
    var filename = req.Headers["X-Filename"];
    var blob = container.GetBlobClient(filename);
    var content = await blob.DownloadContentAsync();
    return new OkObjectResult(content.Value.Content);
}

Using System Properties

[FunctionName("SystemProperties")]
public void Run(
    [QueueTrigger("orders", Connection = "QueueConnection")] string message,
    [Blob("audit", FileAccess.Write)] Stream auditLog,
    ExecutionContext context,
    ILogger log)
{
    // Access system properties
    log.LogInformation($"Function directory: {context.FunctionDirectory}");
    log.LogInformation($"Function name: {context.FunctionName}");
    log.LogInformation($"Invocation ID: {context.InvocationId}");
}

Configuration

Local Settings (local.settings.json)

{
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "ServiceBusConnection": "Endpoint=sb://...",
    "StorageConnection": "DefaultEndpointsProtocol=https;AccountName=..."
  }
}

Application Settings in Azure

# Set connection strings
az functionapp config appsettings set \
  --name my-function-app \
  --resource-group my-rg \
  --settings "StorageConnection=@Microsoft.KeyVault(...)"

Best Practices

PracticeDescription
Use binding expressionsKeep URLs and parameters flexible
Handle missing dataCheck for null in input bindings
Connection poolingUse singleton clients where possible
Error handlingUse try/catch for all binding operations
LoggingLog binding operations for debugging

Performance Tips

// Good: Use BlobClient for multiple operations
[Blob("container/{name}")] BlobClient blob

// Better: Read directly for single reads
[Blob("container/file.txt")] string content

Related Topics


Azure Integration Hub - Intermediate Level