銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

ASP.NET Core的にAzure Web AppからAzure OpenAIへのアクセスをセキュアにする

ASP.NET Coreで作成したアプリをAzure Web Appで動かしAzure OpenAIにアクセスする場合、Microsoftのドキュメントでは認証をマネージドID認証にし、設定項目(エンドポイントなど)はKeyVaultに格納するという方法が記載されていました。ネットワーク的に制限をかける方法なども考えられますが、今回はC#のソースコードで考慮しないといけないこの2つを試してみます。

まず、マネージドID認証にする方法はこちらのドキュメントを参照します。

learn.microsoft.com

まず、記事の手順でAzure側の操作を行い、Azure Web AppsからOpenAIへのマネージド認証を有効にします。まず、Azure Web AppsのIdentityでStatusを有効にします。

この画面のAdd role assigmentからはアサイン済みのマネージドIDが確認でき、追加メニューもありますが一部のリソース種類のみでOpenAIは指定できませんが、OpenAIを含むリソースグループやサブスクリプションを指定することはできます。次の手順で設定するKeyVaultはここから追加できます。下の画面は次の手順まで実施したあとの様子です。今回はシステム割り当て IDを使っています。

さてコードについてですが、前回の記事のコードを書き換える場合を考えます。

tech.tanaka733.net

まず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に格納した情報を取得するようにします。

learn.microsoft.com

KeyVaultを作成して、WebAppsからのマネージドIDをアサインします。アサインの手順はOpenAIの時と同様です。次にdotnet secretに格納した情報をKeyVaultのsecretに保存しますが、secretの名前はSection--SecretNameという形式にします。AzureOpenAI--ChatDeploymentNameAzureOpenAI--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つの対応が完了しました。