Azure Functions Isolated Worker Model
.NET 8+ Separate Process Execution
Introduction
The Isolated Worker Model runs Azure Functions in a separate process from the Azure Functions host, providing complete isolation, independent .NET version control, and full dependency injection support. This model is the recommended approach for .NET functions in production, especially when you need greater control over the execution environment or want to use the latest .NET features.
This comprehensive guide covers:
- Model comparison — In-process vs isolated worker
- Project setup — Creating isolated worker projects
- Programming model — Writing functions with HttpRequestData
- Dependency injection — Configuring services and middleware
- Trigger bindings — Using queues, blobs, Service Bus
- Best practices — Production deployment guidance
Understanding the Architecture
In-Process vs Isolated Worker
┌─────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURE COMPARISON │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ IN-PROCESS MODEL │
│ ─────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ AZURE FUNCTIONS HOST │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ YOUR FUNCTION CODE │ │ │
│ │ │ (Runs in same process) │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ - .NET version = Host .NET version │ │
│ │ - Limited DI support │ │
│ │ - Shared memory, tight coupling │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ISOLATED WORKER MODEL │
│ ────────────────────── │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────────┐ │
│ │ FUNCTIONS HOST │ │ WORKER PROCESS │ │
│ │ (Azure runtime) │────▶│ (.NET 8 separate process) │ │
│ │ │ │ │ │
│ │ - Triggers │ │ ┌───────────────────────────┐ │ │
│ │ - Bindings │ │ │ YOUR FUNCTION CODE │ │ │
│ │ - Scaling │ │ │ - Full DI support │ │ │
│ │ │ │ │ - Custom middleware │ │ │
│ │ │ │ │ - Independent .NET │ │ │
│ └─────────────────────┘ │ └───────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Communication Flow
┌─────────────────────────────────────────────────────────────────────┐
│ ISOLATED WORKER COMMUNICATION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────┐ │
│ │ Trigger Event │ │
│ │ (Queue, HTTP, │ │
│ │ Timer, etc.) │ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ gRPC │
│ │ Functions Host │◀──────────────────┐ │
│ │ │ │ │
│ │ - Receives event │ │ │
│ │ - Manages scaling │ ▼ │
│ │ - Handles binding │ ┌────────────────────────┐ │
│ └─────────┬──────────┘ │ Worker Process │ │
│ │ │ │ │
│ │ │ - Executes function │ │
│ │ │ - Returns result │ │
│ │ │ - Handled by worker │ │
│ │ └────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Function Result │ │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Create Isolated Worker Project
Initialize Project
# Create new isolated worker function app
func init --worker-runtime dotnet-isolated --name MyFunctionApp
# Or with .NET CLI
dotnet new worker -n MyFunctionApp -f net8.0
# Add Functions SDK
cd MyFunctionApp
dotnet add package Microsoft.Azure.Functions.Worker
dotnet add package Microsoft.Azure.Functions.Worker.Sdk
Project Structure
MyFunctionApp/
├── Program.cs
├── host.json
├── local.settings.json
├── MyFunctionApp.csproj
├── Properties/
│ └── launchSettings.json
└── Functions/
├── HttpFunction.cs
├── QueueFunction.cs
└── TimerFunction.cs
.csproj Configuration
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Program.cs Configuration
Basic Setup
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
// Add your services here
services.AddSingleton<IMyService, MyService>();
services.AddHttpClient();
})
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.Build();
await host.RunAsync();
Advanced Configuration
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
// Add middleware
builder.UseMiddleware<LoggingMiddleware>();
builder.UseMiddleware<ExceptionHandlingMiddleware>();
})
.ConfigureServices((context, services) =>
{
// Configuration
services.AddOptions<MyOptions>()
.Configure(options =>
{
options.Setting = context.Configuration["MySetting"];
});
// Database context
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(context.Configuration.GetConnectionString("Default")));
// Business services
services.AddScoped<IOrderService, OrderService>();
services.AddSingleton<ICacheService, MemoryCacheService>();
// HTTP clients
services.AddHttpClient<IExternalApiClient, ExternalApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});
// Mediator for CQRS
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
})
.Build();
await host.RunAsync();
Write Functions
HTTP Trigger Function
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
public class HttpFunction
{
private readonly IOrderService _orderService;
private readonly ILogger<HttpFunction> _logger;
public HttpFunction(IOrderService orderService, ILogger<HttpFunction> logger)
{
_orderService = orderService;
_logger = logger;
}
[Function("HttpTrigger")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_logger.LogInformation("HTTP trigger processed a request");
var response = req.CreateResponse(HttpStatusCode.OK);
switch (req.Method)
{
case "GET":
var orders = await _orderService.GetOrdersAsync();
await response.WriteAsJsonAsync(orders);
break;
case "POST":
var requestBody = await req.ReadAsStringAsync();
var order = await _orderService.CreateOrderAsync(requestBody);
response = req.CreateResponse(HttpStatusCode.Created);
await response.WriteAsJsonAsync(order);
break;
default:
response = req.CreateResponse(HttpStatusCode.MethodNotAllowed);
break;
}
return response;
}
}
Queue Trigger Function
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
public class QueueFunction
{
private readonly IOrderProcessor _orderProcessor;
private readonly ILogger<QueueFunction> _logger;
public QueueFunction(IOrderProcessor orderProcessor, ILogger<QueueFunction> logger)
{
_orderProcessor = orderProcessor;
_logger = logger;
}
[Function("ProcessOrder")]
public async Task Run(
[QueueTrigger("orders-queue", Connection = "StorageConnection")] string message)
{
_logger.LogInformation("Processing queue message: {Message}", message);
try
{
var order = JsonSerializer.Deserialize<Order>(message, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
await _orderProcessor.ProcessOrderAsync(order);
_logger.LogInformation("Order {OrderId} processed successfully", order?.OrderId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to process order");
throw;
}
}
}
Service Bus Trigger Function
using Microsoft.Azure.Functions.Worker;
using Azure.Messaging.ServiceBus;
public class ServiceBusFunction
{
private readonly ILogger<ServiceBusFunction> _logger;
public ServiceBusFunction(ILogger<ServiceBusFunction> logger)
{
_logger = logger;
}
[Function("ServiceBusTrigger")]
public async Task Run(
[ServiceBusTrigger("orders-queue", Connection = "ServiceBusConnection")]
ServiceBusReceivedMessage message)
{
_logger.LogInformation("Service Bus message received: {MessageId}", message.MessageId);
var body = message.Body.ToString();
_logger.LogInformation("Message body: {Body}", body);
// Process the message
// ...
// Message is auto-completed by default
}
}
Timer Trigger Function
using Microsoft.Azure.Functions.Worker;
public class TimerFunction
{
private readonly ILogger<TimerFunction> _logger;
public TimerFunction(ILogger<TimerFunction> logger)
{
_logger = logger;
}
[Function("ScheduledTask")]
public async Task Run(
[TimerTrigger("0 0 * * * *")] TimerInfo timerInfo)
{
_logger.LogInformation("Timer executed at: {Time}", DateTime.UtcNow);
_logger.LogInformation("Next timer run: {NextRun}", timerInfo.ScheduleStatus.Next);
// Perform scheduled task
}
}
Dependency Injection
Register Services
// Program.cs
var host = new HostBuilder()
.ConfigureServices((context, services) =>
{
// Singleton - one instance for entire app lifetime
services.AddSingleton<ISingletonService, SingletonService>();
// Scoped - one instance per request/session
services.AddScoped<IScopedService, ScopedService>();
// Transient - new instance each time
services.AddTransient<ITransientService, TransientService>();
// Database context
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(context.Configuration.GetConnectionString("Default")));
// HttpClient with delegation
services.AddHttpClient<IApiClient, ApiClient>()
.ConfigureHttpClient(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
})
.AddHttpMessageHandler<CustomHandler>();
// Options pattern
services.Configure<FeatureFlags>(context.Configuration.GetSection("FeatureFlags"));
})
.Build();
// Use in functions
public class MyFunction
{
public MyFunction(IScopedService scopedService, ISingletonService singletonService)
{
// Services injected via constructor
}
}
Middleware
// Custom middleware
public class CustomMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(
FunctionContext context,
FunctionExecutionDelegate next)
{
var logger = context.GetLogger<CustomMiddleware>();
logger.LogInformation("Before function execution");
await next(context);
logger.LogInformation("After function execution");
}
}
// Register middleware
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder.UseMiddleware<CustomMiddleware>();
})
.Build();
Binding Configuration
Input/Output Bindings
public class BlobFunction
{
[Function("ProcessBlob")]
public async Task Run(
[BlobTrigger("input/{name}", Connection = "StorageConnection")] BlobClient inputBlob,
[Blob("output/{name}", FileAccess.Write, Connection = "StorageConnection")] BlobClient outputBlob,
string name)
{
// Read from input blob
var downloadResponse = await inputBlob.DownloadContentAsync();
var content = downloadResponse.Value.Content.ToString();
// Process and write to output blob
var processed = ProcessContent(content);
await outputBlob.UploadAsync(BinaryData.FromString(processed));
}
}
Cosmos DB Binding
public class CosmosFunction
{
[Function("GetDocument")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestData req,
[CosmosDB("database", "collection",
Id = "{id}",
PartitionKey = "{partition}")]
MyDocument document)
{
var response = req.CreateResponse(document != null
? HttpStatusCode.OK
: HttpStatusCode.NotFound);
await response.WriteAsJsonAsync(document);
return response;
}
[Function("CreateDocument")]
[return: CosmosDB("database", "collection")]
public MyDocument Create(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
// Create and return document
return new MyDocument { Id = Guid.NewGuid().ToString() };
}
}
Best Practices
Configuration Checklist
| Practice | Description |
|---|---|
| Use DI for all services | Don't instantiate services manually |
| Register at appropriate lifetime | Singleton, Scoped, or Transient |
| Use ILogger<T> | Get typed logger for better filtering |
| Handle exceptions | Catch and log errors properly |
| Configure retry policy | For queue/Service Bus triggers |
Logging
public class FunctionWithLogging
{
private readonly ILogger<FunctionWithLogging> _logger;
public FunctionWithLogging(ILogger<FunctionWithLogging> logger)
{
_logger = logger;
}
[Function("Process")]
public async Task Run([QueueTrigger("queue")] string message)
{
_logger.LogInformation("Processing message: {MessageLength}", message?.Length);
try
{
// Process message
_logger.LogInformation("Message processed successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing message");
throw;
}
}
}
Related Topics
- Error Handling — Error handling patterns
- Managed Identity — Secure authentication
- Performance Tuning — Optimize function performance
Azure Integration Hub - Intermediate Level