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…