この記事では、Azure App Service または Azure Functions アプリのアプリ設定 または 接続文字列 の値として Azure Key Vault のシークレットを使用する方法について説明します。
Key Vault は、アクセス ポリシーと監査履歴を完全に制御できる、一元化されたシークレット管理を提供するサービスです。 アプリ設定または接続文字列が Key Vault 参照である場合、アプリケーション コードでは、他のアプリ設定や接続文字列と同様に使用できます。 これにより、アプリの構成とは別にシークレットを維持できます。 アプリ設定は保存時に安全に暗号化されますが、シークレットを管理するための機能が必要な場合は、キー コンテナーに移動する必要があります。
キー コンテナーへのアクセス権をアプリに付与する
キー コンテナーからシークレットを読み取る場合は、まずコンテナーを作成し、それにアクセスするためのアクセス許可をアプリに付与する必要があります。
Key Vault クイック スタートに従い、Key Vault を作成してください。
アプリケーション用のマネージド ID を作成します。
キー コンテナー参照では、アプリのシステム割り当て ID が既定で使用されますが、ユーザー割り当て ID を指定することもできます。
作成したマネージド ID に対 して、キー コンテナー内のシークレットへの読み取りアクセス を承認します。 その方法は、キー ボールトのアクセス許可モデルによって異なります。
- Azure ロールベースのアクセス制御: キー コンテナー シークレット ユーザー ロールをマネージド ID に割り当てます。 「Azure ロールベースのアクセス制御を使用して Key Vault のキー、証明書、シークレットへのアクセスを提供する」を参照してください。
- コンテナー アクセス ポリシー: マネージド ID にシークレット取得アクセス許可を割り当てます。 「Key Vault アクセス ポリシーを割り当てる」を参照してください。
ネットワーク制限があるコンテナーにアクセスする
ボールトがネットワーク制限付きで構成されている場合は、アプリケーションがネットワークにアクセスできることを確認してください。 シークレット要求の配信元 IP アドレスが異なる可能性があるため、ボールトはアプリのパブリック送信元 IP アドレスに依存しないでください。 代わりに、アプリが使用する仮想ネットワークからのトラフィックを受け入れるようにボールトを構成する必要があります。
App Service のネットワーク機能と Azure Functions のネットワーク オプションの説明に従って、アプリケーションに送信ネットワーク機能が構成されていることを確認します。
現時点では、プライベート エンドポイントに接続する Linux アプリケーションは、仮想ネットワーク経由ですべてのトラフィックをルーティングするように明示的に構成する必要があります。 この設定を構成するには、次のコマンドを実行します。
az webapp config set --resource-group <group-name> --subscription <subscription> --name <app-name> --generic-configurations '{"vnetRouteAllEnabled": true}'
コンテナーの構成で、アプリがアクセスするために使用するネットワークまたはサブネットが許可されていることを確認します。
仮想ネットワークからのトラフィックを受け入れるようにコンテナーを正しく構成した場合でも、コンテナーの監査ログに、アプリのパブリック送信 IP からの失敗 (403 - 禁止) SecretGet イベントが表示される可能性があることに注意してください。 その後、アプリのプライベート IP からの SecretGet イベントが成功し、これは設計上の意図です。
ユーザー割り当て ID を使用してボールトにアクセスする
一部のアプリでは、システム割り当て ID がまだ使用できない場合に、作成時にシークレットを参照する必要があります。 このような場合は、ユーザー割り当ての ID を作成し、事前にボールトへのアクセス権を付与します。
ユーザー割り当て ID にアクセス許可を付与したら、次の手順に従います。
keyVaultReferenceIdentity
プロパティをユーザー割り当て ID のリソース ID に設定して、Key Vault 参照操作にこの 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}
この設定は、アプリのすべての Key Vault 参照に適用されます。
回転を理解する
参照内にシークレットのバージョンが指定されていない場合、アプリではキー コンテナーに存在する最新バージョンが使用されます。 ローテーションなど、新しいバージョンが利用可能になると、アプリは自動的に更新され、24 時間以内に最新バージョンの使用が開始されます。
遅延は、App Service によって Key Vault 参照の値がキャッシュされ、24 時間ごとに参照されるためです。 アプリの構成を変更すると、アプリが再起動され、参照されているすべてのシークレットが直ちに再フェッチされます。
アプリの Key Vault 参照を強制的に解決するには、API エンドポイントに対して認証済みの POST 要求を https://management.azure.com/[Resource ID]/config/configreferences/appsettings/refresh?api-version=2022-03-01
します。
Key Vault のソース アプリの設定について
Key Vault 参照を使用するには、設定の値として参照を設定します。 アプリは通常どおり、そのキーを利用してシークレットを参照できます。 コードに変更を加える必要はありません。
ヒント
環境ごとに個別のボールトが必要であるため、Key Vault 参照を使用するほとんどのアプリ設定はスロットの設定としてマークする必要があります。
Key Vault 参照は @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
設定を使用して名前を指定しない限り、プラットフォームは既定の名前を仮定します。 これらの設定を変更する要求の場合、プラットフォームはこのコンテンツ共有が存在することを検証します。 コンテンツ共有が存在しない場合、プラットフォームはそれを作成しようとします。 プラットフォームがコンテンツ共有を見つけたり作成したりできない場合は、要求がブロックされます。
この設定で Key Vault 参照を使用すると、受信要求の処理中にシークレットを解決できないため、検証チェックは既定で失敗します。 この問題を回避するには、 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 の場合、Azure portal はこれらの設定を使用して、リソースからのテレメトリ データを表示します。 これらの値が Key Vault から参照されている場合、この方法は使用できません。 代わりに、Application Insights リソースを直接操作してテレメトリを表示する必要があります。 ただし、これらの値 はシークレットとは見なされないため、Key Vault 参照を使用する代わりに直接構成することを検討してください。
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]"
}
}
]
}
]
}
注記
この例では、ソース管理デプロイはアプリケーション設定に依存します。 アプリ設定の更新は非同期的に動作するため、通常、この依存関係は安全でない動作です。 ただし、 WEBSITE_ENABLE_SYNC_UPDATE_SITE
アプリケーション設定を含んだため、更新は同期的です。 ソース管理の展開は、アプリケーション設定が完全に更新された後にのみ開始されます。 他のアプリの設定については、「Azure App Service の環境変数とアプリ設定」を参照してください。
Key Vault リファレンスのトラブルシューティング
参照が正しく解決されない場合は、参照文字列が代わりに使用されます (例: @Microsoft.KeyVault(...)
)。 この場合、アプリケーションが別の値のシークレットを予期しているため、エラーが発生する可能性があります。
解決できない一般的な原因は、Key Vault アクセス ポリシーが正しく構成されていないことです。 ただし、シークレットが存在しなくなったり、参照に構文エラーが含まれている可能性もあります。
構文が正しい場合は、Azure portal で現在の解決状態を確認することで、エラーの他の原因を確認できます。 [アプリケーション設定] に移動し、対象の参照に対して [編集] を選択します。 編集ダイアログには、エラーを含む状態情報が表示されます。 ステータス メッセージが表示されない場合は、構文が無効であり、Key Vault 参照として認識されていないことを意味します。
また、組み込みの検出器の 1 つを使用して、詳細情報を取得することもできます。
App Service の検出機能を使用するには:
- Azure portal で、アプリに移動します。
- [Diagnose and solve prolems](問題の診断と解決) を選択します。
- 可用性とパフォーマンス>Web アプリダウン を選択します。
- 検索ボックスで、[Key Vault Application Settings Diagnostics] (Key Vault のアプリケーション設定の診断) を検索して選択します。
Azure Functions の検出機能を使用するには:
- Azure portal で、アプリに移動します。
- プラットフォーム機能に移動します。
- [Diagnose and solve prolems](問題の診断と解決) を選択します。
- 可用性とパフォーマンス>「Function アプリがダウンまたはエラーを報告」を選択します。
- [Key Vault Application Settings Diagnostics] (Key Vault のアプリケーション設定の診断) を選択します。