Шаблоны ARM для Функций Azure с множеством настроек приложения для различных сред и слотов

У меня есть два приложения с функциями 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.


person Anthony Klotz    schedule 17.12.2019    source источник


Ответы (5)


Извините, у меня не так много времени, чтобы ответить, и у вас есть куча вопросов, которые касаются в основном того, «как лучше всего ...», и ответ всегда - «в зависимости от обстоятельств».

Мне проще управлять одной вещью: вместо использования siteConfig для установки всех параметров приложения вы можете создать ресурс верхнего уровня типа Microsoft.Web/sites/config (который иногда мне кажется полезным, поскольку вы можете создавать их после создания сайта, поэтому, если у вас есть зависимости в других местах, которые еще не настроены, может быть удобно разделить конфигурацию и сайт).

"parameters": {
  "appSettings": {
    "type": "object",
    "defaultValue": {
      "property1": "value1",
      "property2": "value2"
    }
  }
}

"resources": [
  {
    "type": "Microsoft.Web/sites",
    "apiVersion": "2018-11-01",
    "name": "[parameters('function-app-name')]",
    "location": "[parameters('location')]",
    "kind": "functionapp",
    "properties": {
      "enabled": true,
      "serverFarmId": "..."
    }
  },
  {
    "type": "Microsoft.Web/sites/config",
    "name": "[concat(parameters('function-app-name'), '/appsettings')]",
    "apiVersion": "2018-11-01",
    "properties": "[parameters('appSettings')]"
    "dependsOn": [ "[resourceId('Microsoft.Web/sites/sites', parameters('function-app-name'))]",
  }
]

Одним из недостатков вышеизложенного является то, что вы не можете использовать определенные функции в разделе params, поэтому вы не можете использовать listKeys () для получения ключа к ресурсу, поэтому это полезно только иногда, или как в этом примере, если вы хотите добавить ссылку на аналитику приложения, которая также создается в том же шаблоне, это невозможно, если вы передаете настройки в качестве параметра.

  {
    "type": "Microsoft.Web/sites/config",
    "name": "[concat(parameters('function-app-name'), '/appsettings')]",
    "apiVersion": "2018-11-01",
    "properties": {
      "property1": "value1",
      "property2": "value2",
      "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
    }
    "dependsOn": [ 
      "[resourceId('Microsoft.Web/sites/sites', parameters('function-app-name'))]",
      "[resourceId('microsoft.insights/components', variables('appInsightsName'))]"
  }

Вы действительно должны разрешать все, что вы можете во время развертывания, поэтому строку подключения учетной записи хранения (например) можно безопасно добавить в шаблон и разрешить только во время развертывания.

Еще один полезный совет - использовать хранилище ключей для хранения любых защищенных учетных данных, ключей API, строк подключения и т. Д., Которые не могут быть разрешены в шаблоне. Вы упоминаете, что они нужны, но затем передаете их в систему контроля версий в шаблонах ... Что ж, они не будут оставаться в секрете очень долго (еще один совет, убедитесь, что все они используют securestring вместо строковых типов, иначе портал покажет их в журналы развертывания для группы ресурсов). Вы можете получить доступ к хранилищам ключей в настройках приложения следующим образом:

"secretConnectionString": "[concat('@Microsoft.KeyVault(SecretUri=https://', variables('vaultName'), '.vault.azure.net/secrets/my-connection-string/)')]",

Но для того, чтобы все вышеперечисленное работало, вам необходимо предоставить вашему приложению доступ для чтения к хранилищу vaultName, что должно быть хорошо, если вы используете идентификаторы управляемых служб.

person Daniel Morritt    schedule 18.12.2019
comment
Привет, Даниэль, мне удалось в некоторой степени достичь того, к чему я стремился, но решение не так уж и велико. Кажется, что API для управления настройками приложения удаляют все настройки приложения и полностью заменяют их. В верхней части моего файла шаблона я определил два параметра, которые обрабатывают развертывания, зависящие от среды. Параметр для среды развертывания и еще один, который определяет параметры приложения и получает все параметры приложения из файла parameters.json в виде объекта. Затем я использую обозначение квадратных скобок, чтобы получить нужные мне параметры среды. Придется объяснять в отдельном ответе. - person Anthony Klotz; 24.12.2019
comment
Я действительно обновил исходный пост с обновлением. Взгляните туда. - person Anthony Klotz; 24.12.2019
comment
Извините, я прочитал это на днях и забыл ответить, вы можете посмотреть union, и вы по-прежнему сможете использовать метод @Microsoft.KeyVault() для хранилища ключей. Конечно, это сложно, но если вы используете переменные, вы сможете разрешить любые строки подключения или другие секреты хранилища ключей, которые вы не можете использовать с помощью метода @Microsoft.KeyVault. - person Daniel Morritt; 02.01.2020
comment
Можно ли установить appSettings в цикле? Я хотел бы иметь шаблон, который позволяет мне добавлять несколько очередей хранилища в учетную запись хранилища и для каждой из очередей добавлять appSetting с указанием имени очереди, например: "AzureStorageQueueName__<QueueName>": "<QueueName>". Есть примеры использования копирующего элемента, но я получаю сообщение об ошибке The parameters property has an invalid value. - person Simmetric; 10.09.2020

Можно комбинировать статическую конфигурацию со ссылками во время развертывания. Вы используете функцию шаблона union , чтобы объединить вашу статическую конфигурацию (объект или массив) с некоторым значением времени развертывания, которое вы обертываете с помощью json шаблонная функция.

В приведенном ниже примере я комбинирую:

  • статический базовый объект конфигурации
  • статический объект конфигурации для конкретной службы
  • значение конфигурации Application Insights во время развертывания
[union(
  variables('appServiceBaseConfig'), 
  variables('appService1'), 
  json(
    concat(
      '{\"APPINSIGHTS_INSTRUMENTATIONKEY\":\"', 
      reference(concat('microsoft.insights/components/', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey,
       '\"}')
    )
  )
]
person sortofbusy    schedule 25.04.2020
comment
Возможно, ваше решение не соответствовало тому, что искал исходный плакат, но оно определенно помогло мне решить мою проблему. Мне нужен был способ поместить некоторые параметры в файл шаблона, чтобы они могли получить доступ к функциям шаблона ARM без перезаписи остальными параметрами, поступающими из файлов параметров. - person PoorInRichfield; 14.05.2020
comment
Это замечательно, поскольку позволяет использовать функции времени выполнения (reference(), listKeys()). Спасибо, что поделился! - person victor; 11.09.2020
comment
@PoorInRichfield Можете ли вы привести более подробный пример использования этой функции объединения? Вы используете его внутри ресурса Microsoft.Web / sites / config или внутри типа: Microsoft.Web / sites в разделе siteConfig - ›appSettings? - person Oliver Nilsen; 23.12.2020

Чтобы ответить на этот вопрос:

Верно ли также, что все параметры, определенные в файле параметров, должны быть определены в файле шаблона развертывания в объекте параметров?

Да, все, что есть в файле параметров, должно быть определено в файле развертывания. Обратное неверно. Все, что определено в вашем файле развертывания, не нужно определять в вашем файле параметров. Определение в файле развертывания может иметь значение по умолчанию:

"location": {
  "type": "string",
  "defaultValue": "Central US",
  "metadata": {
    "description": "Specifies the Azure location where the key vault should be created."
  }
},

В качестве альтернативы параметр можно передать как параметр переопределения в задаче выпуска.

person DreadedFrost    schedule 18.12.2019

просто добавив ввод в этот поток, чтобы добавить свое решение этой проблемы. Я не уверен, что проблема перезаписи параметров приложения была исправлена ​​в обычных шаблонах ARM, но этап конвейера выпуска позволяет вам установить дополнительные параметры приложения, которые развертываются при развертывании приложения. Итак, у меня есть динамические настройки приложения / настройки приложения, которые все мои функции Azure необходимо определить при создании, установленном в шаблоне ARM, а затем у меня есть дополнительные настройки приложения, установленные в конвейере выпуска (с использованием группы переменных с секретными переменными, чтобы скрыть их ценности). Примерно так:  снимок состояния конвейера выпуска

person Michelle    schedule 30.12.2020

Для всех, кто заходит в эту ветку, я хотел бы предложить альтернативу предложенному выше предложению об использовании union. Сначала я выбрал этот вариант, но мне стало трудно использовать конкатенацию строк, образующих json, поэтому я хотел предложить эту альтернативу:

[union(
  variables('appServiceBaseConfig'), 
  variables('appService1'), 
  createObject(
    'APPINSIGHTS_INSTRUMENTATIONKEY', reference(concat('microsoft.insights/components/', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey,
    'MY_VALUE', 'xyz'
  )
 )
]

Причина этого дополнительного предлагаемого ответа заключается в том, что функция createObject значительно упрощает создание объекта, если у вас есть несколько пар ключей и значений.

Примечание. Подобные многострочные функции в настоящее время поддерживаются ТОЛЬКО в ARM, если вы используете команду Powershell в соответствии с это примечание в документации. Мне потребовалось время, чтобы понять это, пытаясь развернуть в Azure DevOps: facepalm:

person Dave Storey    schedule 10.07.2021