Files
awesome-copilot/instructions/azure-functions-csharp.instructions.md
Anmol Behl 10fda505b7 Add Azure Functions and Durable Functions instructions for .NET/C# development (#1139)
* feat: add azure-functions-csharp.instructions.md

Added guidelines and best practices for building Azure Functions in C# using the isolated worker model.

* feat: add azure-durable-functions-csharp.instructions.md

Added guidelines and best practices for building Azure Durable Functions in C# using the isolated worker model.

* docs: add config & testing sections to durable functions instructions

Added detailed configuration and testing guidelines for Azure Durable Functions in C#.

* Fix capitalization in applyTo path for local.settings.json

Changed Local.settings.json to local.settings.json in the applyTo directive to match the actual filename used in Azure Functions projects.

* update through npm build
2026-04-10 14:45:17 +10:00

9.2 KiB

description, applyTo
description applyTo
Guidelines and best practices for building Azure Functions in C# using the isolated worker model **/*.cs, **/host.json, **/local.settings.json, **/*.csproj

Azure Functions C# Development

General Instructions

  • Always use the isolated worker model (not the legacy in-process model) for all new Azure Functions projects targeting .NET 6 or later.
  • Use FunctionsApplication.CreateBuilder(args) or HostBuilder in Program.cs for host setup and dependency injection.
  • Decorate function methods with [Function("FunctionName")] and use strongly typed trigger and binding attributes.
  • Keep function methods focused — each function should do one thing and delegate business logic to injected services.
  • Never put business logic directly inside the function method body; extract it into testable service classes registered via DI.
  • Use ILogger<T> injected through the constructor, not ILogger passed as a function parameter, for consistent structured logging.
  • Always use async/await for all I/O-bound operations; never block with .Result or .Wait().
  • Prefer CancellationToken parameters where supported to enable graceful shutdown.

Project Structure and Setup

  • Use the Microsoft.Azure.Functions.Worker and Microsoft.Azure.Functions.Worker.Extensions.* NuGet packages.
  • Register services in Program.cs using builder.Services.Add* extension methods for clean dependency injection.
  • Group related functions into separate classes by domain concern, not by trigger type.
  • Store configuration in local.settings.json for local development; use Azure App Configuration or Application Settings for deployed environments.
  • Never hardcode connection strings or secrets in code; always read from IConfiguration or environment variables.
  • Use Key Vault references (@Microsoft.KeyVault(SecretUri=...)) in App Settings for secrets in deployed environments.
  • Use Managed Identity (DefaultAzureCredential) for authenticating to Azure services — avoid connection strings with keys wherever possible.
  • Keep host.json tuned per trigger type: configure maxConcurrentCalls, batchSize, and retry policies at the host level.

Triggers

  • HttpTrigger: Use AuthorizationLevel.Function or higher for production endpoints; reserve AuthorizationLevel.Anonymous only for public-facing APIs with explicit justification. Use ASP.NET Core integration (UseMiddleware, IActionResult returns) when using the ASP.NET Core integration model.
  • TimerTrigger: Use NCRONTAB expressions ("0 */5 * * * *") for schedules; avoid RunOnStartup = true in production as it executes immediately on every cold start.
  • QueueTrigger / ServiceBusTrigger: Configure MaxConcurrentCalls, dead-letter policies, and MaxDeliveryCount in host.json and Azure portal; handle ServiceBusReceivedMessage directly for advanced message control (complete, abandon, dead-letter).
  • BlobTrigger: Prefer Event Grid-based blob triggers (Microsoft.Azure.Functions.Worker.Extensions.EventGrid) over polling-based blob triggers for lower latency and reduced storage transaction costs.
  • EventHubTrigger: Set cardinality to many for batch processing; use EventData[] or string[] parameter types for batch mode; always checkpoint using the EventHubTriggerAttribute's built-in checkpointing.
  • CosmosDBTrigger: Use the change feed trigger for event-driven processing of Cosmos DB changes; set LeaseContainerName and manage lease containers separately from data containers.

Input and Output Bindings

  • Use input bindings to read data declaratively rather than using SDKs directly inside function bodies where the binding covers the use case.
  • For multiple output bindings, define a custom return type with properties annotated with the appropriate output binding attributes (e.g., [QueueOutput], [BlobOutput], [HttpResult]).
  • Use [BlobInput] and [BlobOutput] for blob read/write; prefer Stream over byte[] for large blobs to avoid memory pressure.
  • Use [CosmosDBInput] for point reads and simple queries; for complex queries, inject CosmosClient via DI with Managed Identity.
  • Use [ServiceBusOutput] for single-message sends; inject ServiceBusSender via DI for batching or advanced send scenarios.
  • Avoid mixing SDK clients obtained via DI with binding-based I/O for the same resource — choose one pattern per resource to maintain consistency.

Dependency Injection and Configuration

  • Register all external clients (e.g., BlobServiceClient, ServiceBusClient, CosmosClient) as singletons using services.AddAzureClients() from the Azure.Extensions.AspNetCore.Configuration.Secrets package with DefaultAzureCredential.
  • Use IOptions<T> or IOptionsMonitor<T> for strongly typed configuration sections.
  • Avoid using static state in functions; all shared state should flow through DI-registered services.
  • Register HttpClient instances via IHttpClientFactory to manage connection pooling and avoid socket exhaustion.

Error Handling and Retry

  • Configure built-in retry policies in host.json using "retry" with fixedDelay or exponentialBackoff strategy for trigger-level retries.
  • For transient fault handling at the code level, use Microsoft.Extensions.Http.Resilience or Polly v8 (ResiliencePipeline) with retry, circuit breaker, and timeout strategies.
  • Always catch specific exceptions and log them with structured context (e.g., correlation ID, input identifier) before re-throwing or dead-lettering.
  • Use dead-letter queues for messages that fail after all retries; never silently swallow exceptions in function handlers.
  • For HTTP triggers, return appropriate IActionResult types (BadRequestObjectResult, NotFoundObjectResult) rather than throwing exceptions for expected error conditions.

Observability and Logging

  • Use ILogger<T> with structured log properties: _logger.LogInformation("Processing message {MessageId}", messageId).
  • Configure Application Insights via builder.Services.AddApplicationInsightsTelemetryWorkerService() and builder.Logging.AddApplicationInsights() in Program.cs.
  • Use TelemetryClient for custom events, metrics, and dependency tracking beyond what is automatically collected.
  • Set appropriate log levels in host.json under "logging" to avoid excessive telemetry costs in production.
  • Use Activity and ActivitySource from System.Diagnostics for distributed tracing context propagation between functions and downstream services.
  • Avoid logging sensitive data (PII, secrets, connection strings) in any log statement.

Performance and Scalability

  • Keep function startup time minimal: defer expensive initialization to lazy-loaded singletons, not the function constructor.
  • Use the Consumption plan for event-driven, unpredictable workloads; use Premium or Dedicated plans for low-latency, high-throughput, or VNet-integrated scenarios.
  • For CPU-intensive work, offload to a background Task or use Durable Functions rather than blocking the function host thread.
  • Batch operations where possible: process IEnumerable<EventData> or ServiceBusReceivedMessage[] arrays in a single function invocation rather than one message at a time.
  • Set FUNCTIONS_WORKER_PROCESS_COUNT and maxConcurrentCalls appropriately for the hosting plan and expected throughput.
  • Enable WEBSITE_RUN_FROM_PACKAGE=1 in App Settings for faster cold starts by running directly from a deployment package.

Security

  • Always validate and sanitize HTTP trigger inputs before processing; use FluentValidation or Data Annotations.
  • Use AuthorizationLevel.Function with function keys stored in Key Vault for internal API-to-API calls.
  • Integrate Azure API Management (APIM) in front of HTTP-triggered functions for public-facing APIs to handle auth, rate limiting, and routing.
  • Restrict inbound access using App Service networking features (IP restrictions, Private Endpoints) for sensitive functions.
  • Never log request bodies containing PII or secrets.

Testing

  • Unit-test service classes independently of the function host using standard xUnit/NUnit with mocked dependencies.
  • Integration-test functions using Azurite (local Azure Storage emulator) and TestServer or the Azure Functions Core Tools.
  • Use the Microsoft.Azure.Functions.Worker.Testing helpers where available to construct mock FunctionContext instances.
  • Avoid testing the trigger plumbing itself; focus tests on the business logic extracted into services.

Existing Code Review Guidance

  • If a project uses the legacy in-process model (FunctionsStartup, IWebJobsStartup), suggest migrating to the isolated worker model and provide the migration path via dotnet-isolated-process-guide.
  • If hardcoded connection strings or storage account keys are found in code or config files, flag them and suggest replacing with DefaultAzureCredential and Key Vault references.
  • If RunOnStartup = true is set on a TimerTrigger in a production app, flag it as a risk and suggest using deployment slots or feature flags instead.
  • If async void is used in any function, flag it immediately — use async Task instead.
  • If retry logic is implemented manually with Thread.Sleep or Task.Delay inside a function, suggest replacing with host-level retry policies or Polly resilience pipelines.