Implémenter un transformateur et un collecteur de propriétés dans un modèle Azure Resource Manager
Dans l’article Utiliser des objets en tant que paramètres dans une boucle de copie dans un modèle Azure Resource Manager, vous pouvez voir comment stocker des valeurs de propriété de ressource dans un objet et comment les appliquer à une ressource pendant le déploiement. Il s'agit d’une méthode très utile pour gérer vos paramètres, mais elle exige que vous fassiez correspondre les propriétés de l’objet aux propriétés des ressources chaque fois que vous utilisez l’objet dans votre modèle.
Pour contourner ce problème, vous pouvez implémenter un modèle de transformateur et de collecteur de propriétés qui itère votre tableau d’objets et le transforme en schéma JSON pour la ressource.
Important
Pour utiliser cette approche, vous devez disposer d’une connaissance approfondie des modèles et fonctions Resource Manager.
Examinons un exemple qui implémente un collecteur de propriétés et un transformateur pour déployer un groupe de sécurité réseau. Le diagramme ci-dessous montre comment nos modèles sont liés aux ressources de ces modèles :
Notre modèle d’appel inclut deux ressources :
- Un lien de modèle qui appelle notre modèle de collecteur
- Ressource de groupe de sécurité réseau à déployer
Notre modèle de collecteur inclut deux ressources :
- Une ressource ancre
- Un lien de modèle qui appelle le modèle de transformation dans une boucle de copie
Notre modèle de transformateur ne comporte qu’une seule ressource : un modèle vide avec une variable qui transforme notre code JSON source
en schéma JSON attendu par notre ressource de groupe de sécurité réseau dans le modèle principal.
Objet de paramètre
Nous utilisons notre objet de paramètre securityRules
à partir de Utiliser des objets en tant que paramètres dans une boucle de copie dans un modèle Azure Resource Manager. Notre modèle de transformateur transformera chaque objet du tableau securityRules
en schéma JSON attendu par la ressource de groupe de sécurité réseau dans notre modèle d’appel.
{
"$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"
}
]
}
}
}
}
Commençons par examiner notre modèle de transformateur.
Modèle de transformateur
Notre modèle de transformateur comporte deux paramètres qui sont transmis par le modèle de collecteur :
-
source
est un objet qui reçoit l’un des objets de valeur de propriété du tableau de propriétés. Dans notre exemple, les différents objets du tableausecurityRules
sont transmis un par un. -
state
est un tableau qui reçoit les résultats concaténés de toutes les transformations précédentes. Il s’agit de la collection de code JSON transformé.
Nos paramètres ressemblent à ceci :
{
"$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": []
}
},
Notre modèle définit également une variable nommée instance
qui transforme notre source
objet en schéma JSON requis :
"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]"
}
}
]
}
Enfin, l’élément output
de notre modèle concatène les transformations collectées de notre paramètre state
avec la transformation actuelle effectuée par notre variable instance
:
"resources": [],
"outputs": {
"collection": {
"type": "array",
"value": "[concat(parameters('state'), variables('instance'))]"
}
}
À présent, examinons notre modèle de collecteur pour découvrir la façon dont il transmet nos valeurs de paramètre.
Modèle de collecteur
Notre modèle de collecteur comporte trois paramètres :
-
source
est notre tableau d’objets de paramètre complet. Il est transmis par le modèle d’appel. Il a le même nom que le paramètresource
dans notre modèle de transformation, mais il existe une différence clé : bien qu’il s’agisse du tableau complet, nous ne transmettons qu’un seul élément de tableau à la fois au modèle de transformation. -
transformTemplateUri
est l’URI de notre modèle de transformateur. Nous le définissons ici sous la forme d’un paramètre pour permettre la réutilisation du modèle. -
state
est un tableau initialement vide que nous transmettons à notre modèle de transformateur. Il stocke la collection d’objets de paramètre transformés lorsque la boucle de copie est terminée.
Nos paramètres ressemblent à ceci :
"parameters": {
"source": {
"type": "array"
},
"transformTemplateUri": {
"type": "string"
},
"state": {
"type": "array",
"defaultValue": []
}
}
Ensuite, nous définissons une variable nommée count
. Sa valeur correspond à la longueur du tableau d’objets de paramètre source
:
"variables": {
"count": "[length(parameters('source'))]"
}
Nous l’utilisons pour le nombre d’itérations dans notre boucle de copie.
À présent, examinons nos ressources. Nous définissons deux ressources :
-
loop-0
est la ressource de base zéro pour notre boucle de copie. -
loop-
est concaténé avec le résultat de la fonctioncopyIndex(1)
afin de générer pour notre ressource un nom unique basé sur l’itération, en commençant par1
.
Nos ressources ressemblent à ceci :
"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]" }
}
}
}
]
Examinons plus attentivement les paramètres que nous transmettons à notre modèle de transformateur dans le modèle imbriqué. Comme indiqué plus haut, notre paramètre source
transmet l’objet actuel au tableau d’objets de paramètre source
. Le paramètre state
est l’endroit où la collecte se produit, car il prend la sortie de l’itération précédente de notre boucle de copie et la passe à l’itération actuelle. Notez que la fonction reference()
utilise la fonction copyIndex()
sans paramètre pour référencer le name
de notre objet de modèle lié précédent.
Enfin, l’élément output
de notre modèle renvoie l’élément output
de la dernière itération de notre modèle de transformateur :
"outputs": {
"result": {
"type": "array",
"value": "[reference(concat('loop-', variables('count'))).outputs.collection.value]"
}
}
Il peut sembler contre-intuitif de renvoyer le output
de la dernière itération de notre modèle de transformation à notre modèle d'appel, car il semble que nous l'ayons stockée dans notre paramètresource
. Toutefois, il s’agit de la dernière itération de notre modèle de transformateur qui stocke le tableau complet d’objets de propriété transformés, lequel correspond précisément à l’élément que nous souhaitons renvoyer.
Enfin, examinons la façon dont nous pouvons appeler le modèle de collecteur à partir de notre modèle d’appel.
Modèle d’appel
Notre modèle d’appel définit un paramètre unique nommé networkSecurityGroupsSettings
:
...
"parameters": {
"networkSecurityGroupsSettings": {
"type": "object"
}
}
Ensuite, notre modèle définit une seule variable nommée collectorTemplateUri
:
"variables": {
"collectorTemplateUri": "[uri(deployment().properties.templateLink.uri, 'collector.template.json')]"
}
Il s’agit de l’URI du modèle de collecteur qui sera utilisé par notre ressource de modèle lié :
{
"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')]"
}
}
}
}
Nous transmettons deux paramètres au modèle de collecteur :
-
source
est notre tableau d’objets de propriété. Dans notre exemple, il s’agit de notre paramètrenetworkSecurityGroupsSettings
. -
transformTemplateUri
est la variable que nous venons de définir avec l’URI de notre modèle de collecteur.
Enfin, notre ressource Microsoft.Network/networkSecurityGroups
attribue directement l’élément output
de la ressource de modèle lié collector
à sa propriété 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]"
}
}
Essayer le modèle
Un exemple de modèle est disponible sur GitHub. Pour déployer le modèle, clonez le référentiel et exécutez les commandes Azure CLI suivantes :
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
Étapes suivantes
- Azure Resource Manager
- Que sont les modèles ARM ?
- Tutoriel : Créer et déployer votre premier modèle Resource Manager
- Tutoriel : Ajouter une ressource à votre modèle ARM
- Bonnes pratiques de modèle ARM
- Documentation Azure Resource Manager
- Documentation sur les modèles ARM