У меня есть два приложения с функциями Azure, которые используют слоты развертывания, этап и производство. Эти два приложения-функции Azure содержат около 50 пар ключ: значение в параметрах приложения для определения различных ключей API, поведения приложения, строк подключения и т. Д.
Я хочу развернуть эти два приложения функций Azure в пяти разных средах (CI, DEV, QA, STG, PROD). Я считаю, что развертывание этих ресурсов в Azure с использованием шаблонов ARM - лучший выбор по сравнению с Azure CLI. Для этого я создам задачи в моем конвейере выпуска Azure DevOps.
Чтобы разбить шаблон ARM на что-то, что легко поддерживать, я хотел создать файл параметров шаблона ARM для каждой среды. При определении файла развертывания для функции Azure одним из определяемых свойств является объект siteConfig, в котором вы определяете объект appSettings с помощью объекта NameValuePair. Для каждой среды этап и рабочий слот будут иметь разные ключи API, строки подключения и поведение приложения. В моем файле развертывания создается приложение-функция Azure как с рабочим, так и с рабочим слотом. В файле развертывания я должен дважды указать объект appSettings NameValuePair. Затем мне нужно создать 5 разных файлов параметров для каждой среды. Умножьте это на 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
}
}
}
В дополнение к моей жалобе ... Я не могу поверить, что мне придется создать 20 файлов параметров для всех пяти сред и их двух функций Azure, которые имеют два слота. Есть ли лучший способ развертывания во всех моих средах и их слотах развертывания с использованием шаблонов ARM и файлов параметров с их уникальными настройками приложения?
ОБНОВЛЕНИЕ:
Я смог собрать воедино различные методы для создания шаблонов ARM для конкретной среды и пришел к следующему результату с некоторыми неудобными проблемами. Сначала я объясню, где я сейчас нахожусь, а затем подниму проблемы, связанные с дизайном.
В моем шаблоне развертывания я определил два параметра. Они здесь:
"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": ""
}
}
}
}
}
Для каждой среды я разместил все свои строки подключения, apikeys и настройки приложения.
Для производственного слота для приложения-функции вы можете добавить свойство «ресурсы», которое применяет к нему конфигурацию. Вот полное развертывание приложения-функции:
{
"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.
Именно тогда я начал переносить некоторые настройки приложения в шаблон развертывания, чтобы использовать функции шаблона. Поэтому вместо того, чтобы иметь APPINSIGHTS_INSTRUMENTATIONKEY и другие параметры приложения AzureWebJobs * в файле parameters.json, я предоставил объект siteConfig в объекте" properties "для ресурс Microsoft.Web / Sites и Ресурс Microsoft.Web / Sites / Slots.
Это настоящий облом - при запуске развертывания он применял значения 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 или ресурса конфигурации для применения параметров приложения, а затем использовать Azure CLI для развертывания параметров приложения. Полагаю, я мог бы научиться извлекать секреты Key Vault с помощью Azure CLI или задач конвейера Azure DevOps, но было бы еще лучше, если бы все это было в одном шаблоне ARM.
Для справки, вот весь мой шаблон развертывания, когда я пытался использовать динамически сгенерированные appSettings и ресурс config для определения дополнительных 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.