Azure FunctionsにKey Vault参照を追加する

⏱️約5分
シェア:

今年の 10 月頃に、"Key Vault 参照"なる機能が GA されました1。 これは、Azure Functions や App Service で扱うシークレットを Key Vault に保存して、アプリケーションから透過的に(アプリケーションからは特に Key Vault を意識することなく、単なる環境変数として)利用するための機能です。 本記事ではこの Key Vault 参照を実現するための方法を解説していきます。基本的な内容は Microsoft の公式ドキュメント2に書かれている内容と同じですが、自分で実施する際に躓いたことや注意点などを付け加えていきます。 また、本記事では CI/CD を意識して、ポータルではなく、ARM Template や Azure CLI を使用して実現する方法を記載します。

1. Key Vault にシークレットとアクセス権を追加する

Key Vault にシークレットと、Azure Functions のアクセス権を追加します。

json
1{
2 "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#",
3 "contentVersion": "1.0.0.0",
4 "parameters": {
5 "keyVaultName": {
6 "type": "string",
7 "metadata": {
8 "description": "Key Vault Name"
9 }
10 },
11 "secretName": {
12 "type": "string",
13 "metadata": {
14 "description": "Secret Name"
15 }
16 },
17 "functionAppName": {
18 "type": "string",
19 "metadata": {
20 "description": "Function App Name"
21 }
22 },
23 "storageAccountName": {
24 "type": "string",
25 "metadata": {
26 "description": "Storage Account Name"
27 }
28 }
29 },
30 "variables": {},
31 "resources": [
32 {
33 "name": "[parameters('keyVaultName')]",
34 "type": "Microsoft.KeyVault/vaults",
35 "apiVersion": "2018-02-14",
36 "location": "[resourceGroup().location]",
37 "properties": {
38 "tenantId": "[subscription().tenantId]",
39 "sku": {
40 "family": "A",
41 "name": "standard"
42 },
43 "accessPolicies": [
44 {
45 "tenantId": "[subscription().tenantId]",
46 "objectId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2016-03-01', 'Full').identity.principalId]",
47 "permissions": {
48 "keys": [],
49 "secrets": ["get"],
50 "certificates": [],
51 "storage": []
52 }
53 }
54 ],
55 "enabledForDeployment": false,
56 "enabledForDiskEncryption": false,
57 "enabledForTemplateDeployment": false
58 },
59 "resources": [
60 {
61 "name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]",
62 "type": "Microsoft.KeyVault/vaults/secrets",
63 "apiVersion": "2018-02-14",
64 "dependsOn": [
65 "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
66 ],
67 "properties": {
68 "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2015-05-01-preview').key1)]"
69 }
70 }
71 ]
72 }
73 ]
74}

accessPoliciesの部分でアクセス権を設定しています。objectIdというプロパティには、対象の Azure Functions の Managed ID を指定します。Managed ID については後述しますが、Azure Functions に割り当てられた ID で、これを使って Azure Functions を識別し、アクセス権を管理できます。 permissionsには付与したい権限を指定します。上の例では、シークレットの参照権限が与えられています。ここは用途に合わせて任意の権限を設定してください。

Microsoft.KeyVault/vaults/secretsのリソースでシークレットを設定しています。propertiesvalueにシークレットの値を指定します。この例ではストレージアカウントの key を設定しています。ここも用途に合わせて任意のシークレットを指定してください。

2. Azure Functions にアプリケーション設定を追加する

次に、アプリケーション設定をデプロイしていきましょう。簡単のため、Key Vault 参照に関係ある設定のみを記載しています。

json
1{
2 "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#",
3 "contentVersion": "1.0.0.0",
4 "parameters": {
5 "keyVaultName": {
6 "type": "string",
7 "metadata": {
8 "description": "Key Vault Name"
9 }
10 },
11 "secretName": {
12 "type": "string",
13 "metadata": {
14 "description": "Secret Name"
15 }
16 },
17 "functionAppName": {
18 "type": "string",
19 "metadata": {
20 "description": "Function App Name"
21 }
22 }
23 },
24 "variables": {
25 "secretResourceId": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), 'parameters('secretName')')]"
26 },
27 "resources": [
28 {
29 "name": "[concat(parameters('functionAppName'), '/appsettings')]",
30 "type": "Microsoft.Web/sites/config",
31 "apiVersion": "2018-11-01",
32 "location": "[resourceGroup().location]",
33 "properties": {
34 "YOUR_SECRET": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('secretResourceId'), '2018-02-14').secretUriWithVersion, ')')]"
35 }
36 }
37 ]
38}

ARM Template 内では、

  1. シークレットのリソースを示すresourceIdを生成(variablesで実施)
  2. 1 のresourceIdに対してsecretUriWithVersionというプロパティで Key Vault のシークレットの URI を取得
  3. @Microsoft.KeyVault(SecretUri=<シークレットのuri>)という書式でアプリケーション設定に追加

という、手順で設定を行います。 先ほどから Template に出てくるresourceIdreferenceはテンプレート関数と呼ばれるものです。こちらについては、Microsoft の公式ドキュメントをご覧ください3

3. 依存関係に注意してデプロイを行う

Key Vault 参照を使う場合の注意点はデプロイの順番です。 基本的な順番は下記のとおりです。

  1. Azure Functions のリソース(Microsoft.Web/sites)
  2. Key Vault
  3. Azure Functions の アプリケーション設定
  4. Azure Functions の zip(ビルド成果物)の展開

まず、Azure Functions をデプロイします。 これは、2 で Key Vault をデプロイする時点で、Key Vault にアクセス権を追加するために、Azure Functions の Managed ID が事前に発行されていなければならないからです。 Managed ID は以下のような Template で発行できます。

json
1{
2 "apiVersion": "2016-03-01",
3 "name": "[parameters('functionAppName')]",
4 "type": "Microsoft.Web/sites",
5 "identity": {
6 "type": "SystemAssigned"
7 },
8 "properties": {
9 "name": "[parameters('functionAppName')]",
10 "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]",
11 "hostNameSslStates": [
12 {
13 "name": "[concat(variables('functionAppName'),'.azurewebsites.net')]",
14 "sslState": "Disabled",
15 "virtualIP": null,
16 "thumbprint": null,
17 "toUpdate": null,
18 "hostType": "Standard"
19 },
20 {
21 "name": "[concat(variables('functionAppName'),'.scm.azurewebsites.net')]",
22 "sslState": "Disabled",
23 "virtualIP": null,
24 "thumbprint": null,
25 "toUpdate": null,
26 "hostType": "Repository"
27 }
28 ],
29 "siteConfig": null,
30 "clientAffinityEnabled": false,
31 "reserved": false,
32 "httpsOnly": true
33 },
34 "location": "[resourceGroup().location]",
35 "kind": "functionapp"
36}

identityというプロパティを指定し、typeSystemAssignedを指定します。現時点で Key Vault 参照が使えるのはSystemAssignedのみです2

ただし、1 の時点ではまだアプリケーション設定をデプロイすることはできません。なぜなら、この時点では Key Vault がデプロイされておらず、Key Vault 参照用の SecretUri を指定することができないからです。 2 で Key Vault がデプロイされてから、改めて Azure Functions のアプリケーション設定をデプロイします。 そして最後に Azure Functions のビルド成果物を展開します。ここでの注意点は、アプリケーション設定がデプロイされた後に zip を展開する必要があるということです。先に zip を展開してからアプリケーション設定をデプロイすると、関数が消えてしまいます。

Azure CLI を使ってアプリケーション設定を行う場合の注意点

上の例は ARM Template を使っていますが、もちろん Azure CLI を活用することもできます。 特にアプリケーション設定については、ARM Template を使うと、上書きすることしかできませんが、Azure CLI を使えば、既存のアプリケーション設定に追加することができるので、上記の依存関係の縛りを緩和することができます。 例えば、3 のアプリケーション設定は 2 の Key Vault デプロイの後、かつ、4 の zip の展開よりも前、でないといけないと説明しましたが、Azure CLI を使ってアプリケーション設定の追加ができるのであれば、先に Key Vault 参照以外のアプリケーション設定を 1 のタイミングでデプロイしておき、Key Vault のデプロイや zip の展開後に、Azure CLI で Key Vault 参照用のアプリケーション設定だけを追加することで、デプロイ順序をよりフレキシブルに検討することができます。

Azure CLI で Key Vault 用のアプリケーション設定を指定する方法です。

powershell
1try {
2 $ret = az keyvault secret show --vault-name $keyVaultName --name $secretName
3}
4catch
5{
6 $message = $_.Exception.message
7 throw "Failed to get key vault secret: ${message}."
8}
9$secretObj = $ret | ConvertFrom-Json
10$url = $secretObj.id
11$secretUri = '"@Microsoft.KeyVault(SecretUri={0})"' -f $url
12
13# Add appsettings for azure functions
14az functionapp config appsettings set --name $functionAppName --resource-group $resourceGroup --settings YOUR_SECRET=$secretUri

注目すべきは、$secretUri = '"@Microsoft.KeyVault(SecretUri={0})"' -f $urlの部分で、ここを例えば、$secretUri = "@Microsoft.KeyVault(SecretUri=${url})"と書くと、最後の)が正しく設定されずエラーになります4。結構ハマったので注意してください。

もう 1 点注意すべきなのは、上記のコマンドを実行する際の権限です。このコマンドを実行するユーザーやサービスプリンシパルは Key Vault のシークレットに対して参照権限が必要です。前述の Key Vault の Template では Azure Functions のみに参照権限を与えていましたが、コマンドの実行者に対しても一時的に参照権限を与えておきましょう。

4. アプリケーションでのシークレットの取得

アプリケーションのコード内では単に環境変数にアクセスすれば OK です。

csharp
1// C#
2var env = System.Environment.GetEnvironmentVariable("YOUR_SECRET", EnvironmentVariableTarget.Process);
3
4// Javascript
5let env = process.env["YOUR_SECRET"];
6
7// Java
8String env = System.getenv("YOUR_SECRET");

まとめ

本記事では 10 月に GA された Key Vault 参照の使い方について説明しました。デプロイの順序は少し気を付ける必要がありますが、それ以外はそれほど難しくないと思います。 ぜひ使ってみてください。

Footnotes

  1. App Service と Azure Functions で Key Vault 参照を提供開始

  2. App Service と Azure Functions の Key Vault 参照を使用する 2

  3. Resource functions for Azure Resource Manager templates

  4. Incorrect config value set

シェア:

関連記事