Terraform Scaffolding für Azure – jetzt secretless mit OIDC

Inhalt

Das Problem mit Secrets in CI/CD-Pipelines

Secrets in CI/CD-Pipelines haben ein Ablaufdatum. Und irgendwann vergisst jemand, sie rechtzeitig zu erneuern. Wer Infrastructure as Code mit Terraform auf Azure betreibt, kennt das Spiel: Ein Service Principal wird erstellt, das Client Secret landet im Key Vault oder direkt in den Pipeline-Variablen, und alles läuft. Bis das Secret abläuft. Oder geleakt wird. Oder jemand vergisst, es zu rotieren.

Die Realität sieht so aus:

  • Secrets müssen rotiert werden und das regelmäßig. Wer das vergisst, riskiert Ausfälle.
  • Secrets können geleakt werden. Ob durch Logs, fehlerhafte Konfiguration oder kompromittierte Systeme.
  • Secrets sind Angriffsvektoren. Ein gestohlenes Client Secret gibt vollen Zugriff auf Azure-Ressourcen.

Die gute Nachricht: Es gibt einen besseren Weg. Mit Workload Identity Federation (OIDC) können GitHub Actions und Azure DevOps Pipelines sich direkt bei Azure authentifizieren ganz ohne gespeicherte Secrets.

In diesem Beitrag zeigen wir, wie unser aktualisiertes Terraform Scaffolding für Azure diesen Ansatz out-of-the-box unterstützt und wie Sie in wenigen Minuten auf “secretless” umsteigen können.

Was ist Terraform Scaffolding für Azure?

Bevor man mit Terraform auf Azure loslegen kann, braucht man ein solides Fundament: Einen Service Principal mit den richtigen Berechtigungen, ein Storage Account für den Terraform State und bisher einen sicheren Ort für Secrets. Genau das haben wir vor einiger Zeit mit unserem Terraform Scaffold for Azure gelöst. Wer den ursprünglichen Ankündigungspost noch nicht kennt: Das Repository bietet ein einfaches Setup-Skript, das in wenigen Minuten alles Nötige provisioniert.

Was bekommen Sie Out-of-the-Box?

KomponenteBeschreibung
Service PrincipalMit Contributor- und Role Based Access Control Administrator-Rolle (eingeschränkt durch Conditions)
Storage AccountFür den Terraform State mit aktivierter Soft-Delete-Policy
Entra ID BerechtigungenDer Service Principal erhält Graph API Permissions (Group.ReadWrite.All, GroupMember.ReadWrite.All, User.Read.All), um mit dem azuread Provider Gruppen und Identitäten zu verwalten. Erfordert Admin Consent.
Bash & PowerShell SupportSkripte für beide Umgebungen

Das Ziel war von Anfang an: In unter 10 Minuten produktionsbereit ohne manuelles Klicken im Azure Portal.

Was ist neu? OIDC als Default

Die größte Änderung im Repository: OIDC ist jetzt der Standard.

Bisher lag der Fokus auf dem klassischen Ansatz mit Client Secret + Key Vault. Das funktioniert, bringt aber die bekannten Nachteile mit sich. Secrets müssen sicher gespeichert, rotiert und verwaltet werden.

Mit dem Update haben wir die Struktur umgestellt:

  • Root Verzeichnis: Workload Identity Federation für GitHub Actions oder Azure DevOps – empfohlen
  • client-secret Verzeichnis: Klassischer Ansatz mit Key Vault – für Szenarien, in denen OIDC nicht möglich ist

Was bedeutet “secretless”?

Bei der OIDC-Variante wird kein Client Secret mehr erstellt oder gespeichert. Stattdessen stellt GitHub Actions oder Azure DevOps ein kurzlebiges Token aus, das Azure direkt gegen die Federated Credential validiert. Das Ergebnis:

  • Kein Secret im Key Vault
  • Kein Secret in Pipeline-Variablen
  • Kein Secret, das rotiert werden muss
  • Kein Secret, das geleakt werden kann

Der Service Principal existiert weiterhin, aber ohne permanente Credentials.

Warum ist OIDC sicherer?

Mit Client Secrets gibt es immer ein Zeitfenster, in dem ein gestohlenes Secret missbraucht werden kann. Selbst wenn das Secret nie absichtlich geteilt wird, kann es durch Debug-Logs, fehlerhafte Maskierung oder kompromittierte Backups nach außen gelangen. Einmal geleakt, hat ein Angreifer vollen Zugriff, bis jemand das Secret bemerkt und rotiert.

Bei OIDC existiert dieses Zeitfenster praktisch nicht. Das Token, das GitHub ausstellt, ist nur wenige Minuten gültig und an einen spezifischen Workflow-Run gebunden. Selbst wenn es abgefangen würde, wäre es bereits abgelaufen, bevor ein Angreifer es nutzen könnte.

Noch wichtiger: Die Kontrolle liegt nicht mehr beim Secret, sondern bei der Herkunft. Entra ID prüft, ob das Token tatsächlich von GitHub stammt, ob es für das richtige Repository ausgestellt wurde und ob das Environment übereinstimmt. Ein Fork des Repositories oder ein Workflow aus einem anderen Branch wird abgelehnt, selbst wenn der Angreifer Zugriff auf die GitHub Secrets hat.

Vergleich: Client Secret vs. OIDC

AspektClient SecretOIDC (Workload Identity Federation)
Secret-Rotation nötig?Ja, alle 1-2 JahreNein
Leak-RisikoVorhandenEliminiert
Kontrolle über QuelleKeine (wer das Secret hat, hat Zugriff)Repo, Branch, Environment
Empfohlen von MicrosoftLegacyBest Practice

Wie funktioniert Workload Identity Federation?

Für alle, die verstehen wollen, was unter der Haube passiert, hier ist der Ablauf der OIDC-Authentifizierung dargestellt.

Der Authentifizierungsfluss in vier Schritten:

  • GitHub Actions / Azure DevOps startet und fordert ein OIDC-Token vom eigenen Identity Provider an
  • Der CI/CD-Provider stellt ein signiertes JWT aus, das Informationen enthält wie: Repository, Branch, Environment, Workflow
  • Das Token wird an Entra ID gesendet, zusammen mit der Client ID des Service Principals
  • Entra ID validiert das Token gegen die konfigurierte Federated Credential – stimmen Issuer, Subject und Audience überein, wird ein Azure Access Token ausgestellt
Azure Access Token

Die Federated Credential definiert drei Prüfpunkte. Nur wenn alle drei Felder exakt übereinstimmen, wird der Zugriff gewährt. Ein Token aus einem anderen Repository oder Branch wird abgelehnt.

FeldBeispiel GitHubBeispiel Azure DevOps
Issuerhttps://token.actions.githubusercontent.comhttps://vstoken.dev.azure.com/<org-id>
Subjectrepo:org/repo:environment:prodsc://org/project/service-connection
Audienceapi://AzureADTokenExchangeapi://AzureADTokenExchange

Quick Start: In 5 Minuten secretless auf Azure

Hier ist die Kurzanleitung, um das Terraform Scaffolding mit OIDC aufzusetzen.

Voraussetzungen

  • Azure CLI installiert und authentifiziert (az login)
  • Bash mit jq oder PowerShell
  • Azure-Berechtigungen: Subscription Owner (oder Contributor + User Access Administrator) sowie Application Developer in Entra ID

Schritt 1: Repository klonen

  • git clone https://github.com/whiteducksoftware/terraform-scaffold-for-azure.git
  • cd terraform-scaffold-for-azure

Schritt 2: die .env Datei mit den gewünschten Namen anpassen

Schritt 3: Federated Credential konfigurieren

  • Für GitHub Actions – federated_credential_github.json anpassen
  • Für Azure DevOps – federated_credential_ado.json anpassen

Schritt 4: Setup-Skript ausführen (hier up.sh als Beispiel)

In up.sh das richtige Credential-File auswählen und ausführen:

  • # FEDERATED_CREDENTIAL_FILE=”federated_credential_github.json” # für GitHub
  • # FEDERATED_CREDENTIAL_FILE=”federated_credential_ado.json” # für Azure DevOps

Schritt 5: CI/CD-Pipeline konfigurieren (GitHub Actions Beispiel)

name: TF Deploy
on:
  workflow_dispatch:
  push:
    branches:
      - 'main'

# OIDC-Authentifizierung erfordert diese Permission.
# Ohne 'id-token: write' kann der Workflow kein OIDC-Token anfordern.
permissions:
  id-token: write
  contents: read

env:
  tf_version: 1.14.3
  # Aktiviert OIDC für den AzureRM Terraform Provider.
  # Damit entfällt ARM_CLIENT_SECRET komplett.
  ARM_USE_OIDC: true
  # Nutzt Entra ID Auth für den Storage Account statt Access Keys.
  ARM_USE_AZUREAD: true

jobs:
  deploy-dev:
    name: Deploy DEV
    runs-on: ubuntu-latest
    # Das Environment muss exakt mit dem 'subject' in der Federated Credential übereinstimmen.
    # Beispiel: repo:org/repo:environment:dev
    # Ein Workflow ohne passendes Environment wird von Entra ID abgelehnt.
    environment: dev
    env:
      stage: dev
      rg_name: '<your-resource-group-name>'
      sa_name: '<your-storage-account-name>'
      sc_name: '<your-blob-name>'
      # Diese drei Werte sind KEINE Secrets im klassischen Sinne.
      # Sie sind öffentliche Identifier – ein Angreifer kann damit alleine nichts anfangen.
      # Die Authentifizierung erfolgt ausschließlich über das signierte OIDC-Token.
      ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
      ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
      ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # OIDC-Login: Kein client-secret Parameter!
      # Die Action fordert automatisch ein OIDC-Token von GitHub an
      # und tauscht es bei Entra ID gegen ein Azure Access Token.
      - name: Azure CLI login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.ARM_CLIENT_ID }}
          tenant-id: ${{ secrets.ARM_TENANT_ID }}
          subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }}

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.tf_version }}

      # Terraform nutzt die OIDC-Session aus dem azure/login Step.
      # Keine zusätzlichen Credentials nötig – ARM_USE_OIDC übernimmt den Rest.
      - name: Terraform Init
        run: |
          terraform init -input=false \
              -backend-config="resource_group_name=${{ env.rg_name }}" \
              -backend-config="storage_account_name=${{ env.sa_name }}" \
              -backend-config="container_name=${{ env.sc_name }}" \
              -backend-config="key=${{ env.stage }}.tfstate"

      - name: Terraform Format
        run: terraform fmt --check

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        run: |
          terraform plan \
           -var-file=./env/${{ env.stage }}.tfvars \
           -out ${{ env.stage }}_tfplan.out

      - name: Terraform Apply
        run: terraform apply -auto-approve ${{ env.stage }}_tfplan.out

Migration: Von Client Secret zu Workload Identity Federation

Wer das Terraform Scaffolding bereits mit Client Secret nutzt, kann mit wenigen Schritten auf OIDC umsteigen.

Azure DevOps

Um auf dem einfachsten Weg die bestehende Azure DevOps Service Connection und den Service Principal auf Federated Credentials umzustellen, geht man wie folgt vor:

  • Eine weitere Service Connection vom Typ „Azure Resource Manager“ anlegen.
  • „App registration or managed identity (manual)“ auswählen.
  • Als Credential „Workload Identity Federation“ setzen.
  • Einen Namen vergeben und die eigene Tenant ID eintragen.
  • Im nächsten Schritt den Issuer und den Subject Identifier kopieren und für später bereithalten.
  • Die Service Connection anschließend fertigstellen und speichern.
  • Zu Entra ID wechseln und den Service Principal editieren.
  • Unter „Certificates and secrets“ ein Federated Credential anlegen und als Scenario „Other issuer“ auswählen.
  • Issuer und Subject Identifier aus Schritt 6 einfügen und speichern.
  • Die Pipeline anpassen, damit diese die neue Service Connection verwendet. Danach kann die alte Service Connection mit dem Credential-Typ „Secret“ entfernt werden.
  • Das Secret beim Service Principal sollte ebenfalls gelöscht werden.

GitHub

Für die Migration auf federated identity credential für GitHub sind folgende Schritte notwendig:

  • Zu Entra ID wechseln und den Service Principal editieren.
  • Unter „Certificates and secrets“ ein Federated Credential anlegen und als Scenario „GitHub Actions deploying Azure resources“ auswählen.
  • Organisation, Repository und Entity Type so setzen, dass folgender Subject identifier dabei rauskommt: repo:myorg/myrepo:environment:dev (hier das Environment dev als Beispiel).
  • Das Secret beim Service Principal sollte ebenfalls gelöscht werden.

In beiden Fällen muss die Pipeline bzw. der Workflow auf die Secret-freie Authentifizierung angepasst werden.

Fazit

Secrets in CI/CD-Pipelines gehören der Vergangenheit an zumindest für die Authentifizierung bei Azure. Mit Workload Identity Federation bietet Microsoft einen sicheren, wartungsfreien Weg, um GitHub Actions und Azure DevOps Pipelines mit Azure zu verbinden. Kein Client Secret, das rotiert werden muss. Kein Leak-Risiko durch versehentlich geloggte Credentials. Keine nächtlichen Pipeline-Ausfälle, weil jemand die Rotation vergessen hat.

Unser aktualisiertes Terraform Scaffolding für Azure macht den Einstieg so einfach wie möglich:

  • OIDC ist jetzt der Default – die sichere Variante ist gleichzeitig die einfachste
  • GitHub Actions und Azure DevOps werden beide unterstützt
  • Ein Skript, fünf Minuten und die Infrastruktur steht

Falls Sie Fragen, Probleme oder Verbesserungsvorschläge haben, kontaktieren Sie uns! Wir helfen gerne weiter und freuen uns über Issues und Pull Requests im Repository.