ASP.NET Coreで作成したアプリをAzure Web Appで動かしAzure OpenAIにアクセスする場合、Microsoftのドキュメントでは認証をマネージドID認証にし、設定項目(エンドポイントなど)はKeyVaultに格納するという方法が記載されていました。ネットワーク的に制限をかける方法なども考えられますが、今回はC#のソースコードで考慮しないといけないこの2つを試してみます。
まず、マネージドID認証にする方法はこちらのドキュメントを参照します。
まず、記事の手順でAzure側の操作を行い、Azure Web AppsからOpenAIへのマネージド認証を有効にします。まず、Azure Web AppsのIdentityでStatusを有効にします。
この画面のAdd role assigmentからはアサイン済みのマネージドIDが確認でき、追加メニューもありますが一部のリソース種類のみでOpenAIは指定できませんが、OpenAIを含むリソースグループやサブスクリプションを指定することはできます。次の手順で設定するKeyVaultはここから追加できます。下の画面は次の手順まで実施したあとの様子です。今回はシステム割り当て IDを使っています。
さてコードについてですが、前回の記事のコードを書き換える場合を考えます。
まずAzure.Identity
というNuGetライブラリを追加します。次にAPIKeyを指定する代わりに、次のようにDefaultAzureCredential
のインスタンスを指定します。別のコンストラクタを呼び出すことになります。
var credentials = new DefaultAzureCredential(); var options = sp.GetRequiredService<IOptions<AzureOpenAIOptions>>().Value; var kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddAzureOpenAIChatCompletion( deploymentName: options.ChatDeploymentName, endpoint: options.Endpoint, credentials: credentials );
ユーザー割り当てIDを使う場合は次のようになります。
var credentials = new DefaultAzureCredential( new DefaultAzureCredentialOptions { ManagedIdentityClientId = "myIdentityClientId". } );
これで認証はできましたが、このままWebAppsで動かしても前回のコードのままだとdotnet secretに設定したエンドポイントなどの情報が取得できません。そこで次の記事を参照してAzure KeyVaultに格納した情報を取得するようにします。
KeyVaultを作成して、WebAppsからのマネージドIDをアサインします。アサインの手順はOpenAIの時と同様です。次にdotnet secretに格納した情報をKeyVaultのsecretに保存しますが、secretの名前はSection--SecretName
という形式にします。AzureOpenAI--ChatDeploymentName
とAzureOpenAI--ChatDeploymentName
という名前になります。
コード側はまずAzure.Extensions.AspNetCore.Configuration.Secrets
というNuGetライブラリを追加します。上の手順で追加したAzure.Identity
も必要です。そしてoptionsを取得する手前に次のコードを挿入します。
builder.Configuration.AddAzureKeyVault( new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"), credentials);
ドキュメントではKeyVaultNameをConfigurationから取得していたので環境変数に設定しました。またif (builder.Environment.IsProduction())
で括っていましたが、同じコードベースでAzure WebApps以外で動かす場合にEnvironmentを切り替えて別の認証を行うことを想定しているはずです。
最後に、前の記事ではAzureOpenAIOptionsクラスにApiKeyプロパティを用意していたので削除するか、null許容にしておきます。
以上の対応で冒頭に紹介した2つの対応が完了しました。