KeyVault のシークレット情報をマネージドIDを利用して取得する

どうしてこんなにわかりにくいのだろうか。。
ここを見たけどうまくいかず
https://docs.microsoft.com/ja-jp/azure/key-vault/tutorial-net-create-vault-azure-web-app

これに挑戦してようやく
https://news.mynavi.jp/article/zeroazure-23/

# はじめに
KeyVaultにServiceBusの接続情報をシークレットという形式で格納することで、Functionsにハードコーディングせずに済むという話。KeyVaultにFunctionsがシークレット情報ください、と問い合わせるわけだが、当然誰彼構わずわたすわけではなく。特定の認証された人にだけ渡すようになっている。これがマネージドID。

# 大まかな流れ
１．KeyVault（キーコンテナ）を作成
２．作成したキーコンテナにシークレット情報を格納
⇒この時シークレット識別子というのがアサインされるので、これをメモしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/281a2519-f3d6-4065-bc53-fce6c97e0553.png)
３．VisualStadioでキーコンテナからシークレット情報を参照するコーディングを行う
（VSの新規プロジェクト＞ASP.Core Web アプリケーションのテンプレートを指定）

```C#:Program.cs
// キーコンテナーのDNS名を設定する
var keyVaultEndpoint = "https://keyvault-je-001.vault.azure.net/";

// Azure Key Vaultに接続するための追加の設定を記述する
// １．アプリをAzureに認証するためのアクセストークン取得プロバイダーのインスタンス化
var azureServiceTokenProvider = new AzureServiceTokenProvider();

// ２．Azure Key Vaultに接続するためのクライアントのインスタンス化
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

// ３．Azure Key Vault（キーコンテナー）からシークレットにアクセスするための設定をbuilderに追加
builder.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
```

４．デプロイして、テスト！してもつながりません。（500系エラー）

５．デプロイしたWebAppsのIDを選択し、こんな感じでオンに切り替えて保存
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/a54bc4dd-5488-0587-f701-b346864e6065.png)

６．キーコンテナ＞アクセスポリシーから「+アクセスポリシーの追加」を選択

７．さっきWebAppsでオンに切り替えたときに作成されたIDが選択可能になっているはずなので、選択してシークレットの一覧、参照を有効にして保存。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/48bdd488-81f7-343d-f400-8a1469c00eb9.png)

８．もう一回VSからデプロイして、打鍵テスト。正常に表示！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355390/028c26bf-1340-2e78-399e-7f11541de746.png)


# 後記
[Azure Key Vault を使用してサーバー アプリのシークレットを管理する](https://docs.microsoft.com/ja-jp/learn/modules/manage-secrets-with-azure-key-vault/5-access-secrets-from-web-app?pivots=csharp)
> .NET Core の公式の Key Vault クライアントは、Microsoft.Azure.KeyVault NuGet パッケージの KeyVaultClient クラスです。 これを直接使用する必要はありませんが、— と ASP.NET Core の AddAzureKeyVault メソッドを使用すると、起動時にコンテナーのすべてのシークレットを Configuration API に読み込むことができます。 この手法では、残りの構成で使用するものと同じ IConfiguration インターフェイスを使用して、すべてのシークレットに名前を指定してアクセスできます。 AddAzureKeyVault を使用するアプリには、コンテナーに対する Get と List の両方のアクセス許可が必要です。

マイナビのサイトもMSのLearningでも実装方法はほぼ同じなことに気づきました。
MSのLearningだとProgram.cs内でわずかにこれだけ。キーコンテナを丸ごと読み込んで、必要なものだけアプリ内で抽出して(SeacretNameとの一致するものだけ)利用してるんですね。

```C#:Program.cs
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

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

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((context, config) =>
                {
                    // Build the current set of configuration to load values from
                    // JSON files and environment variables, including VaultName.
                    var builtConfig = config.Build();

                    // Use VaultName from the configuration to create the full vault URL.
                    var vaultUrl = $"https://{builtConfig["VaultName"]}.vault.azure.net/";

                    // Load all secrets from the vault into configuration. This will automatically
                    // authenticate to the vault using a managed identity. If a managed identity
                    // is not available, it will check if Visual Studio and/or the Azure CLI are
                    // installed locally and see if they are configured with credentials that can
                    // access the vault.
                    config.AddAzureKeyVault(vaultUrl);
                })
                .UseStartup<Startup>();
    }
}
```


```C#:SecretTestController.cs
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace KeyVaultDemoApp.Controllers
{
    [Route("api/[controller]")]
    public class SecretTestController : ControllerBase
    {
        private readonly IConfiguration _configuration;

        public SecretTestController(IConfiguration configuration)
        {
            _configuration = configuration; // ★ごそっと一括で取得
        }

        [HttpGet]
        public IActionResult Get()
        {
            // Get the secret value from configuration. This can be done anywhere
            // we have access to IConfiguration. This does not call the Key Vault
            // API, because the secrets were loaded at startup.
            var secretName = "SecretPassword";
            var secretValue = _configuration[secretName]; // ★配列みたいなもので一致しているものだけとっている？

            if (secretValue == null)
            {
                return StatusCode(
                    StatusCodes.Status500InternalServerError,
                    $"Error: No secret named {secretName} was found...");
            }
            else {
                return Content($"Secret value: {secretValue}" +
                    Environment.NewLine + Environment.NewLine +
                    "This is for testing only! Never output a secret " +
                    "to a response or anywhere else in a real app!");
            }
        }
    }
}
```