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:

bicep
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):

bicep
var location = 'westeurope'

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

bicep
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.

bicep
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:

bicep
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:

bicep
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:

bicep
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”):

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:

powershell
bicep build .\mytemplate.bicep

PowerShell: bicep build .\mytemplate.bicep lÀuft ohne Ausgabe durch

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

json
{
  "$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.

Visual Studio Code nebeneinander: links mytemplate.bicep mit 16 Zeilen, rechts das erzeugte ARM-Template mytemplate.json mit ĂŒber 40 Zeilen 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


Christoph Vollmann
Christoph VollmannSenior Cloud Architect at SoftwareOne and Microsoft MVP for Azure Networking. Routing, DNS, firewalls and Private Endpoints, preferably with Bicep or Terraform.LinkedInGitHubAbout mePhoto: Matthias Piekacz

Comments