ステージと運用のデプロイ スロットを使用する 2 つの Azure 関数アプリがあります。これら 2 つの Azure Function アプリには、さまざまな API キー、アプリケーションの動作、接続文字列などを定義するために、Application Settings に約 50 ~ のキーと値のペアがあります。
これら 2 つの Azure Function アプリを 5 つの異なる環境 (CI、DEV、QA、STG、PROD) にデプロイしたいと考えています。ARM テンプレートを使用してこれらのリソースを Azure にデプロイすることは、Azure CLI よりも優れた選択肢であると私は信じています。これを実現するために、Azure DevOps リリース パイプラインでタスクを作成します。
ARM テンプレートを保守しやすいものに分解するために、環境ごとに ARM テンプレート パラメーター ファイルを作成したいと考えました。Azure Function のデプロイ ファイルを定義する場合、定義するプロパティの 1 つはsiteConfig オブジェクトです。ここで、nameValuePair オブジェクトを使用して appSettings オブジェクトを定義します。環境ごとに、ステージと運用スロットは異なる API キー、接続文字列、およびアプリケーションの動作を持ちます。私のデプロイ ファイルは、運用スロットとステージ スロットの両方で Azure Function アプリを作成します。デプロイ ファイルでは、appSettings NameValuePair オブジェクトを 2 回指定する必要があります。次に、環境ごとに 5 つの異なるパラメーター ファイルを作成する必要があります。スロットが 2 つあるので、これを 2 倍します。
また、パラメーター ファイルで定義されたすべてのパラメーターは、パラメーター オブジェクトの配置テンプレート ファイルで定義されなければならないというのは本当ですか?
パラメーター ファイルから NameValuePairs を使用してオブジェクトの配列を渡すだけでよいので、デプロイ ファイルの上部と関数アプリの siteConfig.appSettings の下にパラメーターのリスト全体を定義する必要はありませんか?
ここのドキュメントは、文字列の配列または多数のキー:値を持つ単一のオブジェクトのみを提供できることを示しています。ただし、appSettings はオブジェクトの配列であり、各オブジェクトには 3 つのキーと値のペアがあります。
これは、デプロイ ファイルでリソースがどのように見えるかです。パラメーター ファイルからオブジェクトの配列全体を単純に参照したいのですが、デプロイ ファイルの先頭に 50 個のパラメーターすべてを定義するとドキュメントに記載されているようで、Azure CLI またはAzure DevOps タスク。
{
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"name": "[parameters('function-app-name')]",
"location": "[parameters('location')]",
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"kind": "functionapp",
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
"siteConfig": {
"appSettings": [] # I need to provide an array of objects here
}
}
}
私の不満に加えて... 5 つの環境すべてと 2 つのスロットを持つ 2 つの Azure 関数に対して 20 個のパラメーター ファイルを作成する必要があるとは信じられません。独自のアプリケーション設定で ARM テンプレートとパラメーター ファイルを使用して、すべての環境とそのデプロイ スロットにデプロイするより良い方法はありますか?
アップデート:
環境固有の ARM テンプレートを作成するためのさまざまな方法を組み合わせることができ、次のような結果になりましたが、いくつかの不便な問題がありました。まず、現在の状況を説明してから、設計に関連する問題を取り上げます。
私の展開テンプレートでは、2 つのパラメーターを定義しました。どうぞ:
"deploymentEnvironment": {
"type": "string",
"allowedValues": [
"CI",
"DEV",
"QA",
"TRN",
"STG",
"PROD"
],
"metadata": {
"description": "Type of environment being deployed to. AKA \"Stage\" in release definition."
}
},
"applicationSettings": {
"type": "object",
"metadata": {
"description": "Application settings from function.parameters.json"
}
}
私の function.parameters.json は次のような構造を持っています:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"applicationSettings": {
"value": {
"CI": {
"appsetting1": "",
"appsetting2": ""
},
"DEV": {
"appsetting1": "",
"appsetting2": "" },
"QA": {
"appsetting1": "",
"appsetting2": ""
}
}
}
}
}
環境ごとに、すべての接続文字列、API キー、およびアプリケーション設定を配置しました。
関数アプリの運用スロットには、構成を適用する "resources" プロパティを追加できます。関数アプリのデプロイ全体を次に示します。
{
"name": "[parameters('function-app-name')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"kind": "functionapp",
"location": "[parameters('location')]",
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Insights/components/', variables('applicationInsightsName'))]",
"[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
],
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2018-11-01",
"properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
]
}
]
}
次は、ステージ スロットのデプロイ リソースを定義しました。ここにあります:
{
"type": "Microsoft.Web/sites/slots",
"apiVersion": "2018-11-01",
"name": "[concat(parameters('function-app-name'), '/stage')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
],
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"kind": "functionapp",
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2018-11-01",
"properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]",
"[resourceId('Microsoft.Web/sites/slots/', parameters('function-app-name'), 'stage')]"
]
}
]
}
このソリューションを使用すると、環境ごとにたくさんの parameters.json ファイルを用意する必要がなくなります。
問題点...
parameters.json ですべてのアプリケーション設定を定義すると、テンプレート関数を使用して接続文字列や Azure Key Vault 値を取得できなくなります。
これは、テンプレート機能を使用するために、アプリケーション設定の一部を配置テンプレートに移動し始めたときです。したがって、parameters.json ファイルに APPINSIGHTS_INSTRUMENTATIONKEY とその他の AzureWebJobs* アプリケーション設定を含める代わりに、 Microsoft.Web/Sites リソースとMicrosoft.Web/Sites/Slots リソースの "プロパティ" オブジェクトにsiteConfig オブジェクトを指定しました。
これは本当に残念です - デプロイが実行されると、関数アプリで siteConfig.appsettings 値が適用され、次に parameters.json ファイルが適用されると、アプリケーション設定が削除され、マージする代わりに json からのものだけが適用されました。それらを一緒に。それは大きな失望でした。AzureCLI を使用した最初のテストでは、このコマンドを使用az functionapp config appsettings set --name $functionAppName --resource-group $resourceGroupName --settings $settingsFile --slot $slot
して、json ファイルに含まれていないアプリケーション設定で何が起こるかをテストしましたが、アプリケーション設定が削除されないことに満足しました。powershell コマンドは、値を取得して設定し、適切にマージして削除しません。ただし、ARM API は、これらの名前と値のペアをすべて削除し、定義されているものだけを適用します。つまり、テンプレート関数を使用して動的アプリケーション設定を作成したり、json ファイルを使用して静的アプリケーション設定を適用したりすることはできません。
今のところ、適切な ARM テンプレートのデプロイを行う唯一の方法は、siteConfig オブジェクトまたは config リソースを使用せずにリソースをデプロイしてアプリケーション設定を適用し、Azure CLI をフォローアップしてアプリケーション設定をデプロイすることだと思います。Azure CLI または Azure DevOps パイプライン タスクを使用して Key Vault シークレットを取得する方法を学べると思いますが、すべてを 1 つの ARM テンプレートにまとめたほうがよいでしょう。
参考までに、動的に生成された appSettings と構成リソースを使用してさらに appsettings を定義しようとしたときのデプロイ テンプレート全体を以下に示します。
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"function-app-name": {
"defaultValue": "functionappname",
"type": "String",
"metadata": {
"description": "The name of the function app that you wish to create."
}
},
"sku": {
"type": "string",
"allowedValues": [
"S1",
"S2",
"S3"
],
"defaultValue": "S3",
"metadata": {
"description": "The pricing tier for the hosting plan."
}
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"metadata": {
"description": "Storage Account type"
}
},
"location": {
"type": "string",
"defaultValue": "southcentralus",
"metadata": {
"description": "Location for all resources."
}
},
"deploymentEnvironment": {
"type": "string",
"allowedValues": [
"CI",
"DEV",
"QA",
"TRN",
"STG",
"PROD"
],
"metadata": {
"description": "Type of environment being deployed to."
}
},
"applicationSettings": {
"type": "object",
"metadata": {
"description": "Application settings from function.parameters.json"
}
}
},
"variables": {
"storageAccountName": "[concat('store', uniquestring(resourceGroup().id))]",
"appServicePlanName": "[concat('ASP-', uniquestring(resourceGroup().id))]",
"applicationInsightsName": "[concat('appInsights-', uniquestring(resourceGroup().id))]",
"projectName": "DV"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageAccountName')]",
"kind": "Storage",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
}
},
{
"name": "[variables('appServicePlanName')]",
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2019-08-01",
"location": "[parameters('location')]",
"properties": {
},
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"sku": {
"Name": "[parameters('sku')]",
"capacity": 2
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
]
},
{
"name": "[variables('applicationInsightsName')]",
"apiVersion": "2015-05-01",
"type": "Microsoft.Insights/components",
"kind": "web",
"location": "[parameters('location')]",
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"properties": {
"Application_Type": "web"
}
},
{
"name": "[parameters('function-app-name')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"kind": "functionapp",
"location": "[parameters('location')]",
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(concat('microsoft.insights/components/', variables('applicationInsightsName'))).InstrumentationKey]"
},
{
"name": "AzureWebJobsDashboard",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~1"
}
]
}
},
"dependsOn": [
"[resourceId('Microsoft.Insights/components/', variables('applicationInsightsName'))]",
"[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
],
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2018-11-01",
"properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
]
}
]
},
{
"type": "Microsoft.Web/sites/slots",
"apiVersion": "2018-11-01",
"name": "[concat(parameters('function-app-name'), '/stage')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
],
"tags": {
"project": "[variables('projectName')]",
"env": "[parameters('deploymentEnvironment')]"
},
"kind": "functionapp",
"properties": {
"enabled": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(concat('microsoft.insights/components/', variables('applicationInsightsName'))).InstrumentationKey]"
},
{
"name": "AzureWebJobsDashboard",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~1"
}
]
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2018-11-01",
"properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
]
}
]
}
}
]
}
更新 2:
私は github の問題を提起して、ARM テンプレートが各展開のすべてのアプリケーション設定を置き換えるという問題を修正してもらいました。 FWIW - Azure フィードバック投稿にも投票しました。