ConfigurationBuilderを理解する【.NET6.0】
初稿:
更新:
- 9 min read -
本記事の主旨
.NETアプリケーション開発における設定ファイル、とりわけシークレット情報の扱いについて、ようやく最低限のレベルに追いつくことができたのでメモを残す。
構成 API を使用して、.NET アプリケーションを構成する方法を説明します。 さまざまな組み込みの構成プロバイダーについて説明します。
これまで独自の手法で実装していた。
このたびはじめてASP.NET Coreアプリケーション開発を経験し、ConfigurationBuilderの威力を知ることができた。
なぜこれまで知らなかったか。
ASP.NET Coreアプリケーションのテンプレートには、NugetPackageを追加せずとも標準でappsettings.json等を扱う構成関連の機能が備わっている。
しかし、WPFやXamarin Formsといった私がこれまで開発してきたテンプレートにおいては、自身で準備する必要があった。
私はここで道を誤った。
「愚者は経験に学び、賢者は歴史に学ぶ」。
もちろん前者の私は独自のスタイルで実装する愚を犯した。
ベストプラクティスにアクセスできず、おかしな独自実装を行う現象は「個人開発者あるある」だ。(言い訳)
「.NETアプリ開発の設定ファイル管理において、同じ轍を踏む方を一人でも減らしたい」が本記事の主旨である。
ちなみにとりわけリポジトリに含めたくないシークレット情報の取扱いに絞った話となる。
環境
開発環境
- IDE : Visual Studio 2022 Community
- .NET Version : .NET 6.0
- 言語 : C# 10
CI/CD環境
- Azure DevOps pipelines
本番環境
- Azure App Service
設定ファイル管理ビフォー・アフター
ビフォー:以前の設定ファイル管理状況
まずはビフォー。
設定情報の保存場所は以下のとおり。
- development : UserSecrets.json
- product : Azure DevOps Pipelines Library
UserSecretsとAzure DevOps Pipelines Libraryの詳細は以下を参照
- ASP.NET Core での開発におけるアプリ シークレットの安全な保存 | Microsoft Docs
- Azure Pipelines 用のライブラリ - Azure Pipelines | Microsoft Docs
問題は、これら情報をアプリケーションに反映する方法である。
無知な私はこのように実現した。
- UserSecretsを読み込むUserSecretsManagerクラスを作成
- アプリケーションでシークレットを読み込むためのConstantsクラスを作成
- Constantsクラス内に #IF DEBUGによる分岐を作成
- ローカルの開発環境ではUserSecretsManagerクラスを経由しUserSecretsの値を読み込む
- productsはAzure DevOps Pipelinesのビルド実行時にShell Scriptで[PLACE_HOLDER]とした箇所をLibraryの値で置換し反映する。
例としてはこんな感じ。
強引で美しくない。
アフター:ConfigurationBuilderを使用する
ASP.NET Coreアプリケーションの場合、新規作成したアプリケーションのProgram.cs(.NET6.0以前はStartup.cs)でジェネレートされたこの一行にすべて含まれている。
よってすぐに使用できる。
ASP.NET Core以外の場合はこのように記述することで、同様に設定情報を使用することができる。
この例ではappsettings.jsonファイル、環境変数、UserSecrets.jsonの3つを使用しており、ASP.NET Core以外の場合、これらのNugetPackageを自分でインストールしておく必要がある。
- microsoft.extensions.configuration.fileextensions
- microsoft.extensions.configuration.json
- microsoft.extensions.configuration.environmentvariables
- microsoft.extensions.configuration.usersecrets
ConfigurationManagerの仕様について
複数定義された設定情報をConfigurationBuilderはどのように読み込んでいるのか。
それは、ConfigurationBuilderでAddする順番に示されている。
ConfigurationBuilderではプロバイダーを記述した順に読み込み、重複した分は上書きされていく。つまり、今回のコードでは
- appsettings.json
- 環境変数
- User Secrets
の順番で読み込まれる。appsettings.jsonに(Gitにコミット可能な)デフォルトの値を入れておき、開発環境で別な値が必要な時はUser Secrets、CD環境では環境変数から値を読み込む、みたいな運用ができます。 — cloud.config Tech Blogより引用
1から3の順番に読み込み、後に読み込んだものが優先される。
つまり、UserSecrets.jsonが実行環境にあればこれが最優先となる。
UserSecrets.json無ければ環境変数で読み込んだ値が反映される。
環境変数も存在しなければ最初に読み込むappsettings.jsonが反映される。appsettings.jsonファイルがないとビルドエラーとなる。
環境変数はAzure App Service、Azure DevOps Pipelinesなど各環境で定義した変数が読み込まれる。
ASP.NET Coreの場合はこれらの処理が上述したWebApplication.CreateBuilder(args) に含まれている。
簡単・便利・素晴らしい。
この仕様を知らずに生きてきたことを激しく悔いている。
実装サンプル
個人開発で作成したアプリでは、Azure CosmosDBを使用している。
接続に関するシークレット情報はInfrastructureレイヤのクラスライブラリで扱っている。
どなたかの参考になるかわからないが、サンプルとして掲載してみる。
ConfigManagerクラス
ConfigurationBuilderで設定ファイルプロバイダ作成するクラス。
開始クラスがないのでassemblyからUserSecrets.jsonを読み込む。
ConfigManager.Settings[“NodeName”]; で、シークレット情報を読み込めるようにする。
ConstantsCosmosDBクラス
CosmosDbで使用するConstantsを定義するクラス。
ConfigManagerクラスを使用してシークレット情報を設定している。
UserSecrets.json
ローカルの開発環境で使用するCosmosDbの接続情報。
Azure DevOps PipelinesのLibrary
Azure DevOps Pipelinesのビルド時に実行するテストで使用する。
この環境ではUserSecrets.jsonは存在しないため、2番目のAddEnvironmentVariables、つまりこの設定が反映される。
YAMLファイルでLibraryに保存したVariable groupを呼びだすだけ。
本番環境(Azure App Service)
アプリケーション設定にシークレット情報を保存。Azure DevOpsと同様、この環境にはUserSecrets.jsonがないのでこれらの値が使用される。
結果、ローカル開発環境時はUserSecrets.json、Azure DevOps Pipelinesにおけるビルド時はLibraryのVariable group、本番環境ではAzure App Serviceのアプリケーション設定の変数を読み込んでくれる。