Часто при определении шаблона рук вы должны применять одинаковое преобразование снова и снова между ресурсами. Возьмем, к примеру, именование ресурсов, в шаблоне, возможно, вам придется использовать функцию concat () для создания имени ресурса. Даже если это требует всего несколько параметров, это добавляет сложности и часто склонна к ошибкам. То же самое, когда вам нужно сделать расчет комплексов в разделах ресурсов, используя сравнение комплексов и логический оператор. В конце это может быть полный кошмар, когда вам нужно отладить его.
Много раз эти манипуляции с комплексами необходимы более одного раза в шаблоне. Их обновление может иногда трудно. Часто, в конце концов, это может быть полный кошмар, когда вам придется отлаживать его. Если мы сможем централизовать эти манипуляции в одном месте и позвонить им, когда нам это нужно будет отличной функцией.
Эта функция существует, пользовательские функции. Он был введен в 2018 году, и это один из разделов шаблона вместе с ресурсами или переменными.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [], "outputs": {}, "functions": [] }
Функция должна принадлежать пространству имен. Это предотвращает путаницу и помогает определить функцию.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [], "outputs": { "FName" : { "type": "string", "value": "[GetMy.firstName()]" }, "LName" : { "type": "string", "value": "[GetMy.lastName()]" } }, "functions": [ { "namespace": "GetMy", "members": { "firstName": { "parameters": [ ], "output": { "type": "string", "value": "Olivier" } }, "lastName": { "parameters": [ ], "output": { "type": "string", "value": "Miossec" } } } } ] }
Как вы видите, пространство имен может содержать несколько функций, и у вас может быть несколько пространств имен в файле шаблона ARM. Определение функции начинается с имени функции. Он должен быть уникальным в пространстве имен, но это хорошая практика, чтобы сделать его уникальным для всего шаблона, чтобы избежать путаницы.
Каждая функция имеет 2 раздела, параметры и вывод. Разделы параметров позволяют определить ввод функции. Функция может использовать только эти параметры. Функция не может получить доступ к переменным или параметрам шаблона, единственными возможными входами являются параметры. Этот раздел — массив JSON. Он может содержать от 0 до многих параметров. Параметр содержит тип, ожидаемый тип данных JSON, String, Int, Bool, Object, Array, Securestring and SecureBject и имя параметра. Помните, что параметры — единственный способ передать данные на функцию. Невозможно получить доступ к переменным шаблона или другому значению, определенному в шаблоне. Порядок параметров здесь определяет порядок в вызове функции.
Вывод содержит два элемента, тип данных, как и в разделе «Параметры», он должен быть действительным типом данных JSON и значением. Значение может хард -код, как в предыдущем примере (но нет реального значения для этого), или это может быть более сложным выражением шаблона рук. Здесь есть некоторые ограничения, эталонная функция ARM недоступна, вы не можете использовать функции списка или другую пользовательную функцию. Вы можете использовать параметры так же, как и в шаблоне.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [], "outputs": { "Name" : { "type": "string", "value": "[GetMy.lastName('Olivier', 'Miossec')]" } }, "functions": [ { "namespace": "GetMy", "members": { "lastName": { "parameters": [ { "name": "FistName", "type": "string" }, { "name": "LastName", "type": "string" } ], "output": { "type": "string", "value": "[concat(parameters('FistName'), ' ', parameters('LastName'))]" } } } } ] }
Когда использовать пользовательские функции? Иногда вам нужно создавать расчеты комплексов. Представьте себе ситуацию, когда вам нужно развернуть несколько виртуальных машин из шаблона. Все ресурсы должны соответствовать соглашению об именах. Имя виртуальной машины, диска и никаума должно быть построено с помощью префикса, суффикса, представляющего имя ресурса (как -vm), и приращение. Наконец, мы хотим, чтобы все имена ресурсов использовали строчные.
Когда использовать пользовательские функции? Иногда вам нужно создавать расчеты комплексов. Например, представьте себе ситуацию, когда вам нужно развернуть несколько виртуальных машин из шаблона. Все ресурсы должны соответствовать соглашению об именах. Имя виртуальной машины, диска и никаума должно быть построено с помощью префикса, суффикса, представляющего имя ресурса (как -vm), и приращение. Наконец, и мы хотим, чтобы все имена ресурсов использовали строчные.
Имя ресурса должно выглядеть. Prefix-0x- (VM | OSVHD | VHD | NIC)
Без пользовательской функции вам придется использовать функции concat () и tolower () не только для имен ресурсов, но и для Desentson. В этом случае будет трудно обновлять и отлаживать.
Но что, если у нас будет более 9 ВМ для развертывания? Нам нужно проверить приращение, если значение больше, чем 9, нам нужно удалить 0. Давайте посмотрим на функцию.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [], "outputs": { "Name" : { "type": "string", "value": "[resourceName.GetName('appli', 12,'vhd')]" } }, "functions": [ { "namespace": "resourceName", "members": { "GetName": { "parameters": [ { "name": "prefix", "type": "string" }, { "name": "increment", "type": "int" }, { "name": "resourceType", "type": "string" } ], "output": { "type": "string", "value": "[toLower(concat(parameters('prefix'), '-', if( greater(parameters('increment'), 9 ) ,'','0') ,parameters('increment'),'-', parameters('resourceType')))]" } } } } ] }
Чтобы справиться с 0, когда приращение превышает 9, нам нужно использовать логическую функцию с функцией сравнения большего ().
Полный шаблон выглядит так.
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "VMNumber": { "type": "int", "defaultValue": 3, "metadata": { "description": "Nunber of VM to deploy" } }, "Prefix": { "type": "string", "metadata": { "description": "The prefix used to deploy VM" } }, "adminUsername": { "type": "string", "defaultValue": "labadmin", "metadata": { "description": "VM Administrator Username" } }, "adminPassword": { "type": "securestring", "metadata": { "description": "VM Administrator password" } } }, "variables": { "imagePublisher": "MicrosoftWindowsServer", "imageOffer": "WindowsServer", "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', 'testvnet')]", "subnetRef": "[concat(variables('vnetID'),'/subnets/', 'vmapp')]", "location": "[resourceGroup().location]", "osSku": "2019-Datacenter-smalldisk", "vmSize": "Standard_B2s" }, "resources": [ { "apiVersion": "2018-02-01", "type": "Microsoft.Network/networkInterfaces", "name": "[resourceName.GetName(parameters('Prefix'), copyIndex('NicCopy', 1),'nic')]", "location": "[variables('location')]", "properties": { "ipConfigurations": [ { "name": "ipconfig1", "properties": { "privateIPAllocationMethod": "Dynamic", "subnet": { "id": "[variables('subnetRef')]" } } } ] }, "copy": { "name": "NicCopy", "count": "[parameters('VMNumber')]" } }, { "apiVersion": "2018-06-01", "type": "Microsoft.Compute/virtualMachines", "name": "[resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'vm')]", "location": "[variables('location')]", "dependsOn": [ "[concat('Microsoft.Network/networkInterfaces/', resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'nic'))]" ], "properties": { "hardwareProfile": { "vmSize": "[variables('vmSize')]" }, "osProfile": { "computername": "[resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'vm')]", "adminUsername": "[parameters('adminUsername')]", "adminPassword": "[parameters('adminPassword')]" }, "licenseType": "Windows_Server", "storageProfile": { "osDisk": { "osType": "Windows", "name": "[resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'osvhd')]", "caching": "ReadWrite", "createOption": "FromImage", "managedDisk": { "storageAccountType": "Premium_LRS" } }, "dataDisks": [ { "name": "[resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'vhd')]", "createOption": "Empty", "caching": "None", "diskSizeGB": 10, "lun": 0, "managedDisk": { "storageAccountType": "Premium_LRS" } } ], "imageReference": { "publisher": "[variables('imagePublisher')]", "offer": "[variables('imageOffer')]", "sku": "[variables('osSku')]", "version": "latest" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', resourceName.GetName(parameters('Prefix'), copyIndex('VmCopy',1),'nic'))]" } ] } }, "copy": { "name": "VmCopy", "count": "[parameters('VMNumber')]" } } ], "outputs": {}, "functions": [ { "namespace": "resourceName", "members": { "GetName": { "parameters": [ { "name": "prefix", "type": "string" }, { "name": "increment", "type": "int" }, { "name": "resourceType", "type": "string" } ], "output": { "type": "string", "value": "[toLower(concat(parameters('prefix'), '-', if( greater(parameters('increment'), 9 ) ,'','0') ,parameters('increment'),'-', parameters('resourceType')))]" } } } } ] }
Обратите внимание, что используя копию для создания ресурсов. Но по умолчанию CopyIndex () начинается с 0. Чтобы избежать именования ресурсов с 00, нам нужно использовать смещение.
copyIndex('VmCopy',1)
Есть некоторые ограничения, когда вы используете пользовательские функции в шаблоне ARM. Но они могут сэкономить время с шаблонами комплексов. Они берут сложность из раздела ресурсов, и они многоразовые.
Оригинал: «https://dev.to/omiossec/infra-as-code-user-defined-functions-in-arm-templates-987»