デプロイ時に Azure Key Vault を使用して、セキュリティで保護されたパラメーター値を渡す

お使いのテンプレートやパラメーター ファイルに安全な値 (パスワードなど) を直接入れる代わりに、デプロイ時に、Azure Key Vault から値を取得できます。 値を取得するには、キー コンテナーとパラメーター ファイル内のシークレットを参照します。 参照するのは Key Vault ID だけであるため、値が公開されることはありません。

重要

この記事では、機密の値をテンプレート パラメーターとして渡す方法に焦点を当てます。 シークレットがパラメーターとして渡されるとき、キー コンテナーは別のサブスクリプションに存在するか、デプロイ先のリソース グループに存在することがあります。

この記事では、仮想マシンのプロパティを、キー コンテナー内の証明書の URL に設定する方法については説明しません。 そのシナリオのクイックスタート テンプレートについては、Azure Key Vault から証明書を仮想マシンにインストールする方法に関する記事を参照してください。

キー コンテナーとシークレットをデプロイする

テンプレートのデプロイ時にキー コンテナーにアクセスするには、キー コンテナーの enabledForTemplateDeploymenttrue に設定します。

既にキー コンテナーがある場合は、テンプレートのデプロイが許可されていることを確認してください。

az keyvault update  --name ExampleVault --enabled-for-template-deployment true

新しいキー コンテナーを作成してシークレットを追加するには、次のように使用します。

az group create --name ExampleGroup --location centralus
az keyvault create \
  --name ExampleVault \
  --resource-group ExampleGroup \
  --location centralus \
  --enabled-for-template-deployment true
az keyvault secret set --vault-name ExampleVault --name "ExamplePassword" --value "hVFkk965BuUv"

キー コンテナーの所有者には、シークレットを作成するためのアクセスが自動的に付与されています。 別のユーザーにシークレットの作成を許可する必要がある場合は、次を使用します。

az keyvault set-policy \
  --upn <user-principal-name> \
  --name ExampleVault \
  --secret-permissions set delete get list

シークレットを取得するテンプレートをユーザーがデプロイしている場合、アクセス ポリシーは必要ありません。 ユーザーがシークレットを直接使用する必要がある場合にのみ、アクセス ポリシーにユーザーを追加します。 デプロイのアクセス許可については、次のセクションで定義します。

キー コンテナーの作成とシークレットの追加の詳細については、次を参照してください。

シークレットへのデプロイ アクセスを付与する

テンプレートをデプロイするユーザーには、そのリソース グループとキー コンテナーのスコープで Microsoft.KeyVault/vaults/deploy/action のアクセス許可がある必要があります。 Azure Resource Manager は、このアクセス権を確認することによって、未承認のユーザーがキー コンテナーのリソース ID を渡してシークレットにアクセスすることを防ぎます。 シークレットへの書き込みアクセスを付与せずに、ユーザーにデプロイ アクセスを付与できます。

このアクセスは、所有者ロールと共同作成者ロールが許可します。 キー コンテナーの作成者である場合は、その所有者であり、そのアクセス許可を持っています。

他のユーザーには、Microsoft.KeyVault/vaults/deploy/action アクセス許可を付与します。 次の手順は、最小限のアクセス許可があるロールを作成する方法と、それをユーザーに割り当てる方法を示しています。

  1. カスタム ロール定義の JSON ファイルを作成します。

    {
      "Name": "Key Vault resource manager template deployment operator",
      "IsCustom": true,
      "Description": "Lets you deploy a resource manager template with the access to the secrets in the Key Vault.",
      "Actions": [
        "Microsoft.KeyVault/vaults/deploy/action"
      ],
      "NotActions": [],
      "DataActions": [],
      "NotDataActions": [],
      "AssignableScopes": [
        "/subscriptions/00000000-0000-0000-0000-000000000000"
      ]
    }
    

    "00000000-0000-0000-0000-000000000000" はサブスクリプション ID に置き換えます。

  2. JSON ファイルを使用して新しいロールを作成します。

    az role definition create --role-definition "<path-to-role-file>"
    az role assignment create \
      --role "Key Vault resource manager template deployment operator" \
      --scope /subscriptions/<Subscription-id>/resourceGroups/<resource-group-name> \
      --assignee <user-principal-name> \
      --resource-group ExampleGroup
    

    このサンプルでは、リソース グループ レベルでカスタム ロールを割り当てます。

Managed Applications のテンプレートでキー コンテナーを使用する場合は、アプライアンス リソース プロバイダー サービス プリンシパルにアクセス許可を付与する必要があります。 詳細については、「Access Key Vault secret when deploying Azure Managed Applications」(Azure Managed Applications のデプロイ時に Key Vault シークレットにアクセスする) を参照してください。

固定 ID でのシークレットの参照

この手法では、テンプレートではなく、パラメーター ファイルでキー コンテナーを参照します。 次の図は、パラメーター ファイルがシークレットを参照し、その値をテンプレートに渡すしくみを示しています。

静的 ID を使用した Resource Manager キー コンテナー統合の固定 ID の図。

チュートリアル:Resource Manager テンプレートのデプロイで Azure Key Vault を統合する」では、この手法が利用されます。

次のテンプレートでは、管理者パスワードを含む SQL サーバーがデプロイされます。 パスワード パラメーターは、セキュリティで保護された文字列に設定されます。 しかし、テンプレートには値がどこから来ているかが指定されていません。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "sqlServerName": {
      "type": "string"
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "adminLogin": {
      "type": "string"
    },
    "adminPassword": {
      "type": "securestring"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Sql/servers",
      "apiVersion": "2021-11-01",
      "name": "[parameters('sqlServerName')]",
      "location": "[parameters('location')]",
      "properties": {
        "administratorLogin": "[parameters('adminLogin')]",
        "administratorLoginPassword": "[parameters('adminPassword')]",
        "version": "12.0"
      }
    }
  ]
}

ここで、前のテンプレートのためにパラメーター ファイルを作成します。 このパラメーター ファイルで、テンプレート内のパラメーターの名前に一致するパラメーターを指定します。 パラメーター値のために、キー コンテナーのシークレットを参照します。 シークレットを参照するには、Key Vault のリソース識別子とシークレットの名前を渡します。

次のパラメーター ファイルには、キー コンテナーのシークレットが既に存在している必要があり、そのリソース ID に対して静的な値を指定します。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "adminLogin": {
      "value": "exampleadmin"
    },
    "adminPassword": {
      "reference": {
        "keyVault": {
          "id": "/subscriptions/<subscription-id>/resourceGroups/<rg-name>/providers/Microsoft.KeyVault/vaults/<vault-name>"
        },
        "secretName": "ExamplePassword"
      }
    },
    "sqlServerName": {
      "value": "<your-server-name>"
    }
  }
}

現在のバージョン以外のバージョンのシークレットを使用する必要がある場合は、secretVersion プロパティを含めます。

"secretName": "ExamplePassword",
"secretVersion": "cd91b2b7e10e492ebb870a6ee0591b68"

テンプレートをデプロイし、パラメーター ファイルを渡します。

az group create --name SqlGroup --location westus2
az deployment group create \
  --resource-group SqlGroup \
  --template-uri <template-file-URI> \
  --parameters <parameter-file>

動的 ID でのシークレットの参照

前のセクションでは、Key Vault シークレットの静的リソース ID をパラメーターから渡す方法について説明しました。 参照すべきキー コンテナー シークレットがデプロイごとに変わる状況も考えられます。 または、パラメーター ファイルに参照パラメーターを作成するのではなく、テンプレートでパラメーター値を渡すこともできます。 解決策は、リンクされたテンプレートを使用して、キー コンテナー シークレットのリソース ID を動的に生成することです。

パラメーター ファイルではテンプレート式が使用できないので、パラメーター ファイルでリソース ID を動的に生成することはできません。

親テンプレートに、その入れ子にされたテンプレートを追加し、動的に生成されたリソース ID をパラメーターに格納して渡します。 次の図は、リンクされたテンプレート内のパラメーターがシークレットを参照するしくみを示しています。

キー コンテナー シークレット向け動的 ID 生成を示す図。

次のテンプレートは、動的に key vault ID を作成し、パラメーターとして渡します。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
      "location": {
        "type": "string",
        "defaultValue": "[resourceGroup().location]",
        "metadata": {
          "description": "The location where the resources will be deployed."
        }
      },
      "vaultName": {
        "type": "string",
        "metadata": {
          "description": "The name of the keyvault that contains the secret."
        }
      },
      "secretName": {
        "type": "string",
        "metadata": {
          "description": "The name of the secret."
        }
      },
      "vaultResourceGroupName": {
        "type": "string",
        "metadata": {
          "description": "The name of the resource group that contains the keyvault."
        }
      },
      "vaultSubscription": {
        "type": "string",
        "defaultValue": "[subscription().subscriptionId]",
        "metadata": {
          "description": "The name of the subscription that contains the keyvault."
        }
      }
  },
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2020-10-01",
      "name": "dynamicSecret",
      "properties": {
        "mode": "Incremental",
        "expressionEvaluationOptions": {
          "scope": "inner"
        },
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {
            "adminLogin": {
              "type": "string"
            },
            "adminPassword": {
              "type": "securestring"
            },
            "location": {
              "type": "string"
            }
          },
          "variables": {
            "sqlServerName": "[concat('sql-', uniqueString(resourceGroup().id, 'sql'))]"
          },
          "resources": [
            {
              "type": "Microsoft.Sql/servers",
              "apiVersion": "2021-11-01",
              "name": "[variables('sqlServerName')]",
              "location": "[parameters('location')]",
              "properties": {
                "administratorLogin": "[parameters('adminLogin')]",
                "administratorLoginPassword": "[parameters('adminPassword')]"
              }
            }
          ],
          "outputs": {
            "sqlFQDN": {
              "type": "string",
              "value": "[reference(variables('sqlServerName')).fullyQualifiedDomainName]"
            }
          }
        },
        "parameters": {
          "location": {
            "value": "[parameters('location')]"
          },
          "adminLogin": {
            "value": "ghuser"
          },
          "adminPassword": {
            "reference": {
              "keyVault": {
                "id": "[resourceId(parameters('vaultSubscription'), parameters('vaultResourceGroupName'), 'Microsoft.KeyVault/vaults', parameters('vaultName'))]"
              },
              "secretName": "[parameters('secretName')]"
            }
          }
        }
      }
    }
  ]
}

次のステップ