Azure Standard Test Deployments (Teil 3)

Im dritten Teil der Serie besprechen wir die Templates, die die restlichen Ressourcen erzeugen sollen.

Dies ist der dritte von fünf Artikeln zu diesem Thema. Die anderen sind:

  • Teil 1 legt die Namenskonvention fest.
  • Teil 2 behandelt VNet und NSG
  • Teil 3 (dieser hier) handelt von der VM und zugehörigen Ressourcen
  • Teil 4 fasst das alles in einem PowerShell Skript zusammen
  • Teil 5 macht noch ein paar Bemerkungen dazu.

Vorbemerkungen

Im letzten Teil haben wir ein VNet und eine NSG erzeugt. Warum eigentlich die Trennung zwischen Teil 2 und Teil 3 dieser Serie? Der Vorteil der Trennung liegt darin, dass ich meistens nur ein VNet brauche, welches aber mehrere VMs enthält. Indem ich das Netzwerk getrennt anlege, kann ich den verbleibenden Teil einfach mehrmals aufrufen. Ich mache also nur einmal Teil 2 und mehrmals Teil 3.

Egal ob man eine Linux- oder eine Windows-VM erzeugt, die jeweiligen Templates schauen fast identisch aus. Insbesondere sind die Teile wie Netzwerkinterface und öffentliche IP Adresse identisch. Ich verwende daher immer mehrere Templates, das erlaubt die Wiederverwertung (quasi mit grünem Punkt :-)). Genauer gesagt verwende ich ein Template für IP-ADresse und Netzwerkinterface, und je ein Template für Windows- oder Linux-VMs.

Das muss ich glaub ich nochmal erklären. Für die Public IP (PIP) und das Netzwerkinterface (NIC) verwende ich ein und das selbe Template, egal ob es sich um Linux oder Windows handelt. Lediglich für die eigentliche VM habe ich zwei verschiedene Templates, eines für Windows, eines für Linux.

Jetzt kommen bestimmt zwei Fragen:
1) Warum dann nicht PIP und NIC zusammen mit dem VNet aus dem letzten Teil anlegen? Ganz einfach: Pro Ressourcegruppe mache ich ein VNet und eine NSG, aber PIP und NIC mache ich pro VM, und das kann ja mehr als einmal sein.

2) Warum keine verlinkten Templates? Ginge natürlich, aber um aus einem Template heraus ein anderes aufrufen zu können, müsste das aufgerufene über eine URI erreichbar sein, das geht leider nicht lokal. Ich könnte das hier auch noch in einem Teil 6 mit Git Repository vorstellen, mal schauen…

PIP und NIC

Hier das Template, diesmal gleich alles auf einmal. Als Parameter brauchen wir ganz allein nur den Namen der späteren VM.

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vmName": {
            "type": "string"
        }
    },
    "variables": {
        "location": "[resourceGroup().location]",
        
        "vnetName":   "[concat(resourceGroup().name,'vnet1')]",
        "subnetName": "[concat(resourceGroup().name,'vnet1sub1')]",
        
        "nicName":    "[concat(parameters('vmName'),'nic1')]",
        "pipName":    "[concat(parameters('vmName'),'pip1')]",
                
        "pipId":  "[resourceId(resourceGroup().name, 'Microsoft.Network/publicIpAddresses', variables('pipName'))]",
        "vnetId":   "[resourceId(resourceGroup().name, 'Microsoft.Network/virtualNetworks', variables('vnetName'))]",
        "subnetId": "[concat(variables('vnetId'), '/subnets/', variables('subnetName'))]",
        
        "pipType": "Static",
        "pipSku": "Standard",
        
    },
    "resources": [
        {   "type": "Microsoft.Network/publicIpAddresses",
            "name": "[variables('pipName')]",
            "apiVersion": "2018-08-01",
            "location": "[variables('location')]",
            "properties": {
                "publicIpAllocationMethod": "[variables('pipType')]"
            },
            "sku": {
                "name": "[variables('pipSku')]"
            }
        },
        {   "type": "Microsoft.Network/networkInterfaces",
            "name": "[variables('nicName')]",
            "apiVersion": "2018-10-01",
            "location": "[variables('location')]",
            "dependsOn": ["[concat('Microsoft.Network/publicIpAddresses/', variables('pipName'))]"],
            "properties": {
                "ipConfigurations": [
                    {
                        "name": "ipconfig1",
                        "properties": {
                            "subnet": {
                                "id": "[variables('subnetId')]"
                            },
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIpAddress": {
                                "id": "[variables('pipId')]"
                            }
                        }
                    }
                ]
            }
        }
    ],
    "outputs": {
    }
}

Auch in diesem Template übernehmen wir sinnvollerweise die Region der Ressourcengruppe. Es folgt die Benennung der Ressourcen analog zu unserem Namensschema aus dem ersten Teil. Man sieht hier den Vorteil der einheitlichen Namen: Wir können für viele Ressourcen den Namen oder die ID einfach setzen, obwohl diese Ressourcen in diesem Template gar nicht vorkommen. Wir „wissen“ aber, wie sie heißen und welche ID sie damit haben. Praktisch, oder?

Möchte man eine Loadbalancer-Umgebung aufbauen, dann bitte auf den Typ der PublicIP achten (Zeile 22,23).

Virtueller Server

Und jetzt endlich die eigentliche VM. Zuerst das komplette Linux-Template:

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vmName": {
            "type": "string"
        },
        "adminUser": {
            "type": "string"
        },
        "adminKey" : {
            "type": "securestring"
        }
    },
    "variables": {
        "location": "[resourceGroup().location]",
        
        "nicName":    "[concat(parameters('vmName'),'nic1')]",
        "nicId":  "[resourceId(resourceGroup().name, 'Microsoft.Network/networkInterfaces', variables('nicName'))]",
        
        "osDiskName": "[concat(parameters('vmName'),'disk1')]",
        "osDiskType": "StandardSSD_LRS",

        "virtualMachineSize": "Standard_D2s_v3"
    },
    "resources": [
        {   "type": "Microsoft.Compute/virtualMachines",
            "name": "[parameters('vmName')]",
            "apiVersion": "2018-06-01",
            "location": "[variables('location')]",
            "properties": {
                "hardwareProfile": {
                    "vmSize": "[variables('virtualMachineSize')]"
                },
                "storageProfile": {
                    "osDisk": {
                        "name": "[variables('osDiskName')]",
                        "createOption": "FromImage",
                        "managedDisk": {
                            "storageAccountType": "[variables('osDiskType')]"
                        }
                    },
                    "imageReference": {
                        "publisher": "Canonical",
                        "offer": "UbuntuServer",
                        "sku": "18.04-LTS",
                        "version": "latest"
                    }
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[variables('nicId')]"
                        }
                    ]
                },
                "osProfile": {
                    "computerName": "[parameters('vmName')]",
                    "adminUsername": "[parameters('adminUser')]",
                    "linuxConfiguration": {
                        "disablePasswordAuthentication": true,
                        "ssh": {
                            "publicKeys": [
                                {
                                    "path": "[concat('/home/', parameters('adminUser'), '/.ssh/authorized_keys')]",
                                    "keyData": "[parameters('adminKey')]"
                                }
                            ]
                        }
                    }
                }
            }
        }
    ],
    "outputs": {
    }
}

Man erkennt, dass ich immer die gleiche Version verwende, wer öfters mal wechselt, der sollte „publisher“, „offer“ etc. in den Variablenteil hochziehen, zum Beispiel in die Nähe der Zeile 24, wo die Größe festgelegt wird. In Zeile 60-70 die Geschichte mit der PublicKey-Authentifizierung…

Dann mal noch das Windows Template, wie gesagt fast identisch:

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vmName": {
            "type": "string"
        },
        "adminUser": {
            "type": "string"
        },
        "adminPassword" : {
            "type": "securestring"
        }
    },
    "variables": {
        "location": "[resourceGroup().location]",
        
        "nicName":    "[concat(parameters('vmName'),'nic1')]",
        "nicId":  "[resourceId(resourceGroup().name, 'Microsoft.Network/networkInterfaces', variables('nicName'))]",
        
        "osDiskName": "[concat(parameters('vmName'),'disk1')]",
        "osDiskType": "StandardSSD_LRS",
        
        "virtualMachineSize": "Standard_D2s_v3"
    },
    "resources": [
        {   "type": "Microsoft.Compute/virtualMachines",
            "name": "[parameters('vmName')]",
            "apiVersion": "2018-06-01",
            "location": "[variables('location')]",
            "dependsOn": [
                "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
            ],
            "properties": {
                "hardwareProfile": {
                    "vmSize": "[variables('virtualMachineSize')]"
                },
                "storageProfile": {
                    "osDisk": {
                        "name": "[variables('osDiskName')]",
                        "createOption": "FromImage",
                        "managedDisk": {
                            "storageAccountType": "[variables('osDiskType')]"
                        }
                    },
                    "imageReference": {
                        "publisher": "MicrosoftWindowsServer",
                        "offer": "WindowsServer",
                        "sku": "2016-Datacenter",
                        "version": "latest"
                    },
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[variables('nicId')]"
                        }
                    ]
                },
                "osProfile": {
                    "computerName": "[parameters('vmName')]",
                    "adminUsername": "[parameters('adminUser')]",
                    "adminPassword": "[parameters('adminPassword')]"
                }
            }
        }
    ],
    "outputs": {
    }
}

Auch hier gelten die Hinweise zu „publisher“ etc. sowie die VM Größe.

Zusammenfassung

Jetzt haben wir eigentlich alles zusammen, um unsere Testumgebungen ausbringen zu können. Ich fasse mal zusammen:

  • Den Beginn eines PowerShell Scripts, um eine Ressourecengruppe anzulegen
  • Ein Template, um pro Gruppe ein VNet, Subnet und eine Netzwerk-Sicherheitsgruppe anzulegen
  • Ein Template, um pro VM eine Public IP Adresse und eine Netzwerkkarte anzulegen
  • Ein Template, um eine Windows-VM anzulegen
  • Ein Template, um eine Linux-VM anzulegen

Jetzt brauchen wir nur noch den Script zu erweitern, um alles richtig zu steuern. Und genau darum geht es im vierten Teil.

4 Kommentare zu „Azure Standard Test Deployments (Teil 3)

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s