Azure .NET 8 Isolated Function AppSettings Configuration
WARNING
This article addresses a critical breaking change when migrating Azure Functions from .NET 6 (in-process) to .NET 8 (isolated worker model). Key configuration behaviors change significantly in production environments.
Problem Statement
Azure Functions running on .NET 8 (isolated worker model) fail to load appsettings.json
configurations when deployed to Azure, despite working locally. Configuration objects and nested properties defined in appsettings.json
return null
values in production environments, while environment variable overrides still function. The root cause is an incorrect base path resolution in Azure's Linux environment, leading to the configuration builder looking for appsettings.json
in the wrong directory.
Core Symptoms
- Configuration loads locally but fails in Azure PaaS environments
IOptions<T>
properties returnnull
values- Environment variable overrides still work (e.g.,
DummyOption__Foo
) - Confirmed file existence in deployment directory
Recommended Solution
Use the assembly execution path to reliably locate appsettings.json
across environments. This approach works for Linux/Windows and all hosting plans (Consumption/App Service):
1. Create Configuration Helper
// Startup/ConfigHelper.cs
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace YourNamespace.Startup;
internal static class ConfigHelper
{
public static void ConfigureAppConfig(
HostBuilderContext context,
IConfigurationBuilder builder)
{
builder.SetBasePath(GetApplicationBasePath());
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
// Environment-specific configuration
builder.AddJsonFile(
$"appsettings.{context.HostingEnvironment.EnvironmentName}.json",
optional: true);
builder.AddEnvironmentVariables();
if (context.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>(optional: true);
}
}
private static string GetApplicationBasePath()
{
var assemblyPath = Assembly.GetExecutingAssembly().Location;
return Path.GetDirectoryName(assemblyPath) ?? Directory.GetCurrentDirectory();
}
}
2. Program.cs Implementation
// Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureAppConfiguration(ConfigHelper.ConfigureAppConfig)
.ConfigureServices((context, services) =>
{
// Bind configuration
services.Configure<DummyOption>(
context.Configuration.GetSection(nameof(DummyOption)));
})
.Build();
host.Run();
3. Project File Configuration
<!-- .csproj -->
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
Why This Works
Assembly.GetExecutingAssembly().Location
reliably locates the DLL directory- Not dependent on Azure hosting implementation details
- Works for both Windows and Linux environments
- Compatible with all hosting plans (Consumption/App Service)
Alternative Solution: Azure-Friendly Path Hardcoding
For Linux deployments where assembly approach isn't feasible, use conditional path logic:
// Program.cs
builder.SetBasePath(true switch
{
_ when context.HostingEnvironment.IsDevelopment() => Directory.GetCurrentDirectory(),
_ when Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") != null
=> "/home/site/wwwroot",
_ => Directory.GetCurrentDirectory()
});
When to Use This Approach
- Projects not requiring cross-host flexibility
- Linux-specific deployments
- When
Assembly
methods are restricted
Path Reliability Warning
Hardcoded paths like /home/site/wwwroot
may break in future Azure updates. Assembly-based solution is preferred for long-term stability.
Explanation of Failure
Azure Functions Isolated Worker has different runtime behaviors across environments:
Path Source | Local Result | Azure Result | Issue |
---|---|---|---|
HostingEnvironment.ContentRootPath | Project directory | /azure-functions-host | Host runtime config |
Directory.GetCurrentDirectory() | Project directory | /tmp/functions/standby/wwwroot | Linux consumption temporary path |
Assembly Location | /bin/Debug/net8.0 | /home/site/wwwroot | Consistent location |
Environment Variables Take Precedence
Azure portal environment variables always override appsettings.json
values. For nested objects, use object binding via configuration.GetSection()
instead of flattening to environment variables.
Deployment Verification
Confirm file placement in Azure:
- Use Kudu Console or Azure App Service Editor
- Navigate to
/home/site/wwwroot
- Verify
appsettings.json
exists alongside DLLs - Check file contents with
cat appsettings.json
# Azure deployment file structure
/home/site/wwwroot/
├── YourFunctionApp.dll
├── host.json
├── appsettings.json
└── bin/
Key Takeaways
- Use
Assembly
location for reliable cross-platform configuration - Never rely on
HostingEnvironment.ContentRootPath
in Azure - Confirm Publish settings for
appsettings.json
- Environment-specific files (e.g.,
appsettings.Production.json
) are supported - Local development uses
local.settings.json
automatically (don't deploy this)
Configuration loading for .NET 8 Azure Functions requires explicit path resolution as shown. The assembly-based approach provides the most reliable solution across all hosting environments.