【.NET Core】ConfigurationBuilderを使ってC#で設定ファイルや環境変数を扱う
2019-07-17
azblob://2022/11/11/eyecatch/2019-7-11-how-to-configuration-builder-000.jpg

こんにちは、FintechでAzure DevOpsなどを触っている和田です。最近大三元を両面待ちでテンパイしてテンションが上がりましたが、直後に切った牌をタンヤオに潰されて拗ねました。今回はConfigurationBuilderを使って環境変数などから.NET Coreアプリケーションに値を読み込む方法をまとめておきます。



ConfigurationBuilderとは

ConfigurationBuilderは、.NET Coreで利用できる、外部から設定をキー・バリューの形で参照できるライブラリです。扱いたいデータソースごとにプロバイダーが用意されています。既に用意されているデータソースは以下の通りです。

  • Azure Key Vault
  • コマンド ライン引数
  • ディレクトリ内ファイル
  • 環境変数
  • In-memory .NET オブジェクト
  • 設定ファイル
  • User Secrets(Secret Manager)

上記以外のデータソースから読み込みたい場合は、カスタムプロバイダーを利用して自分で構成することが可能です。また、優先順位を設定し、複数のデータソースを組み合わせて読み込むこともできます。

User Secretsについて

User Secretsを使うと、アプリの設定をユーザーディレクトリに生成されるプロジェクトごとに紐付けられたsecrets.jsonで管理できるようになります。Gitリポジトリに設定値を含めたくないけど、環境変数を汚したくないみたいな場合に便利です。Secretsって名前なので勘違いされやすいみたいですが、暗号化はされずに平文で格納されているので、100%安全になるものではないです。開発環境のみで使用するようにしてください。

なぜConfigurationBuilderが必要なのか

当たり前すぎる話ではありますが、The Twelve-Factor Appでも述べられている通り、アプリケーションの設定は環境によって変化するため、コードから分離されていなければなりません。また、設定には外部サービスの認証情報などのセンシティブな情報が含まれており、これらはセキュリティ上の観点からもコード(とそのGitリポジトリ)に含めるのは避けなければいけません。これを.NET Coreで実現するのがConfigurationBuilderです。

階層構造な設定ファイルのキー指定

ConfigurationBuilderは、キー・バリューの形で値を持っています。ですが、JSONなどのデータソースは階層構造になっています。

{
  "Hello": {
    "World": "Hello, world!"
  },
  "Category": {
    "Contents": [
      "content0",
      "content1",
      "content2"
    ]
  }
}

この場合、区切り文字に:を使って参照します。

  • "Hello:World" => "Hello, world!"

配列要素は、インデックスの数字が入ります。

  • "Category:Contents:0" => "content0"
  • "Category:Contents:1" => "content1"

環境変数に設定する場合、Bashなど一部環境ではキーに:を使う事ができないので、すべての環境で使える代替表現として__(アンダースコア2個)が推奨されています。

使い方

サンプルコードを見ていきましょう。今回は

  • 設定ファイル(JSON)
  • User Secrets(Secret Manager)
  • 環境変数

を扱う例を説明します。ASP.NET Coreプロジェクトの場合は微妙に扱い方が異なるため、別にコードを示します。

ASP.NET Coreプロジェクト以外の場合

NuGetでMicrosoft.Extensions.Configurationをインストールします。

あとはMicrosoft.Extensions.Configuration.*のうち、必要なプロバイダーをインストールしてください。今回は次の4つをインストールします。

実際のコードは次のようになります。

using System;
using System.IO;
using Microsoft.Extensions.Configuration;

namespace ConfigrationBuilderTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile(path: "appsettings.json")
                .AddEnvironmentVariables(prefix: "DOTNET_")
                .AddUserSecrets<Program>(optional: true);
            var configuration = builder.Build();

            Console.WriteLine(configuration["Hello:World"]);
            Console.WriteLine(configuration["Category:Contents:0"]);
            Console.WriteLine(configuration["Category:Contents:1"]);
            Console.WriteLine(configuration["Category:Contents:2"]);
        }
    }
}

プロジェクトのルートディレクトリにappsettings.jsonを作成してください。appsettings.jsonは、VS上で右クリックしてファイルのプロパティを開き、出力ディレクトリにコピー常にコピー新しい場合はコピーに設定してください。

User Secretsを扱う設定も必要です。csprojファイルにUserSecretsIdを追加してください。通常は任意のGUIDを指定します(ってドキュメントに書いてありましたが別にGUIDじゃない文字列でも使用できました)。GUIDはVisual Studioならツールメニューから作成できます。複数プロジェクトで同じUser Secretsを使いまわしたいなら共通のIDを設定すれば共有されるようになります。

<PropertyGroup>
  <!-- 既存のプロパティ -->

  <UserSecretsId>FA8C2E4B-8486-479E-A4D5-3C447C835C41</UserSecretsId>
</PropertyGroup>

あとはASP.NET Coreでの開発中のアプリ シークレットの安全な格納に従って、.csprojのあるディレクトリでdotnet user-secrets set KEY VALUEなどのコマンドを打って設定します。

ConfigurationBuilderではプロバイダーを記述した順に読み込み、重複した分は上書きされていきます。つまり、今回のコードでは

  1. appsettings.json
  2. 環境変数
  3. User Secrets

の順番で読み込まれますね。appsettings.jsonに(Gitにコミット可能な)デフォルトの値を入れておき、開発環境で別な値が必要な時はUser Secrets、CD環境では環境変数から値を読み込む、みたいな運用ができます。

ASP.NET Coreプロジェクトの場合

ASP.NET Coreプロジェクトの場合、Microsoft.AspNetCore.Appメタパッケージに一般的なプロバイダーのパッケージが含まれています。そのため、ASP.NET Coreアプリケーションでは基本的にNuGetでパッケージを追加しなくてもConfigurationBuilderは使用できます。

というかテンプレートのProgram.csの中のCreateDefaultBuilderがいい感じにプロバイダーの構成までやってくれています。具体的には次のプロバイダーを構成しています。詳細はホスト設定するを確認してください。

  1. appsettings.json
  2. appsettings.{Environment}.json
  3. User Secrets
  4. ASPNETCOREのプレフィックスが付いた環境変数
  5. コマンドライン引数

追加のプロバイダーを設定したい場合は、ConfigureAppConfigurationを使うと設定できます。

using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

namespace ConfigurationBuilderTestEmptyASP
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.SetBasePath(Directory.GetCurrentDirectory());
                    config.AddJsonFile("hoge.json");
                    config.AddXmlFile("fuga.xml");
                })
                .UseStartup<Startup>();
    }
}

User Secretsの管理ですが、ASP.NET Coreプロジェクトの場合はプロジェクトを右クリックして「ユーザーシークレットの管理」を選択するとJSONファイルを開けるので、それを編集するのが楽だと思います。

まとめ

  • ConfigurationBuilderを使うとアプリの設定を簡単に構成できる
  • ASP.NET Coreの場合はCreateDefaultBuilderが基本的な設定を構成してくれる

ConfigurationBuilder自体はそんなめんどくさくないですけど、ASP.NET Coreプロジェクト以外でUser Secretsを使うのがちょっと面倒ですね。Open UserSecretsを使うといい感じにできそうなので試してみようと思います。