Azure App Service と Azure Functions でアプリ設定として Key Vault 参照を使用する

この記事では、App Service または Azure Functions アプリのアプリ設定または接続文字列の値として Azure Key Vault のシークレットを使用する方法について説明します。

Azure Key Vault は、アクセス ポリシーと監査履歴を完全制御する、一元化されたシークレット管理を提供するサービスです。 アプリ設定または接続文字列がキー コンテナー参照である場合、アプリケーション コードでは、それを他のアプリ設定や接続文字列と同様に使用できます。 これにより、アプリの構成とは別にシークレットを維持できます。 アプリ設定は保存時に安全に暗号化されますが、シークレットの管理機能が必要な場合は、キー コンテナーにアクセスする必要があります。

キー コンテナーへのアクセス権をアプリに付与する

キー コンテナーからシークレットを読み取るには、コンテナーを作成し、アクセス許可をアプリに付与する必要があります。

  1. Key Vault クイック スタートに従い、Key Vault を作成してください。

  2. アプリケーション用のマネージド ID を作成します。

    キー コンテナー参照では、アプリのシステム割り当て ID が既定で使用されますが、ユーザー割り当て ID を指定することもできます。

  3. 先ほど作成したマネージド ID に対して、キー コンテナーのシークレットへの読み取りアクセスを承認します。 その方法は、キー コンテナーのアクセス許可モデルによって異なります。

ネットワーク制限があるコンテナーにアクセスする

コンテナーがネットワーク制限付きで構成されている場合は、アプリケーションがネットワークにアクセスできることを確認する必要があります。 シークレット要求の配信元 IP が異なる可能性があるため、コンテナーはアプリのパブリック送信 IP に依存しないようにします。 代わりに、アプリで使用される仮想ネットワークからのトラフィックを受け入れるようにコンテナーを構成する必要があります。

  1. App Service のネットワーク機能Azure Functions のネットワーク オプションのページの説明に従って、アプリケーションの送信ネットワーク機能が構成されていることを確認します。

    プライベート エンドポイントに接続する Linux アプリケーションでは、すべてのトラフィックを仮想ネットワーク経由でルーティングするように明示的に構成する必要があります。 この要件は、今後の更新で削除される予定です。 この設定を構成するには、次のコマンドを実行します。

    az webapp config set --subscription <sub> -g <group-name> -n <app-name> --generic-configurations '{"vnetRouteAllEnabled": true}'
    
  2. コンテナーの構成で、アプリがアクセスするために使用するネットワークまたはサブネットが許可されていることを確認します。

ユーザー割り当て ID を使用してコンテナーにアクセスする

一部のアプリでは、作成時にシステム割り当て ID をまだ使用できない場合、シークレットを参照する必要があります。 このような場合は、ユーザー割り当て ID を作成し、コンテナーへのアクセス権を事前に与えることができます。

ユーザー割り当て ID にアクセス許可を付与した後、次の手順のようにします。

  1. まだ行っていない場合は、アプリケーションに ID を割り当てます

  2. keyVaultReferenceIdentity プロパティにユーザー割り当て ID のリソース ID を設定することにより、この ID を使用してキー コンテナー参照操作を行うようアプリを構成します。

    identityResourceId=$(az identity show --resource-group <group-name> --name <identity-name> --query id -o tsv)
    az webapp update --resource-group <group-name> --name <app-name> --set keyVaultReferenceIdentity=${identityResourceId}
    

この設定は、アプリのすべてのキー コンテナー参照に適用されます。

回転

参照内にシークレットのバージョンが指定されていない場合、アプリではキー コンテナーに存在する最新バージョンが使用されます。 ローテーション イベントなどにより新しいバージョンが利用できるようになると、アプリは自動的に更新され、24 時間以内に最新バージョンの使用が開始されます。 この遅延は、App Service によって Key Vault 参照の値がキャッシュされ、24 時間ごとに再フェッチされるためです。 アプリの構成を変更すると、アプリが再起動され、参照されているすべてのシークレットが直ちに再フェッチされます。

キー コンテナーからのソース アプリ設定

キー コンテナー参照を使用するには、設定の値として参照を設定します。 アプリは通常どおり、そのキーを利用してシークレットを参照できます。 コードに変更を加える必要はありません。

ヒント

キー コンテナー参照を使用するほとんどのアプリ設定は、環境ごとに別個のコンテナーを用意する必要があるため、スロット設定としてマークする必要があります。

キー コンテナー参照の形式は @Microsoft.KeyVault({referenceString}) です。ここで、{referenceString} は次のいずれかの形式です。

参照文字列 説明
SecretUri=secretUri SecretUri は、コンテナー内のシークレットのフル データプレーン URI である必要があり、必要に応じてバージョンが含まれます (例: https://myvault.vault.azure.net/secrets/mysecret/https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)。
VaultName=vaultName;SecretName=secretName;SecretVersion=secretVersion VaultName は必須であり、コンテナー名です。 SecretName は必須であり、シークレット名です。 SecretVersion は省略可能ですが、存在する場合は、使用するシークレットのバージョンを示します。

たとえば、完全な参照は次の文字列のようになります。

@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/)

あるいは:

@Microsoft.KeyVault(VaultName=myvault;SecretName=mysecret)

Azure Files のマウントに関する考慮事項

アプリでは、WEBSITE_CONTENTAZUREFILECONNECTIONSTRING アプリケーション設定を使用して、Azure Files をファイル システムとしてマウントできます。 この設定には、アプリを正しく開始できることを確認するための検証チェックがあります。 プラットフォームでは、Azure Files 内にコンテンツ共有があることを前提としており、WEBSITE_CONTENTSHARE 設定で指定されていない場合は既定の名前を想定します。 これらの設定を変更する要求がある場合、プラットフォームでは、このコンテンツ共有が存在するかどうかを検証し、存在しない場合は作成を試みます。 コンテンツ共有が見つからないか作成できない場合、要求はブロックされます。

この設定でキー コンテナー参照を使用すると、受信要求の処理中にシークレット自体を解決できないため、検証チェックは既定で失敗します。 この問題を回避するために、WEBSITE_SKIP_CONTENTSHARE_VALIDATION を "1" に設定して検証をスキップできます。 この設定では、すべてのチェックをバイパスするように App Service に指示し、コンテンツ共有は作成されません。 事前に作成されていることを確認する必要があります。

注意事項

検証をスキップし、接続文字列またはコンテンツ共有が無効である場合、アプリは正常に開始できず、HTTP 500 エラーのみを処理します。

アプリ作成の途中で、マネージド ID のアクセス許可が伝達されていないか、仮想ネットワーク統合がセットアップされていないことが原因で、コンテンツ共有のマウント試行が失敗する場合があります。 これに対応するために、デプロイ テンプレートの後半まで Azure Files のセットアップを先送りすることができます。 詳細については、「Azure Resource Manager デプロイ」を参照してください。 この場合、App Service では、Azure Files が設定されるまでは既定のファイルシステムが使用され、ファイルはコピーされません。 Azure Files がマウントされる前の一時的な期間中はデプロイ試行が発生しないようする必要があります。

Application Insights のインストルメンテーションに関する考慮事項

アプリでは、APPINSIGHTS_INSTRUMENTATIONKEY または APPLICATIONINSIGHTS_CONNECTION_STRING アプリケーション設定を使用して Application Insights と統合できます。 App Service と Azure Functions のポータル エクスペリエンスでも、これらの設定を使用してリソースのテレメトリ データを表示できます。 これらの値が Key Vault から参照されている場合、これらのエクスペリエンスは利用できず、代わりに Application Insights リソースを直接操作してテレメトリを表示する必要があります。 ただし、これらの値はシークレットとは見なされないため、キー コンテナー参照を使用するのではなく、直接構成することも検討できます。

Azure Resource Manager デプロイ

Azure Resource Manager テンプレート経由でリソース デプロイを自動化するとき、この機能を動作させるには、場合によっては特定の順序で依存関係を並べる必要があります。 アプリ定義の siteConfig プロパティを使用するのではなく、アプリ設定を独自のリソースとして定義してください。 これはアプリを最初に定義する必要があるからです。そうすることで、システム割り当て ID が一緒に作成され、アクセス ポリシーで使用できるようになります。

次の擬似テンプレートは、関数アプリがどのようなものになるかを示す例です。

{
    //...
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "name": "[variables('storageAccountName')]",
            //...
        },
        {
            "type": "Microsoft.Insights/components",
            "name": "[variables('appInsightsName')]",
            //...
        },
        {
            "type": "Microsoft.Web/sites",
            "name": "[variables('functionAppName')]",
            "identity": {
                "type": "SystemAssigned"
            },
            //...
            "resources": [
                {
                    "type": "config",
                    "name": "appsettings",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('storageConnectionStringName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('appInsightsKeyName'))]"
                    ],
                    "properties": {
                        "AzureWebJobsStorage": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringName')).secretUriWithVersion, ')')]",
                        "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringName')).secretUriWithVersion, ')')]",
                        "APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyName')).secretUriWithVersion, ')')]",
                        "WEBSITE_ENABLE_SYNC_UPDATE_SITE": "true"
                        //...
                    }
                },
                {
                    "type": "sourcecontrols",
                    "name": "web",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
                        "[resourceId('Microsoft.Web/sites/config', variables('functionAppName'), 'appsettings')]"
                    ],
                }
            ]
        },
        {
            "type": "Microsoft.KeyVault/vaults",
            "name": "[variables('keyVaultName')]",
            //...
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
            ],
            "properties": {
                //...
                "accessPolicies": [
                    {
                        "tenantId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.tenantId]",
                        "objectId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.principalId]",
                        "permissions": {
                            "secrets": [ "get" ]
                        }
                    }
                ]
            },
            "resources": [
                {
                    "type": "secrets",
                    "name": "[variables('storageConnectionStringName')]",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
                    ],
                    "properties": {
                        "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),'2019-09-01').key1)]"
                    }
                },
                {
                    "type": "secrets",
                    "name": "[variables('appInsightsKeyName')]",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
                    ],
                    "properties": {
                        "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2019-09-01').InstrumentationKey]"
                    }
                }
            ]
        }
    ]
}

Note

この例では、ソース管理デプロイはアプリケーション設定に依存します。 アプリ設定は非同期で更新されるため、通常、これは安全ではありません。 ただし、WEBSITE_ENABLE_SYNC_UPDATE_SITE アプリケーション設定を含めているため、同期で更新されます。 つまり、ソース管理デプロイは、アプリケーションが完全に更新されたときにのみ開始されます。 他のアプリの設定については、「Azure App Service の環境変数とアプリ設定」を参照してください。

キー コンテナー参照のトラブルシューティング

参照が正しく解決されない場合は、代わりに参照文字列が使用されます (例: @Microsoft.KeyVault(...))。 この場合、アプリケーションでは別の値のシークレットが想定されているため、エラーがスローされることがあります。

解決できない一般的な原因は、Key Vault アクセス ポリシーが正しく構成されていないことです。 ただし、シークレットが既に存在していないか、参照自体の構文エラーが原因である可能性もあります。

構文が正しい場合は、ポータルで現在の解決状態を確認して、エラーの他の原因を確認できます。 [アプリケーションの設定] に移動し、該当する参照の [編集] を選択します。 編集ダイアログには、エラーを含む状態情報が表示されます。 ステータス メッセージが表示されない場合は、構文が無効であり、キー コンテナー参照として認識されないことを意味します。

組み込みの検出機能のいずれかを使用して、追加情報を取得することもできます。

App Service の検出機能の使用

  1. ポータルで、アプリに移動します。
  2. [Diagnose and solve prolems](問題の診断と解決) を選択します。
  3. [Availability and Performance] (可用性とパフォーマンス) を選択し、[Web app down] (Web アプリのダウン) を選択します。
  4. 検索ボックスで、[Key Vault Application Settings Diagnostics] (Key Vault のアプリケーション設定の診断) を検索して選択します。

Azure Functions の検出機能の使用

  1. ポータルで、アプリに移動します。
  2. [プラットフォーム機能] に移動します。
  3. [Diagnose and solve prolems](問題の診断と解決) を選択します。
  4. [Availability and Performance] (可用性とパフォーマンス) を選択し、[Function app down or reporting errors] (関数アプリのダウンまたはエラーの報告) を選択します。
  5. [Key Vault Application Settings Diagnostics] (Key Vault のアプリケーション設定の診断) を選択します。