Bedingte Verzweigungen mit ARM Templates

(also available in english)

Wer häufiger mit ARM Templates arbeitet, der kommt früher oder später an eine Stelle, wo ein if...then sinnvoll wäre, oder, mit anderen Worten, eine bedingte Verzweigung. Nun, es gibt ein paar Möglichkeiten, und in diesem Beitrag werden wir uns welche davon ansehen.

Condition

Der wahrscheinlich einfachste Weg ist die Verwendung von condition für eine Ressource. Bei der Definition einer Ressource wird die Direktive einfach irgendwo innerhalb des Ressourcenblocks eingefügt, der besseren Lesbarkeit wegen hier am Anfang:

[code]
"condition": "<true-or-false>",
"type": "<rpnamespace/type>",
"apiVersion": "<api-version>",
...

Es ist so einfach wie es aussieht: Falls der Ausdruck hinter dem condition-Element ein True ergibt, wird die Ressource verteilt (natürlich nur, wenn de Rest der Syntax stimmt...), oder eben nicht. Hier ein oft verwendetes Beispiel:

[code]
"condition": "[equals(parameters('sshOrPW'),'password')]",
"apiVersion": "2016-03-30",
"type": "Microsoft.Compute/virtualMachines",
...
},
{
"condition": "[equals(parameters('sshOrPW'),'ssh')]",
"apiVersion": "2016-03-30",
"type": "Microsoft.Compute/virtualMachines",
...
},

Wir brauchen nur einen Parameter mit den zwei möglichen Eingaben password oder ssh. Der entsprechende Parameter-Abschnitt sei dem Leser zur Übung überlassen...

Die hier verwendete Template Funktion equals ergibt True, wenn beide Argumente gleich sind (wer hätte das gedacht?), ansonsten False. Wenn wir also den Parameter sshOrPW mit "password" belegen, dann wird nur die erste Ressource ausgebracht, die zweite wird ignoriert. Und umgekehrt natürlich. Total einfach, oder? Aber Achtung: Gerade mit mehreren bedingten Ressourcen wird das schnell unübersichtlich, besonders wenn dann auch noch Abhängigkeiten definiert sind. Zum Glück gibt es noch eine Alternative, und zwar mit verschachtelten Templates.

Verschachtelte Templates

Vielleicht hat das der ein oder andere noch nie verwendet, aber man kann tatsächlich mehr als ein Template definieren und dann das eine vom anderen aus aufrufen. Das nennt man dann verschachtelte Templates, und man verwendet dafür den Ressourcentyp Microsoft.Resources/deployments. Einfach ein Name, ein paar Eigenschaften, und in den Eigenschaften steckt die URI zum nächsten Template drin, templatelink. Mehr dazu auf AzureDocs.

[code]
{
"apiVersion": "2017-05-10",
"name": "linkedTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "incremental",
"templateLink": {
"uri": "[variables('subnetTemplate')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
...
}
}
}

Die Idee, um das zu einer bedingten Verzweigung zu machen, ist wieder ganz einfach: Wir rufen Template A oder Template B auf, in Abhängigkeit von einer Variablen oder einem Parameter. Klar? Vielleicht ein Beispiel? Das gibt es auch als Download auf
GitHub oder kann - wie unten gezeigt - auch direkt aufgerufen werden.

Zuerst brauchen wir einen Parameter, um zu entscheiden, ob wir ein Subnetz anlegen oder nicht:

[code]
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"subnetYesNo":{
"type": "string",
"defaultValue": "Yes",
"allowedValues": [
"Yes",
"No"
]
}
},

Dann brauchen wir die Links zu den alternativen Templates, und speichern das in einer Variable. Wir definieren auch gleich noch den passenden Namen für das Sub-Deployment, das macht das Debugging übersichtlicher...

[code firstline="15"]
"variables": {
"subnetTemplate": "[concat('https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]",
"subnetTemplateName": "[concat('Subnet-',parameters('subnetYesNo'))]",
},

Die entscheidende Stelle nochmal in Großaufnahme:
[code gutter="false"]
"[concat('https://.../test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]"

Wir verwenden hier die Funktionen concat und toLower. Ich denke, jeder weiß, was die tun (oder schaut hier nach). Wir bauen also den Dateinamen auf, indem wir den übergebenen Parameter integrieren. Wir könnten auch vorher eine entsprechende Variable zusammenbauen und dann hier verwenden. Abhängig von der Eingabe rufen wir jetzt also das Template subnetTemplate auf, das entweder auf deploysubnet-yes.json oder deploysubnet-no.json endet:

[code firstline="19"]
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"name": "[variables('subnetTemplateName')]",
"properties": {
"mode": "Incremental",
"templateLink": { "uri": "[variables('subnetTemplate')]" },
}
}
],
"outputs": {
}
}

Für dieses einfache Beispiel verwenden wir zwei identische (leere) Templates (mit den oben zusammengebauten Namen) und starten das Deployment mit dem -Verbose Switch. Wir sehen so was ähnliches wie das hier:

[code gutter="false"]
PS C:\temp> New-AzureRmResourceGroupDeployment -Name "conditional02" -ResourceGroupName vstest1-mcd -verbose -subnetYesNo No -TemplateUri "https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/azuredeploy.json"

AUSFÜHRLICH: Ausführen des Vorgangs "Creating Deployment" für das Ziel "vstest1-mcd".
AUSFÜHRLICH: 14:37:04 - Template is valid.
AUSFÜHRLICH: 14:37:05 - Create template deployment 'conditional02'
AUSFÜHRLICH: 14:37:05 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:10 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is running
AUSFÜHRLICH: 14:37:10 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:15 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is succeeded

DeploymentName : conditional02
ResourceGroupName : vstest1-mcd
ProvisioningState : Succeeded
Timestamp : 10.08.2017 12:37:14
Mode : Incremental
TemplateLink :
Parameters :
Name Type Value
=============== ========================= ==========
subnetYesNo String No

Outputs :
DeploymentDebugLogLevel :

Es wurde also wie zu sehen ist das zweite Deployment "Subnet-No" aufgerufen, wie erwartet.

Ausblick

Es gibt noch andere Wege, um zu entscheiden, ob ein Template aufgerufen werden soll oder nicht. Zum Beispiel kann man einen Array füllen mit den alternativen URLs, und dann etwas Mathe-Magie nutzen, um einen Index zu ermitteln, und übergibt dann das entsprechende Array-Element (wie in meinem nächsten Artikel beschrieben). Der Grundgedanke ist aber immer gleich: Man ruft über ein verschachteltes Template weitere Deployments mit verschiedenen URLs auf.

Für heute genug. Aber da werden noch ein paar weitere Artikel zu Azure Resource Manager Templates folgen. Dranbleiben!

Warum nicht in der Zwischenzeit mit einem kostenlosen Azure Testaccount anfangen? Hier der Link für die Microsoft Cloud Deutschland...