Erste Schritte mit Azure Bicep 💪

Anfang letzter Woche hat Microsoft die erste Version (0.1) von Azure Bicep veröffentlicht.

Azure Bicep stellt eine domänenspezifische Sprache (DSL) dar, mit denen wir Azure Ressourcen deklarativ beschreiben können. Die Sprache ist kein ARM Ersatz. Vielmehr werden Bicep Templates in ARM Templates übersetzt und wir werden weiterhin ARM Templates in Azure ausrollen. ARM Templates werden damit zu einer Intermediate Language (IL)

Ziele sind, dass wir wesentlich leichter modulare Templates bauen können, eine saubere und besser lesbare Syntax erhalten und unseren Template Code besser wiederverwenden können.

Voraussetzungen

Zum Start ist nicht viel nötig. Wir benötigen die Bicep CLI und zum leichteren Schreiben die Bicep VS Code Erweiterung.

Die Schritte für macOS, Linux und Windows sind gut dokumentiert im GitHub Repository.

Erstes Template

Heute beginnen wir mit ein paar Grundlagen. Parameter, Variablen, Ressourcen, Ausgaben.

Parameter

Wir legen zunächst einen Parameter fest:

param storageAccountName string = 'mycoolstorage' 

Es ist fast selbsterklärend. “param” ist das Schlüsselwort, danach folgt der Parameter-Name, dann der Parameter-Typ (string, int, bool, array, object) und ein Default Wert.

Variablen

Eine Variable kommt ohne den Typ aus (genau wie in einem ARM Template):

var location = 'westeurope'

Um einen Parameter oder eine Variable zu nutzen, benötigen wir keine Funktionsaufrufe mehr. Einfach den Namen nutzen! Ein Beispiel:

param environment = 'dev'
param appName = 'MyApp'
var vmName = 'vm-${appName}-${environment}' // Ergebnis: "vm-MyApp-dev"

Ressourcen

Bauen wir uns eine Ressource. Einen Storage Account.
Wir benötigen einen Namen, eine Location und eine Angabe über Redundanz.

resource MyStorageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: 'mycoolstorage'
  location: 'westeurope'
  kind: 'Storage'
  sku: {
    name: 'Standard_LRS'
  }
}

“resource” ist wieder das Schlüsselwort, “MyStorageAccount” ist meine Ressourcen-Referenz, die ich später im Template nutzen kann (Achtung: Das ist nicht der Name der Ressource!), dann folgt der Ressourcen-Typ mit API-Version.

Wir sollten ein paar Parameter und Variablen für unser Template nutzen:

param location string = resourceGroup().location
param environment string = 'dev'
param appName string = 'myapp'
var storageAccountName = 'st${appName}${environment}'

Der “Location” Parameter nutzt eine Expression (auch ähnlich zu ARM Templates). Der Ausdruck hier verweist auf die Location der Ressource Group, in der das Deployment ausgeführt wird.

Dazu passen wir die Ressourcen-Beschreibung an:

resource MyStorageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: storageAccountName
  location: location
  kind: 'Storage'
  sku: {
    name: 'Standard_LRS'
  }
}

Wie man an den Eigenschaften “name” und “location” erkennen kann: Sollten wir nichts verketten oder umschreiben, können wir die Variablen oder Parameter ohne weitere Funktionen nutzen. Eine – im Vergleich zu ARM – erfrischend kurze Schreibweise.

Ausgaben

Zum Schluss lassen wir uns noch beispielhaft die ID und den Blob-Endpunkt unseres Storage Accounts ausgeben:

output storageId string = MyStorageAccount.id
output blobEndpoint string = MyStorageAccount.properties.primaryEndpoints.blob

Für Ausgaben ist “output” das Schlüsselwort, dann der Name, dann der Typ. Danach nutzen wir die Ressourcen-Referenz “MyStorageAccount” und greifen auf Eigenschaften zu.
Wir können auf alle Eigenschaften zugreifen, auf die wir auch im ARM-Template Zugriff hätten.

Zu ARM übersetzen

Zunächst die vollständige Bicep-Datei (wir nennen sie “mytemplate.bicep”):

param location string = resourceGroup().location
param environment string = 'dev'
param appName string = 'myapp'
var storageAccountName = 'st${appName}${environment}'

resource MyStorageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
    name: storageAccountName
    location: location
    kind: 'Storage'
    sku: {
      name: 'Standard_LRS'
    }
}

output storageId string = MyStorageAccount.id
output blobEndpoint string = MyStorageAccount.properties.primaryEndpoints.blob

In einer PowerShell können wir jetzt die Datei zu ARM übersetzen lassen:

bicep build .\mytemplate.bicep

Bicep erzeugt eine “mytemplate.json” Datei. Ein, wie wir ja vermutet hatten, ARM-Template:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "environment": {
      "type": "string",
      "defaultValue": "dev"
    },
    "appName": {
      "type": "string",
      "defaultValue": "myapp"
    }
  },
  "functions": [],
  "variables": {
    "storageAccountName": "[format('st{0}{1}', parameters('appName'), parameters('environment'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2019-06-01",
      "name": "[variables('storageAccountName')]",
      "location": "[parameters('location')]",
      "kind": "Storage",
      "sku": {
        "name": "Standard_LRS"
      }
    }
  ],
  "outputs": {
    "storageId": {
      "type": "string",
      "value": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
    },
    "blobEndpoint": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))).primaryEndpoints.blob]"
    }
  }
}

Schon beim Lesen hier im Blog sieht man den Vergleich, wie kurz und knackig Bicep-Dateien im Vergleich zu den sehr “geschwätzigen” ARM-Templates sind.

Links: Azure Bicep Datei, Rechts: Erzeugtes ARM-Template

Was ist gut gelungen?

Die Templates sind wesentlich schmaler und lesbarer. Es erinnert sehr an Terraform.

Automatische Dependencies. Wenn ich eine Resource in einer anderen referenziere, dann fügt Bicep automatisch eine Abhängigkeit (“dependsOn”) ein.

“concat” ist Geschichte. Einfach '${appName}-abc-${environment}' schreiben. Das ist wesentlich leichter lesbar als "[concat(parameters('appName'), '-abc', variables('environment')]".

Referenzen auf andere Ressourcen und deren Eigenschaften ist auch gut gelungen.
Vorher:reference(resourceId('Microsoft.insights/components', variables('appInsightsName'))).InstrumentationKey]"

Nachher: AppInsights.properties.InstrumentationKey

Was funktioniert (noch) nicht?

Bicep ist im Alpha-Stadium (momentan Version 0.1). Das heißt, da gibt es sicher noch jede Menge fehlende Funktionalität.

Was ist mir bisher beim Testen aufgefallen?

  • Keine Parameter-Datei:
    Bicep 0.1 erzeugt noch eine Parameter-Datei. Es steht uns aber frei einfach eine “mytemplate.parameters.json” Datei anzulegen. Dann natürlich im ARM-Format.
  • Subscription Level Templates nutzen momentan das falsche Schema. Es werden nur ARM-Templates mit Resource Group Deployments erzeugt.
    Das erzeugte Template kann man trotzdem auf Subscription-Ebene anwenden.
  • Die API-Version muss fest angegeben werden, du kannst keine Variable dafür nutzen (das ist aber eh gegen die Best Practices)
  • Verschacheltung von Funktionen: Ich habe es nicht geschafft bspw. einen String zusammen zu stellen und diesen dann direkt durch “toLower” zu schicken. Momentan regele ich das über zwei Variablen.

Referenzen, Version, Quellcode

Zum Schluss: Bicep? Warum denn nun gerade Bicep? “Well, your biceps let’s you flex your ARM.” 😎 … Ich bin ja schon weg…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.