Azure Functions .NET 8 Isolated での appsettings.json 読み込み問題
問題概要
Azure Functions を .NET 6 インプロセスモデルから .NET 8 Isolated ワーカーモデルに移行する際、appsettings.json
ファイルの構成設定がローカル環境では正常に読み込まれるものの、Azure 環境(特に Linux)では読み込まれないという問題が発生します。具体的な症状は以下の通りです:
- ローカル実行時:
appsettings.json
の設定値が正常に読み取られる - Azure デプロイ時:
appsettings.json
の設定値が無視されnull
が返る - 環境変数からの設定は正常に適用される
- ファイルはデプロイ先に存在することを確認済み
根本的な原因は、Azure 環境(特に Linux)での アプリケーションベースパスの差異にあります。インプロセスモデルとは異なり、Isolated モデルでは実行コンテキストが変わります。
解決策
方法 1: ベースパスの明示的指定 (Linux 環境向け)
Azure Linux 環境では、アプリケーションファイルは /home/site/wwwroot
に配置されます。ConfigureAppConfiguration
でベースパスを明示的に設定します。
.ConfigureAppConfiguration((context, builder) =>
{
// Azure 環境ではベースパスを固定
if (!context.HostingEnvironment.IsDevelopment())
{
builder.SetBasePath("/home/site/wwwroot");
}
builder
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
})
特徴:
- Linux 環境で確実に動作
- ホスティングプラン(Consumption/App Service)を問わない
- 開発環境と本番環境を自動切り替え
注意点
/home/site/wwwroot
パスは Azure の Linux ホスティング環境に依存します。将来のプラットフォーム変更に備え、他の方法との併用を推奨します。
方法 2: アセンブリ位置を基準としたパス設定 (クロスプラットフォーム)
現在のアセンブリの場所をベースパスとして使用する方法です。プラットフォーム非依存で動作します。
internal static class Config
{
public static void AddAppSettings(
HostBuilderContext hostContext, IConfigurationBuilder builder)
{
// 現在のアセンブリ位置をベースパスに設定
string assemblyPath = Assembly.GetExecutingAssembly().Location;
string assemblyDir = Path.GetDirectoryName(assemblyPath)
?? Directory.GetCurrentDirectory();
builder.SetBasePath(assemblyDir)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
}
}
.ConfigureAppConfiguration(Config.AddAppSettings)
特徴:
- Windows/Linux の両方で動作
- Azure の内部パス変更に影響されない
- 信頼性が高い
方法 3: 現在の作業ディレクトリを使用 (App Service Plan 向け)
Directory.GetCurrentDirectory()
を使用する簡易的な方法ですが、Consumption プランでは動作しない場合があります。
.ConfigureAppConfiguration((context, builder) =>
{
// 現在の作業ディレクトリを取得
var currentDirectory = Directory.GetCurrentDirectory();
builder.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
})
ホスティングプラン別の挙動
- App Service Plan: 正常動作
- Consumption Plan: 動作しない場合あり
ファイル配置の設定 (csproj)
すべての解決策で共通して必要な appsettings.json
の配置設定:
<ItemGroup>
<!-- appsettings.json のコピー設定 -->
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!-- 開発環境用設定(デプロイ除外) -->
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
<!-- local.settings.json はデプロイしない -->
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
根本原因の解説
Azure Functions の Isolated ワーカーモデルでは、.NET アプリケーションが別プロセスで実行されるため、パスの解決方法がインプロセスモデルとは異なります。特に Linux 環境では、以下のパス差異が問題を引き起こします:
環境 | ContentRootPath | 実際のアプリパス |
---|---|---|
ローカル | プロジェクトルート | プロジェクトルート |
Azure (Windows) | アプリケーションフォルダ | アプリケーションフォルダ |
Azure (Linux) | /azure-functions-host | /home/site/wwwroot |
この差異により、Linux 環境ではデフォルトの ContentRootPath
が間違った場所を参照し、appsettings.json
が見つからなくなります。
ベストプラクティス
環境ごとの設定管理
cs.AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true)
設定値のバリデーション
csservices.AddOptions<DummyOption>() .Bind(configuration.GetSection(nameof(DummyOption))) .ValidateDataAnnotations();
構成ソースの優先順位
cs.AddJsonFile("appsettings.json") // 基本設定 .AddJsonFile($"appsettings.{env}.json", optional: true) // 環境別設定 .AddEnvironmentVariables() // 環境変数(上書き用)
最終的な Program.cs 例
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureAppConfiguration((context, builder) =>
{
// 方法1: Linux環境用ベースパス指定
if (!context.HostingEnvironment.IsDevelopment())
{
builder.SetBasePath("/home/site/wwwroot");
}
// 方法2: アセンブリ基準パス(クロスプラットフォーム)
var assemblyPath = Assembly.GetExecutingAssembly().Location;
var assemblyDir = Path.GetDirectoryName(assemblyPath);
if (!string.IsNullOrEmpty(assemblyDir))
{
builder.SetBasePath(assemblyDir);
}
builder
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
})
.ConfigureServices((context, services) =>
{
var configuration = context.Configuration;
services.Configure<DummyOption>(configuration.GetSection(nameof(DummyOption)));
})
.Build();
host.Run();
まとめ
Azure Functions の .NET 8 Isolated モデルで appsettings.json
を確実に読み込むには、プラットフォーム環境に応じたベースパスの明示的指定が不可欠です。特に Linux 環境では:
- Azure のファイル配置パス (
/home/site/wwwroot
) を直接指定するか - アセンブリの位置を基準としたパス解決を行う
上記いずれかの方法を実施し、csproj ファイルでの配置設定を適切に行うことで、ローカルと Azure 環境の両方で一貫した構成管理が可能になります。