Jak dobrze parametryzować szablony ARM #1

Szablony Resource Managera w Azure to genialna sprawa. Ich deklaratywność pozwala łatwo zacząć opisywać infrastrukturę zamiast ją skryptować czy też co gorsza namiętnie wyklikiwać. Niestety z szablonami jest problem, a dokładniej z przykładami i materiałami, które uczą, jak je pisać. Obiekty są robione na sztywno zamiast budowane dynamiczne z parametrów lub zmiennych. Maszyny wirtualne, dyski, podsieci, karty sieciowe, NSG, ustawienia w WebApp czy Functions, aż proszą się o takie podejście do wdrażania

W praktyce ludzie wpisują to na sztywno w szablon zamiast korzystać z dobrej parametryzacji. Dobrym przykładem są podsieci w virtual network. Często spotykam się z tym, że są jawnie deklarowane w zasobach w szablonie. Poniżej jak to wygląda dla takiej konfiguracji obiektu. Czyli mamy parametry dla subnet1 (subnet1Name i subnet1AddressPrefix) i potem analogicznie dla podsieci dwa i trzy.

{
    "apiVersion": "2018-02-01",
    "name": "[parameters('name')]",
    "type": "Microsoft.Network/virtualNetworks",
    "location": "[parameters('location')]",
    "properties": {
        "addressSpace": {
            "addressPrefixes": [
                "[parameters('addressPrefix')]"
            ]
        },
        "subnets": [
            {
                "name": "[parameters('subnet1Name')]",
                "properties": {
                    "addressPrefix": "[parameters('subnet1AddressPrefix')]"
                }
            },
            {
                "name": "[parameters('subnet2Name')]",
                "properties": {
                    "addressPrefix": "[parameters('subnet2AddressPrefix')]"
                }
            },
            {
                "name": "[parameters('subnet2Name')]",
                "properties": {
                    "addressPrefix": "[parameters('subnet2AddressPrefix')]"
                }
            }
        ]
    }
}

O wiele lepiej wygląda to jeśli użyjemy tablicy obiektów jako parametr. Jest czytelniej, prościej do parametryzacji i nie wymaga edycji samego szablonu, jeśli coś zmieniamy. Dodatkowo kopiuj/wklej jest prostsze jeżeli chcemy użyć tego w innym miejscu.

[
    {
        "name": "Subnet1",
        "cidr": "172.16.1.0/24"
    },
    {
        "name": "Subnet2",
        "cidr": "172.16.2.0/24"
    }
]

Żeby wykorzystać takie tablice obiektów należy skorzystać z funkcji copy. W opisanym podejściu jest to copy we właściwościach obiektu. W miejscu, gdzie ma być zbudowana lista obiektów wstawiamy copy. W tym wypadku funkcja przyjmuje 3 parametry.

Tak wygląda użycie copy do generowania podsieci.

{
    "apiVersion": "2018-02-01",
    "name": "[parameters('name')]",
    "type": "Microsoft.Network/virtualNetworks",
    "location": "[parameters('location')]",
    "properties": {
        "addressSpace": "[variables('addressPrefixes')]",
        "copy": [
            {
                "name": "subnets",
                "count": "[length(parameters('subnets'))]",
                "input": {
                    "name": "[parameters('subnets')[copyIndex('subnets')].name]",
                    "properties": {
                        "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].cidr]"
                    }
                }
            }
        ]
    }
}

input to obiekt, który w wyniku działania copy ma się pojawić w wygenerowanym jsonie. Generacja jego może się odbywać z wykorzystaniem całego dobrodziejstwa inwentarzu dostępnego w składni szablonów, czyli funkcji, zmiennych, parametrów czy właściwości innych obiektów. W przypadku, kiedy naszym wejściem jest tablica, to wystarczy się po prostu odwołać do aktualnej pozycji funkcji copy i po tym wybrać interesujący nas element z tablicy. Do aktualnej pozycji dostajemy się poprzez copyIndex, czyli kiedy nasze copy nazywa się subnets to wywołanie wygląda tak - copyIndex('subnets’).

Mając pozycje trzeba odwołać się do tablicy. Jak w prawie każdym języku robimy to <tablica>[<index>], czyli parameters('subnets')[copyIndex('subnets')] i w ten sposób uzyskujemy dostęp do elementu w tablicy.

Jako, ze w tablicy mamy obiekty to odwojujemy się do konkretnej właściwość po kropce, czyli parameters('subnets')[copyIndex('subnets')].<nazwa właściwości>. Dla przykładu odwołanie się do nazwy to parameters('subnets')[copyIndex('subnets')].name. input z copy prezentuje się tak:

"input": {
    "name": "[parameters('subnets')[copyIndex('subnets')].name]",
    "properties": {
        "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].cidr]"
    }
}

Obiekt subnets zostanie rozwinięty do takiego stanu i wdrożony.

"subnets": [
    {
        "name": "Subnet1",
        "properties": {
            "addressPrefix": "172.16.1.0/24"
        }
    },
    {
        "name": "Subnet2",
        "properties": {
            "addressPrefix": "172.16.2.0/24"
        }
    }
]

Kolejną rzeczą do takiej dynamicznej parametryzacji jest addressSpace. addressSpace posiada właściwość addressPrefixes, która jest listą adresacji dla wirtualnej sieci. W pierwszym przykładzie jest on definiowany jako jeden element na stałe. Dodanie nowej adresacji w takiej konfiguracji wymaga za każdym razem edycji szablonu.

"addressSpace": {
    "addressPrefixes": [
        "[parameters('addressPrefix')]"
    ]
}

Fajnie jest adresację przekazać jako tablice.

[ "172.16.0.0/16", "10.0.0.0/16" ]

Niestety nie można przypisać o tak sobie tablicy z parametru czy zmiennej w przypadku niektórych właściwości (takim przypadkiem jest virtual network i addressSpace) i trzeba zrobić drobny hack z użyciem obiektu.

Obiekt przygotowujemy w zmiennej addressPrefixes. W takim obiekcie można już przypisać tablice.

"variables": {
    "addressPrefixes" : {
        "addressPrefixes" : "[parameters('addressPrefixs')]"
    }
}

Użycie przygotowanej zmiennej we właściwościach sieci.

 "addressSpace": "[variables('addressPrefixes')]",

Wygenerowany zostanie z tego taki szablon w trakcie wdrożenia

    "addressSpace": {
      "addressPrefixes": [
        "172.16.0.0/16",
        "10.0.0.0/16"
      ]
    }

Dokumentacja użycia copy wraz z przykładami

Na koniec drobne ogłoszenie. W najbliższych miesiącach planuje organizować lub współorganizować dwa wydarzenia w społeczności związane z nauka ARM. Jeśli chcesz być powiadomiony możesz zapisać się na listę tutaj albo czekać na informację na LinkedIn, Twitter czy Facebooku na mojej stronie lub też polskiej grupie Azure.

Cały omówiony szablon dla sieci