ASP.NET Core アプリを Windows サービスでホストする場合はカレントディレクトリの変更を忘れてはいけない

大人の事情で、ASP.NET Core アプリを Microsoft.AspNetCore.Hosting.WindowsServices を使って Windows サービスでホストしていたんだけど、カレントディレクトリの変更は不要と思ってコードを省略したら、ヒドイ目にあった。

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        
        // これを忘れたらダメ!ゼッタイ!忘れたらヒドイ目にあう、かも。
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            host.RunAsService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .UseStartup<Startup>();
}

ASP.NET Core Web API を IdentityServer4 で保護しようとしていて、appsettings.json に IdentityServer4 のアドレスを書いていた。カレントディレクトリの変更を忘れたばかりに、Windows サービスとして動かしたときだけ appsettings.json が読み込まれず。

IdentityServer4 のアドレスを Microsoft.AspNetCore.Authentication.JwtBearer の構成時に指定できなかったので、 IdentityServer4 から署名キーを入手することができなかったようで、アクセストークンの検証時に The signature key was not found が返ってきてしまっていた。

気付くまで 2 日かかったよ…。

ちなみに、 .NET 3.1 から Microsoft.Extensions.Hosting.WindowsServices が推奨されていて、そっちを使うと UseWindowsService 内でカレントディレクトリを切り替えてくれる。早く .NET Core 3.1 か、.NET 6 に移行しなければ。