Infrastructure as Code mit Bicep: Der moderne Weg
von Stefan Sørensen

Infrastructure as Code mit Bicep: Der moderne Weg

Bicep ist die moderne Alternative zu ARM Templates. Lernen Sie, wie Sie Azure-Ressourcen deklarativ und type-safe definieren.

AzureIaCBicep

Bicep ist Microsofts neue Domain-Specific Language für Azure Resource Manager. Im Gegensatz zu JSON-basierten ARM Templates bietet Bicep eine deutlich bessere Developer Experience: weniger Boilerplate-Code, klare Syntax und native Tooling-Unterstützung direkt in VS Code. Wer einmal mit Bicep gearbeitet hat, möchte nie wieder rohe ARM-JSON-Dateien schreiben.

Warum Bicep?

  • Type-Safety und IntelliSense – Der Bicep-Compiler erkennt Tippfehler und falsche Ressourcentypen bereits zur Entwicklungszeit, nicht erst beim Deployment.
  • Kürzere und lesbarere Syntax – Vergleichbare ARM Templates sind oft 3–5× länger. Bicep spart durch Weglassen von Boilerplate-Konstrukten erheblich an Zeilen.
  • Native Azure-Integration – Bicep wird direkt von Microsoft entwickelt und unterstützt neue Azure-Ressourcenversionen in der Regel am Tag der GA-Veröffentlichung.
  • Module für Wiederverwendbarkeit – Gemeinsame Infrastrukturbausteine lassen sich als Module kapseln und aus öffentlichen oder privaten Registries referenzieren.
  • Idempotente Deployments – Bicep-Dateien beschreiben den gewünschten Zustand; Azure Resource Manager sorgt dafür, dass die tatsächliche Infrastruktur diesem Zustand entspricht.
  • Nahtlose ARM-Interoperabilität – Jede Bicep-Datei wird deterministisch in ein ARM-Template kompiliert. Es gibt keine versteckte Laufzeitlogik.

Beispiel: Storage Account

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

Dieses Snippet definiert einen Storage Account mit lokal redundantem Speicher (Standard_LRS). Die Variablen storageAccountName und location werden als Parameter übergeben, sodass dieselbe Datei für mehrere Umgebungen wiederverwendet werden kann.

Parameter und Variablen

Sauber getrennte Parameter machen Bicep-Templates umgebungsunabhängig:

@description('Name des Storage Accounts (3–24 Zeichen, nur Kleinbuchstaben und Ziffern)')
@minLength(3)
@maxLength(24)
param storageAccountName string

@description('Azure-Region für alle Ressourcen')
param location string = resourceGroup().location

@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Premium_LRS'
])
param skuName string = 'Standard_LRS'

Decoratoren wie @minLength, @maxLength und @allowed fügen Validierungsregeln direkt im Template hinzu – kein externes Schema nötig. Variablen (var) eignen sich für berechnete Werte, die intern verwendet werden, aber nicht von außen konfigurierbar sein müssen.

Module: Wiederverwendbare Bausteine

Das eigentliche Potenzial von Bicep liegt in seiner Modulstruktur. Komplexe Architekturen lassen sich in handliche, testbare Einheiten aufteilen:

// main.bicep
module storage './modules/storage.bicep' = {
  name: 'storageDeployment'
  params: {
    storageAccountName: 'meinprojektstorage'
    location: location
    skuName: 'Standard_GRS'
  }
}

module appService './modules/appService.bicep' = {
  name: 'appServiceDeployment'
  params: {
    appName: 'mein-api-backend'
    location: location
    storageConnectionString: storage.outputs.connectionString
  }
}

Module kommunizieren über outputs. Dadurch entsteht ein expliziter Abhängigkeitsgraph, den Azure Resource Manager automatisch auflöst – parallele Deployments inklusive.

Outputs und Abhängigkeiten

Outputs geben Werte aus einem Deployment zurück, etwa Connection Strings oder Ressource-IDs, die nachgelagerte Prozesse (CI/CD-Pipelines, andere Module) benötigen:

output storageAccountId string = storageAccount.id
output primaryEndpoint string = storageAccount.properties.primaryEndpoints.blob
output connectionString string = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'

Hinweis: Sensitive Outputs wie Connection Strings sollten in der Produktion über Key Vault References bezogen werden, nicht direkt als Output.

Bicep in der CI/CD-Pipeline

Ein typischer GitHub Actions Workflow für Bicep-Deployments:

name: Deploy Infrastructure

on:
  push:
    branches: [main]
    paths:
      - 'infra/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Azure Login
        uses: azure/login@v2
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Validate Bicep
        run: az deployment group validate \
          --resource-group ${{ vars.RESOURCE_GROUP }} \
          --template-file infra/main.bicep \
          --parameters @infra/parameters.prod.json

      - name: What-If Preview
        run: az deployment group what-if \
          --resource-group ${{ vars.RESOURCE_GROUP }} \
          --template-file infra/main.bicep \
          --parameters @infra/parameters.prod.json

      - name: Deploy
        run: az deployment group create \
          --resource-group ${{ vars.RESOURCE_GROUP }} \
          --template-file infra/main.bicep \
          --parameters @infra/parameters.prod.json

Der what-if-Schritt zeigt vor dem eigentlichen Deployment an, welche Ressourcen erstellt, geändert oder gelöscht werden – ein unverzichtbares Sicherheitsnetz, besonders bei Produktionsinfrastruktur.

Bicep vs. Terraform: Kurzer Vergleich

Kriterium Bicep Terraform
Scope Nur Azure Multi-Cloud (AWS, GCP, Azure, …)
State Management Kein lokaler State, Azure übernimmt Lokale oder Remote State-Datei
Lernkurve Gering (kennt man ARM, ist Bicep intuitiv) Mittel (HCL, State-Konzept, Provider)
Azure-Aktualität Tag-1-Support für neue APIs Oft Verzögerung (Community-Provider)
Tooling VS Code Extension, Azure CLI Terraform CLI, viele IDEs
Modularisierung Bicep Registry, öffentliche Module Terraform Registry, sehr reif

Für reine Azure-Umgebungen ist Bicep die schärfere Wahl. Wer Multi-Cloud oder ein bestehendes Terraform-Setup mitbringt, sollte abwägen.

Best Practices auf einen Blick

  1. Parameter-Dateien pro Umgebungparameters.dev.json, parameters.staging.json, parameters.prod.json halten Konfigurationen sauber getrennt.
  2. Naming Conventions mit Variablen – Ressourcennamen zentral aus Prefix + Umgebung + Region konstruieren, nie hartcodieren.
  3. Bicep Linter nutzenaz bicep build warnt bei Best-Practice-Verstößen; in CI/CD als Pflicht-Schritt einbauen.
  4. Key Vault für Secrets – Passwörter und Connection Strings nie als Klartext-Parameter übergeben, stattdessen getSecret() aus Key Vault referenzieren.
  5. Module versionieren – Module aus einer privaten Azure Container Registry mit expliziten Tags beziehen, nicht aus Branches.
  6. what-if vor jedem Prod-Deployment – Pflicht. Überraschungen in der Produktion sind teuer.

Fazit

Bicep ist kein Hype – es ist die logische Weiterentwicklung von ARM Templates für alle, die ernsthaft Infrastructure as Code auf Azure betreiben. Die Kombination aus Type-Safety, schlanker Syntax und nahtloser Azure-Integration macht es zum natürlichen Standard für Azure-native Teams. Wer heute ein neues Azure-Projekt startet, sollte direkt mit Bicep beginnen, nicht mit ARM-JSON oder als ARM-Krücke gedachten Workarounds.

Der nächste Schritt: Schau dir die offiziellen Bicep-Module auf GitHub an – dort gibt es fertige, gut getestete Module für die gängigsten Azure-Ressourcen, die sofort produktionsreif eingesetzt werden können.

Stefan Sørensen
Stefan Sørensen
Cloud Enterprise Architect