Создание преобразователя и сборщика свойств в шаблоне Azure Resource Manager

В статье Использование объектов в качестве параметров в цикле копирования в шаблоне Azure Resource Manager вы узнаете, как хранить значения свойств ресурса в объекте и как применять их к ресурсу во время развертывания. Это очень полезный способ управления параметрами, но при каждом использовании объекта в шаблоне необходимо сопоставлять свойства объекта со свойствами ресурсов.

Чтобы обойти эту проблему, можно реализовать преобразование свойств и шаблон сборщика, который выполняет итерацию массива объектов и преобразует его в схему JSON для ресурса.

Важно!

Такой подход требует глубокого понимания функций и шаблонов Resource Manager.

Рассмотрим пример реализации сборщика свойств и преобразователя для развертывания группы безопасности сети. На схеме ниже показано, как наши шаблоны связаны с ресурсами в этих шаблонах:

Архитектура сборщика и преобразователя свойств

У нас есть вызывающий шаблон с двумя ресурсами:

  • Ссылка на шаблон, которая вызывает шаблон сборщика
  • Ресурс группы безопасности сети для развертывания

Шаблон сборщика также содержит два ресурса:

  • Ресурс привязки
  • Ссылка на шаблон, которая вызывает шаблон преобразования в цикле копирования.

Наш шаблон преобразования включает в себя один ресурс: пустой шаблон с переменной, которая преобразует наш source JSON в схему JSON, ожидаемую ресурсом группы безопасности сети в шаблоне main.

Объект параметров

Мы используем наш securityRules объект параметров из раздела Использование объектов в качестве параметров в цикле копирования в шаблоне Azure Resource Manager. Наш шаблон преобразования преобразует каждый объект в массиве securityRules в схему JSON, ожидаемую ресурсом группы безопасности сети в вызывающем шаблоне.

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "networkSecurityGroupsSettings": {
            "value": {
                "securityRules": [
                    {
                        "name": "RDPAllow",
                        "description": "allow RDP connections",
                        "direction": "Inbound",
                        "priority": 100,
                        "sourceAddressPrefix": "*",
                        "destinationAddressPrefix": "10.0.0.0/24",
                        "sourcePortRange": "*",
                        "destinationPortRange": "3389",
                        "access": "Allow",
                        "protocol": "Tcp"
                    },
                    {
                        "name": "HTTPAllow",
                        "description": "allow HTTP connections",
                        "direction": "Inbound",
                        "priority": 200,
                        "sourceAddressPrefix": "*",
                        "destinationAddressPrefix": "10.0.1.0/24",
                        "sourcePortRange": "*",
                        "destinationPortRange": "80",
                        "access": "Allow",
                        "protocol": "Tcp"
                    }
                ]
            }
        }
    }
}

Изучение шаблонов мы начнем с шаблона преобразования.

Шаблон преобразования

Шаблон преобразования содержит два параметра, которые передаются от шаблона сборщика:

  • объект source, который принимает значение одного из объектов со значениям свойств, полученных из массива свойств. В нашем примере каждый объект из массива securityRules передается по одному.
  • массив state, в который передаются сцепленные результаты всех предыдущих преобразований. Это и есть сборщик преобразованных данных в формате JSON.

Мы используем следующий набор параметров:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "source": {
            "type": "object"
        },
        "state": {
            "type": "array",
            "defaultValue": []
        }
    },

Наш шаблон также определяет переменную с именем instance , которая преобразует наш source объект в требуемую схему JSON:

"variables": {
    "instance": [
        {
            "name": "[parameters('source').name]",
            "properties": {
                "description": "[parameters('source').description]",
                "protocol": "[parameters('source').protocol]",
                "sourcePortRange": "[parameters('source').sourcePortRange]",
                "destinationPortRange": "[parameters('source').destinationPortRange]",
                "sourceAddressPrefix": "[parameters('source').sourceAddressPrefix]",
                "destinationAddressPrefix": "[parameters('source').destinationAddressPrefix]",
                "access": "[parameters('source').access]",
                "priority": "[parameters('source').priority]",
                "direction": "[parameters('source').direction]"
            }
        }
    ]
}

Наконец, output шаблон объединяет собранные преобразования нашего state параметра с текущим преобразованием, выполняемым нашей переменной instance :

"resources": [],
"outputs": {
    "collection": {
        "type": "array",
        "value": "[concat(parameters('state'), variables('instance'))]"
    }
}

Теперь давайте рассмотрим наш шаблон сборщика , чтобы узнать, как он передает значения параметров.

Шаблон сборщика

Шаблон сборщика содержит три параметра:

  • source — это полный массив объекта параметров. Он передается вызывающим шаблоном. Он имеет то же имя, что и source параметр в нашем шаблоне преобразования, но есть одно ключевое отличие: хотя это полный массив, мы передаём в шаблон преобразования только один элемент массива за раз.
  • transformTemplateUri содержит URI шаблона преобразования. Мы определяем его как параметр для повторного использования шаблона.
  • state изначально является пустым массивом, который передается в шаблон преобразования. Он сохраняет коллекцию преобразованных объектов параметров после завершения цикла копирования.

Мы используем следующий набор параметров:

"parameters": {
    "source": {
        "type": "array"
    },
    "transformTemplateUri": {
        "type": "string"
    },
    "state": {
        "type": "array",
        "defaultValue": []
    }
}

Затем определяется переменная с именем count. В качестве значения ей присваивается длина массива объектов параметров source:

"variables": {
    "count": "[length(parameters('source'))]"
}

Мы используем его для количества итераций в цикле копирования.

Теперь давайте посмотрим на ресурсы. Мы определим два ресурса:

  • loop-0 имеет нулевое начальное значение и используется для цикла копирования;
  • loop- объединяется с результатом функции copyIndex(1) для получения уникального имени ресурса для каждой итерации, начиная с 1.

Описание ресурсов выглядит следующим образом:

"resources": [
    {
        "type": "Microsoft.Resources/deployments",
        "apiVersion": "2015-01-01",
        "name": "loop-0",
        "properties": {
            "mode": "Incremental",
            "parameters": { },
            "template": {
                "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                "contentVersion": "1.0.0.0",
                "parameters": { },
                "variables": { },
                "resources": [ ],
                "outputs": {
                    "collection": {
                        "type": "array",
                        "value": "[parameters('state')]"
                    }
                }
            }
        }
    },
    {
        "type": "Microsoft.Resources/deployments",
        "apiVersion": "2015-01-01",
        "name": "[concat('loop-', copyindex(1))]",
        "copy": {
            "name": "iterator",
            "count": "[variables('count')]",
            "mode": "serial"
        },
        "dependsOn": [
            "loop-0"
        ],
        "properties": {
            "mode": "Incremental",
            "templateLink": { "uri": "[parameters('transformTemplateUri')]" },
            "parameters": {
                "source": { "value": "[parameters('source')[copyindex()]]" },
                "state": { "value": "[reference(concat('loop-', copyindex())).outputs.collection.value]" }
            }
        }
    }
]

Давайте подробнее рассмотрим параметры, передаваемые в шаблон преобразования во вложенном шаблоне. Как вы помните, параметр source здесь передает текущий объект в массив объектов параметров source. Параметр state — это место, где происходит коллекция, так как он принимает выходные данные предыдущей итерации цикла копирования и передает их в текущую итерацию. Обратите внимание, что reference() функция использует функцию copyIndex() без параметра для ссылки на name предыдущий связанный объект шаблона.

И наконец, объект output в нашем шаблоне возвращает output выходные данные последней итерации в шаблоне преобразования:

"outputs": {
    "result": {
        "type": "array",
        "value": "[reference(concat('loop-', variables('count'))).outputs.collection.value]"
    }
}

Возврат последней итерации шаблона преобразования в вызывающий шаблон может показаться нелогичнымoutput, так как кажется, что мы сохранили его в параметре source . Однако это последняя итерация нашего шаблона преобразования , в которой содержится полный массив преобразованных объектов свойств, и это то, что мы хотим вернуть.

В конце этой статьи мы рассмотрим способ вызова шаблона сборщика из вызывающего шаблона.

Вызывающий шаблон

Вызывающий шаблон определяет один параметр с именем networkSecurityGroupsSettings:

...
"parameters": {
    "networkSecurityGroupsSettings": {
        "type": "object"
    }
}

Затем этот шаблон определяет одну переменную с именем collectorTemplateUri:

"variables": {
    "collectorTemplateUri": "[uri(deployment().properties.templateLink.uri, 'collector.template.json')]"
}

Это универсальный код ресурса (URI) для шаблона сборщика , который используется нашим ресурсом связанного шаблона:

{
    "apiVersion": "2020-06-01",
    "name": "collector",
    "type": "Microsoft.Resources/deployments",
    "properties": {
        "mode": "Incremental",
        "templateLink": {
            "uri": "[variables('collectorTemplateUri')]",
            "contentVersion": "1.0.0.0"
        },
        "parameters": {
            "source": {
                "value": "[parameters('networkSecurityGroupsSettings').securityRules]"
            },
            "transformTemplateUri": {
                "value": "[uri(deployment().properties.templateLink.uri, 'transform.json')]"
            }
        }
    }
}

Затем мы передаем два параметра в шаблон сборщика:

  • source — это массив объектов свойств. В нашем примере это параметр networkSecurityGroupsSettings.
  • transformTemplateUri — это переменная, которую мы только что определили с помощью универсального кода ресурса (URI) шаблона сборщика.

И наконец, ресурс Microsoft.Network/networkSecurityGroups напрямую связывает выходные данные (output) связанного шаблона collector с его свойством securityRules:

"resources": [
    {
        "apiVersion": "2020-05-01",
        "type": "Microsoft.Network/networkSecurityGroups",
        "name": "networkSecurityGroup1",
        "location": "[resourceGroup().location]",
        "properties": {
            "securityRules": "[reference('collector').outputs.result.value]"
        }
    }
],
"outputs": {
    "instance": {
        "type": "array",
        "value": "[reference('collector').outputs.result.value]"
    }
}

Пробное использование шаблона

Пример шаблона доступен на сайте GitHub. Чтобы развернуть этот шаблон с помощью Azure CLI, выполните следующие команды Azure CLI:

git clone https://github.com/mspnp/template-examples.git
cd template-examples/example4-collector
az group create --location <location> --name <resource-group-name>
az deployment group create -g <resource-group-name> \
    --template-uri https://raw.githubusercontent.com/mspnp/template-examples/master/example4-collector/deploy.json \
    --parameters deploy.parameters.json

Дальнейшие действия