Implementar um transformador de propriedades e um recoletor num modelo de Resource Manager do Azure

No artigo Utilizar objetos como parâmetros num ciclo de cópia num modelo de Resource Manager do Azure, pode ver como armazenar valores de propriedade de recursos num objeto e como aplicá-los a um recurso durante a implementação. Esta é uma forma muito útil de gerir os parâmetros, mas requer que mapeie as propriedades do objeto para propriedades de recursos sempre que utilizar o objeto no seu modelo.

Para contornar esta situação, pode implementar um modelo de transformação de propriedades e recoletor que itera a matriz do objeto e o transforma no esquema JSON do recurso.

Importante

Esta abordagem requer que tenha uma compreensão aprofundada das Resource Manager modelos e funções.

Vejamos um exemplo que implementa um recoletor de propriedades e um transformador para implementar um grupo de segurança de rede. O diagrama abaixo mostra como os nossos modelos estão relacionados com os recursos nesses modelos:

arquitetura do recoletor de propriedades e do transformador

O nosso modelo de chamada inclui dois recursos:

  • Uma ligação de modelo que invoca o nosso modelo de recoletor
  • O recurso do grupo de segurança de rede a implementar

O nosso modelo de recoletor inclui dois recursos:

  • Um recurso de âncora
  • Uma ligação de modelo que invoca o modelo de transformação num ciclo de cópia

O nosso modelo de transformação inclui um único recurso: um modelo vazio com uma variável que transforma o nosso source JSON no esquema JSON esperado pelo nosso recurso do grupo de segurança de rede no modelo principal.

Objeto de parâmetro

Utilizamos o nosso securityRules objeto de parâmetro de Utilizar objetos como parâmetros num ciclo de cópia num modelo de Resource Manager do Azure. O nosso modelo de transformação transforma cada objeto na matriz no securityRules esquema JSON esperado pelo recurso do grupo de segurança de rede no nosso modelo de chamada.

{
    "$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"
                    }
                ]
            }
        }
    }
}

Vamos ver primeiro o nosso modelo de transformação .

Transformar modelo

O nosso modelo de transformação inclui dois parâmetros que são transmitidos a partir do modelo de recoletor:

  • source é um objeto que recebe um dos objetos de valor de propriedade da matriz de propriedades. No nosso exemplo, cada objeto da securityRules matriz é transmitido um de cada vez.
  • state é uma matriz que recebe os resultados concatenados de todas as transformações anteriores. Esta é a coleção de JSON transformado.

Os nossos parâmetros têm o seguinte aspeto:

{
    "$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": []
        }
    },

O nosso modelo também define uma variável denominada instance que transforma o nosso source objeto no esquema JSON necessário:

"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]"
            }
        }
    ]
}

Por fim, o output do nosso modelo concatena as transformações recolhidas do nosso state parâmetro com a transformação atual efetuada pela nossa instance variável:

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

Em seguida, vamos ver o nosso modelo de recoletor para ver como transmite os nossos valores de parâmetros.

Modelo de recoletor

O nosso modelo de recoletor inclui três parâmetros:

  • source é a nossa matriz completa de objetos de parâmetros. É transmitido pelo modelo de chamadas. Tem o mesmo nome que o source parâmetro no nosso modelo de transformação, mas há uma diferença fundamental: embora seja a matriz completa, só transmitimos um elemento de matriz de cada vez para o modelo de transformação.
  • transformTemplateUri é o URI do nosso modelo de transformação. Definimo-lo como um parâmetro para a reutilização de modelos.
  • state é uma matriz inicialmente vazia que transmitimos para o nosso modelo de transformação. Armazena a coleção de objetos de parâmetros transformados após a conclusão do ciclo de cópia.

Os nossos parâmetros têm o seguinte aspeto:

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

Em seguida, definimos uma variável com o nome count. O respetivo valor é o comprimento da matriz do source objeto de parâmetro:

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

Utilizamo-lo para o número de iterações no nosso ciclo de cópia.

Agora, vamos ver os nossos recursos. Definimos dois recursos:

  • loop-0 é o recurso baseado em zero para o nosso ciclo de cópia.
  • loop- é concatenado com o resultado da copyIndex(1) função para gerar um nome baseado em iteração exclusivo para o nosso recurso, começando por 1.

Os nossos recursos têm o seguinte aspeto:

"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]" }
            }
        }
    }
]

Vamos analisar mais detalhadamente os parâmetros que transmitimos ao nosso modelo de transformação no modelo aninhado. Lembre-se de que o nosso source parâmetro transmite o objeto atual na matriz de source objetos de parâmetros. O state parâmetro é onde a coleção acontece, porque utiliza a saída da iteração anterior do ciclo de cópia e transmite-a para a iteração atual. Repare que a reference() função utiliza a copyIndex() função sem parâmetros para referenciar o name objeto de modelo ligado anterior.

Por fim, o output do nosso modelo devolve a output última iteração do nosso modelo de transformação:

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

Pode parecer contraintuitivo devolver a output última iteração do nosso modelo de transformação ao nosso modelo de chamada, porque parece que o armazenámos no nosso source parâmetro. No entanto, é a última iteração do nosso modelo de transformação que contém a matriz completa de objetos de propriedade transformados e é isso que queremos devolver.

Por fim, vamos ver como chamar o modelo de recoletor a partir do nosso modelo de chamadas.

Modelo de chamada

O nosso modelo de chamada define um único parâmetro com o nome networkSecurityGroupsSettings:

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

Em seguida, o nosso modelo define uma única variável com o nome collectorTemplateUri:

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

Este é o URI do modelo de recoletor utilizado pelo nosso recurso de modelo ligado:

{
    "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')]"
            }
        }
    }
}

Transmitimos dois parâmetros ao modelo de recoletor:

  • source é a nossa matriz de objetos de propriedade. No nosso exemplo, é o nosso networkSecurityGroupsSettings parâmetro.
  • transformTemplateUri é a variável que acabamos de definir com o URI do nosso modelo de recoletor.

Por fim, o nosso Microsoft.Network/networkSecurityGroups recurso atribui diretamente o output recurso do collector modelo ligado à respetiva securityRules propriedade:

"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]"
    }
}

Experimentar o modelo

Está disponível um modelo de exemplo no GitHub. Para implementar o modelo, clone o repositório e execute os seguintes comandos da CLI do Azure :

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

Passos seguintes