Azure Automation: Runbooks mit Managed Identity und Microsoft Graph
Azure Automation in der Praxis: Automation Account einrichten, System-assigned Managed Identity aktivieren, Microsoft-Graph-Berechtigungen vergeben, im Runbook per Connect-AzAccount -Identity authentifizieren und die richtigen Runbook-Typen wählen.
Wiederkehrende Verwaltungsaufgaben von Hand zu erledigen ist langsam, fehleranfällig und bindet Personal, das an anderer Stelle fehlt. Azure Automation löst genau dieses Problem: Du hinterlegst deine Logik einmalig als Skript – ein sogenanntes Runbook – und lässt sie vom Dienst zeitgesteuert, auf Abruf oder durch ein externes Ereignis in der Cloud ausführen. Der Schlüssel für einen sauberen, produktiven Betrieb liegt dabei in der Authentifizierung: Statt Passwörter oder Secrets im Code zu hinterlegen, nutzt du eine Managed Identity und sprichst darüber Azure- und Microsoft-365-Ressourcen an.
Dieser Artikel zeigt dir den kompletten Weg: vom Aufbau des Automation Accounts über die wichtigen Einstellungen und die Aktivierung der System-assigned Managed Identity, die Vergabe von Microsoft-Graph-Berechtigungen (inklusive der App-Role-IDs), das Holen eines Tokens im Runbook per Connect-AzAccount -Identity bis hin zur Wahl des richtigen Runbook-Typs, der Modul- und Paketverwaltung, Schedules und den Best Practices für den Produktivbetrieb.
Was ist Azure Automation?
Azure Automation ist ein Cloud-Dienst von Microsoft Azure, mit dem sich manuelle, wiederkehrende und fehleranfällige Aufgaben automatisieren lassen. Microsoft fasst den Nutzen in vier Punkten zusammen: Zeitersparnis, Reduktion von Fehlerrisiken, Freisetzung von Personalkapazität und Standardisierung von Abläufen.
Konzeptionell gliedert sich der Dienst in drei Funktionsbereiche:
- Process Automation – das Herzstück: das Ausführen von Runbooks. Darauf liegt der Fokus dieses Artikels.
- Configuration Management – über Azure Automation State Configuration (DSC) lassen sich Soll-Zustände von Maschinen erzwingen und überwachen.
- Shared Capabilities – gemeinsam genutzte Ressourcen wie Zeitpläne, Module, Variablen und Anmeldeinformationen sowie rollenbasierte Zugriffssteuerung, Quellcodeverwaltung und Auditing.
Der Begriff Runbook stammt aus dem klassischen IT-Betrieb, wo ein „Runbook" die schriftliche Handlungsanweisung für eine wiederkehrende Aufgabe war. In Azure ist es die ausführbare, automatisierte Variante davon – in der Praxis ein PowerShell- oder Python-Skript, das der Dienst zuverlässig ausführt.
Der Automation Account
Der Automation Account (Automatisierungskonto) ist die oberste Verwaltungseinheit und der zentrale Container für alles Weitere: Runbooks, Zeitpläne, Shared Resources (Variablen, Credentials, Zertifikate), Module und die Identitätskonfiguration. Er ist eine eigenständige Azure-Ressource innerhalb einer Ressourcengruppe und eines Abonnements (Subscription).
Jobs verschiedener Automation Accounts sind voneinander isoliert, auch wenn sie technisch auf geteilter Infrastruktur laufen. Pro Account erhältst du einen eindeutigen Verschlüsselungsschlüssel, mit dem alle sicheren Ressourcen (Secure Assets) verschlüsselt werden.
Automation Account erstellen
Der Account lässt sich per Portal, ARM/Bicep oder Azure CLI anlegen. Über die CLI reichen wenige Zeilen:
az automation account create `
--name "aa-prod-automation" `
--resource-group "rg-automation" `
--location "germanywestcentral" `
--sku "Basic"
Ein wichtiger Punkt bei der SKU-Wahl: Der SKU „Basic" reicht für die meisten Szenarien. „Free" hat ein Limit von 500 Job-Minuten pro Monat – im Produktivbetrieb solltest du daher immer „Basic" verwenden.
Wichtige Einstellungen nach der Erstellung
Direkt nach dem Anlegen gibt es einige Einstellungen, die du prüfen und anpassen solltest:
| Einstellung | Empfehlung |
|---|---|
| Public network access | Deaktivieren (Private Endpoint nutzen) |
| RunAs Account | NICHT erstellen – veraltet, Managed Identity verwenden |
| Diagnostic Settings | Sofort aktivieren → Log Analytics Workspace |
| Managed Identity | System-assigned aktivieren |
Region und Netzwerk
Automation Account und Log Analytics Workspace sollten in derselben Region liegen. Für DSGVO-relevante Workloads bietet sich germanywestcentral (Frankfurt) an.
Für produktive Umgebungen richtest du einen Private Endpoint ein, damit der Automation Account nicht über das öffentliche Internet erreichbar ist:
az network private-endpoint create `
--name "pe-automation" `
--resource-group "rg-automation" `
--vnet-name "vnet-prod" `
--subnet "snet-endpoints" `
--private-connection-resource-id $(az automation account show --name "aa-prod-automation" --resource-group "rg-automation" --query id -o tsv) `
--group-id "Webhook" `
--connection-name "conn-automation"
Diagnose und Logging aktivieren
Aktiviere direkt nach der Erstellung die Diagnoseeinstellungen und leite Job-Logs und Job-Streams an einen Log Analytics Workspace weiter. So sind Ausführungen später nachvollziehbar und auswertbar:
az monitor diagnostic-settings create `
--name "diag-automation" `
--resource $(az automation account show --name "aa-prod-automation" --resource-group "rg-automation" --query id -o tsv) `
--workspace $(az monitor log-analytics workspace show --resource-group "rg-monitoring" --workspace-name "law-prod" --query id -o tsv) `
--logs '[{"category":"JobLogs","enabled":true},{"category":"JobStreams","enabled":true}]'
Managed Identity: die richtige Authentifizierung
Automatisierung, die Benutzer anlegt oder Konten deaktiviert, ist sicherheitskritisch. Umso wichtiger ist die Frage, wie sich ein Runbook authentifiziert. Die Antwort lautet: Managed Identity.
Eine Managed Identity ist eine von Azure automatisch verwaltete Identität in Microsoft Entra ID, mit der eine Azure-Ressource – hier der Automation Account – sich gegenüber anderen Diensten authentifiziert, ohne dass Passwörter oder Secrets im Code stehen. Azure kümmert sich um Erstellung und Rotation der Anmeldeinformationen. Microsoft empfiehlt Managed Identities ausdrücklich als bevorzugte Authentifizierungsmethode für Runbooks.
Wichtig: Die früheren „RunAs"-Konten wurden am 30. September 2023 eingestellt und durch Managed Identities ersetzt. RunAs-Accounts nutzten Service Principals mit Zertifikaten – veraltet, schwer zu rotieren und nicht mehr empfohlen. Erstelle bei neuen Automation Accounts keinen RunAs-Account mehr.
System-assigned vs. User-assigned
Es gibt zwei Varianten der Managed Identity:
| System-assigned | User-assigned | |
|---|---|---|
| Lebenszyklus | Gebunden an den Automation Account | Unabhängige Azure-Ressource |
| Wiederverwendung | Nein (genau eine pro Ressource) | Ja (mehrere Accounts/Dienste) |
| Empfehlung | Lab, einfache Setups | Produktion, geteilte Identität |
Die System-assigned Identität ist an die Ressource gebunden und wird mit ihr gelöscht – eine Ressource hat genau eine. Die User-assigned Identität ist eine eigenständige Azure-Ressource, die mehreren Diensten zugewiesen werden kann – ideal für geteilte Berechtigungssätze und einen sauberen Lebenszyklus.
Für einfache Setups und den Einstieg reicht die System-assigned Variante. In diesem Artikel arbeiten wir damit.
System-assigned Managed Identity aktivieren
Die Aktivierung ist ein einzelner Befehl. Anschließend liest du die Object ID (Principal ID) aus – diese brauchst du später, um Berechtigungen zu vergeben:
az automation account update `
--name "aa-prod-automation" `
--resource-group "rg-automation" `
--assign-identity "[system]"
# Object ID auslesen (wird für Berechtigungen benötigt)
$objectId = az automation account show `
--name "aa-prod-automation" `
--resource-group "rg-automation" `
--query identity.principalId -o tsv
Im Bereich Identität des Automation Accounts wird die System-assigned Managed Identity per Statusschalter auf „Ein" aktiviert.
Zwei Berechtigungsebenen verstehen
Greift ein Runbook auf Azure- oder Microsoft-365-Ressourcen zu, braucht die Managed Identity Berechtigungen auf zwei unabhängigen Ebenen. Diese Unterscheidung ist der häufigste Stolperstein überhaupt:
| Ebene | Wofür | Wie vergeben |
|---|---|---|
| Azure RBAC | Azure-Ressourcen (VMs, Storage, Key Vault, ARM …) | Portal → IAM → Rollenzuweisung |
| Microsoft Graph App Roles | Entra ID, SharePoint, Exchange, Teams … | PowerShell (Graph SDK) – nicht über Portal möglich |
Der entscheidende Punkt: Die meisten Portal-Nutzer suchen Graph-Berechtigungen im Portal unter „API-Berechtigungen" – das zeigt aber nur Delegated Permissions (für Benutzer-Logins), nicht die Application Permissions, die eine Managed Identity braucht. Graph App Roles lassen sich ausschließlich per PowerShell oder REST vergeben.
Microsoft-Graph-Berechtigungen vergeben
Für Aufgaben gegen Entra ID, SharePoint, Exchange oder Teams braucht die Managed Identity Microsoft-Graph-Anwendungsberechtigungen. Diese werden über App Roles auf den Service Principal der Managed Identity gesetzt.
Voraussetzungen
- Das Graph PowerShell SDK ist installiert (
Install-Module Microsoft.Graph) oder bereits in der Cloud Shell vorhanden. - Dein eigener Account besitzt die Berechtigungen
AppRoleAssignment.ReadWrite.AllundApplication.Read.All– als Global Administrator oder Privileged Role Administrator im Tenant.
Schritt-für-Schritt
Schritt 1 – Mit dem eigenen Admin-Account an Graph anmelden:
Connect-MgGraph -Scopes "AppRoleAssignment.ReadWrite.All", "Application.Read.All"
# Alternativ in Cloud Shell (Device Code Flow):
# Öffne https://microsoft.com/devicelogin und gib den angezeigten Code ein
Schritt 2 – Object ID der Managed Identity ermitteln. Am saubersten geht das über den Namen des Automation Accounts, ohne hartcodierte IDs:
# Methode A: über den Displaynamen (empfohlen)
$miObjectId = (Get-MgServicePrincipal -Filter "displayName eq 'aa-prod-automation'").Id
# Methode B: im Portal ablesen
# Automation Account → Account Settings → Identity → Object (principal) ID
# Methode C: über die Azure-Ressource (bei verbundenem Az-Modul)
$miObjectId = (Get-AzResource `
-ResourceGroupName "rg-automation" `
-ResourceType "Microsoft.Automation/automationAccounts" `
-Name "aa-prod-automation").Identity.PrincipalId
Schritt 3 – Gewünschte Graph-Berechtigung vergeben. Du holst dir den Service Principal von Microsoft Graph (dessen App ID 00000003-0000-0000-c000-000000000000 immer identisch ist), suchst die passende App Role und weist sie zu:
# Graph Service Principal holen
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
# Gewünschte App Role suchen (Beispiel: User.ReadWrite.All)
$appRole = $graphSp.AppRoles | Where-Object {
$_.Value -eq "User.ReadWrite.All" -and
$_.AllowedMemberTypes -contains "Application"
}
# Berechtigung zuweisen
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $miObjectId `
-PrincipalId $miObjectId `
-ResourceId $graphSp.Id `
-AppRoleId $appRole.Id
Schritt 4 – Vergabe prüfen:
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $miObjectId |
Select-Object AppRoleId, PrincipalDisplayName, CreatedDateTime |
Format-Table -AutoSize
Alternativ siehst du die zugewiesenen App Roles im Portal unter Entra ID → Enterprise Applications, wenn du dort den Automation Account suchst und zu Permissions wechselst.
Häufig benötigte Graph App Roles
Diese Berechtigungen brauchst du in der Praxis am häufigsten. Die App-Role-IDs sind tenant-unabhängig identisch und lassen sich direkt verwenden:
| Berechtigung | App Role ID | Zweck |
|---|---|---|
| User.Read.All | df021288-bdef-4463-88db-98f22de89214 | Benutzer lesen |
| User.ReadWrite.All | 741f803b-c850-494e-b5df-cde7c675a1ca | Benutzer anlegen/ändern/löschen |
| Group.Read.All | 5b567255-7703-4780-807c-7be8301ae99b | Gruppen lesen |
| Group.ReadWrite.All | 62a82d76-70ea-41e2-9197-370581804d09 | Gruppen verwalten |
| GroupMember.ReadWrite.All | dbaae8cf-10b5-4b86-a4a1-f871c94c6695 | Mitglieder hinzufügen/entfernen |
| Directory.Read.All | 7ab1d382-f21e-4acd-a863-ba3e13f7da61 | Gesamtes Verzeichnis lesen |
| Mail.Send | b633e1c5-b582-4048-a93e-9f11b44c7e96 | Mails versenden (als App) |
| Sites.Selected | 883ea226-0bf2-4a8f-9f9d-92c9162a727d | Nur bestimmte SharePoint Sites |
| Sites.ReadWrite.All | 9492366f-7969-46a4-8d15-ed1a20078fff | Alle SharePoint Sites lesen/schreiben |
| AuditLog.Read.All | b0afded3-3588-46d8-8b3d-9842eff778da | Audit-Logs / Sign-in Activity lesen |
Für ein typisches User-Provisioning reichen User.ReadWrite.All und GroupMember.ReadWrite.All. Directory.ReadWrite.All ist zu weitreichend – vergib es nur, wenn du es wirklich brauchst. Es gilt konsequent Least Privilege: nur die tatsächlich benötigten Scopes.
Zwei Details, die oft Zeit kosten
Zwei Eigenheiten der Graph-Berechtigungsvergabe führen regelmäßig zu Verwirrung – merke sie dir gleich mit:
- Case Sensitivity: Die Werte der Berechtigungen sind in der Graph API case-sensitiv.
User.ReadWrite.Allist nicht dasselbe wieuser.readwrite.all. - Wirksamkeit mit Verzögerung: Neu gesetzte Graph-Berechtigungen brauchen mindestens etwa zwei Stunden, bis sie wirksam sind, wenn sie initial vergeben werden. Ein direkt danach getesteter Runbook-Lauf kann also noch mit
403 Forbiddenfehlschlagen, obwohl die Zuweisung korrekt ist.
Eine gesetzte Berechtigung lässt sich bei Bedarf auch wieder entfernen:
$assignment = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $miObjectId |
Where-Object { $_.AppRoleId -eq "741f803b-c850-494e-b5df-cde7c675a1ca" }
Remove-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $miObjectId `
-AppRoleAssignmentId $assignment.Id
SharePoint mit Sites.Selected (Least Privilege)
Sites.Selected ist die sicherste Methode für SharePoint-Zugriff: Die Managed Identity bekommt nur Zugriff auf eine bestimmte Site, nicht auf den gesamten Tenant. Der wichtige Punkt dabei – die App Role allein reicht nicht. Zusätzlich muss die konkrete Site explizit berechtigt werden:
# Site ID ermitteln
$siteResp = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/v1.0/sites/tenant.sharepoint.com:/sites/automation"
$siteId = $siteResp.id
# Managed Identity auf der Site berechtigen (read / write / fullcontrol)
$body = @{
roles = @("write")
grantedToIdentities = @(
@{
application = @{
id = $miObjectId
displayName = "aa-prod-automation"
}
}
)
} | ConvertTo-Json -Depth 5
Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/v1.0/sites/$siteId/permissions" `
-Body $body `
-ContentType "application/json"
Ohne den POST auf /sites/{id}/permissions hat die App zwar die Role, aber Zugriff auf keine einzige Site.
Für automatisierte Szenarien wird die Ziel-Site (hier eine Communication site) angelegt und anschließend gezielt für die Managed Identity berechtigt.
Azure RBAC: Rollen für Azure-Ressourcen
Greift das Runbook direkt auf Azure-Ressourcen zu – etwa Key Vault, Storage Account, Log Analytics oder ARM-Operationen wie das Starten einer VM –, braucht die Managed Identity Azure-RBAC-Rollen. Diese vergibst du über das Portal (Access Control / IAM) oder per PowerShell:
$miObjectId = (Get-MgServicePrincipal -Filter "displayName eq 'aa-prod-automation'").Id
# Key Vault Secrets User auf einem einzelnen Key Vault
az role assignment create `
--assignee $miObjectId `
--role "Key Vault Secrets User" `
--scope "/subscriptions/<SubId>/resourceGroups/rg-automation/providers/Microsoft.KeyVault/vaults/kv-automation"
Auch hier gilt Least Privilege: Bekommt ein Runbook nur eine VM gestartet/gestoppt, erhält die Identität auch nur dieses Recht – keine Contributor-Rolle auf das ganze Abonnement. Wildcard-Aktionen vermeiden; bei Bedarf benutzerdefinierte Rollen erstellen.
Über „Rollenzuweisung hinzufügen" wird der Zugriff der Option „Verwaltete Identität" zugewiesen und der Automation Account als Mitglied ausgewählt.
Wichtige RBAC-Rollen für Automation
| Rolle | Scope-Empfehlung | Zweck |
|---|---|---|
| Key Vault Secrets User | Einzelner Key Vault | Secrets lesen (kein Schreiben) |
| Key Vault Secrets Officer | Einzelner Key Vault | Secrets lesen + schreiben |
| Storage Blob Data Reader | Storage Account | Blob-Inhalte lesen |
| Storage Blob Data Contributor | Storage Account | Blob-Inhalte lesen + schreiben |
| Log Analytics Reader | Log Analytics Workspace | Logs abfragen |
| Monitoring Reader | Subscription / RG | Azure Monitor Daten lesen |
| Virtual Machine Contributor | RG oder VM | VMs starten/stoppen/neu starten |
| Reader | Subscription | Alle Ressourcen lesen |
| Contributor | RG (nie Subscription!) | Ressourcen erstellen/ändern |
Zusätzlich existieren spezielle Automation-Rollen für die Verwaltung des Accounts selbst: Automation Contributor (erstellt und verwaltet alle Automation-Ressourcen – sehr mächtig, Inhaber strikt begrenzen), Automation Operator (Jobs erstellen/verwalten, Runbook-Namen lesen) sowie feiner granulierte Automation Job Operator und Runbook Operator für das reine Ausführen.
Token im Runbook holen
Der eigentliche Charme der Managed Identity zeigt sich im Runbook selbst: Du brauchst kein Secret. Der empfohlene Weg führt über das Az-Modul und Connect-AzAccount -Identity:
# Innerhalb eines Runbooks – kein Secret nötig!
Connect-AzAccount -Identity
$token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token
Alternativ lässt sich das Token direkt am IMDS-Endpoint (Instance Metadata Service) abholen. Diese Variante funktioniert nur in Azure-Sandbox-Jobs, nicht auf einem Hybrid Worker ohne Arc-Agent:
$tokenResponse = Invoke-RestMethod `
-Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com/" `
-Headers @{ Metadata = "true" } `
-Method GET
$accessToken = $tokenResponse.access_token
In der Praxis kombinierst du beide Schritte: Über Connect-AzAccount -Identity holst du das Graph-Token und übergibst es an Connect-MgGraph. Damit sprichst du anschließend das Microsoft Graph SDK an, ohne jemals ein Passwort hinterlegt zu haben:
Connect-AzAccount -Identity -ErrorAction Stop | Out-Null
$graphToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token
Connect-MgGraph -AccessToken ($graphToken | ConvertTo-SecureString -AsPlainText -Force) -NoWelcome
Runbook-Typen und Laufzeitversionen
Azure Automation unterstützt mehrere Runbook-Typen. Die Wahl ist endgültig – ein grafisches Runbook lässt sich nicht in ein textuelles umwandeln und umgekehrt.
| Typ | Sprache | Empfehlung |
|---|---|---|
| PowerShell | PS 5.1 / 7.2 / 7.4 | Hauptwahl für Azure-/M365-Automation |
| Python | Python 2.x / 3.x | Datenverarbeitung, REST-lastige Szenarien |
| Graphical | GUI-basiert | Nicht empfohlen – schwer zu versionieren |
| PowerShell Workflow | PS 5.1 | Veraltet – nicht mehr verwenden |
Für die meisten Microsoft-nahen Automatisierungen (Entra ID, Intune, Defender) sind textuelle PowerShell-Runbooks die richtige Wahl: Sie lassen sich versionieren, per Git verwalten und nutzen das Microsoft-Graph-PowerShell-SDK für Identitätsaufgaben.
Empfehlung zur PowerShell-Version: PowerShell 7.2 (LTS) oder 7.4. PS 5.1 nur, wenn Legacy-Module es erzwingen.
Runtime Environment und Runbook anlegen
Für eine definierte Laufzeitumgebung erstellst du ein Runtime Environment und legst darauf das Runbook an. Erst nach dem Publishen ist ein Runbook ausführbar:
# Runtime Environment für PowerShell 7.2 erstellen
az automation runtime-environment create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "ps72-runtime" `
--language "PowerShell" `
--version "7.2"
# Runbook anlegen
az automation runbook create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "Invoke-UserProvisioning" `
--type "PowerShell" `
--runtime-version "7.2"
# Inhalt hochladen
az automation runbook replace-content `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "Invoke-UserProvisioning" `
--content @"./runbooks/Invoke-UserProvisioning.ps1"
# Publishen (erst dann ausführbar)
az automation runbook publish `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "Invoke-UserProvisioning"
Jobs und die Ausführungsumgebung
Startest du ein Runbook, entsteht ein Job – eine einzelne Ausführungsinstanz. Ein Runbook kann mehrere Jobs gleichzeitig haben. Jobs laufen in einer von zwei Umgebungen:
- Azure-Sandbox – eine isolierte, von Microsoft verwaltete Ausführungsumgebung in der Cloud. Ideal für Aufgaben gegen Azure-/Cloud-Ressourcen, weil keine eigene Infrastruktur nötig ist und die Authentifizierung per Managed Identity einfach funktioniert. Sie unterliegt jedoch Ressourcengrenzen.
- Hybrid Runbook Worker – ein selbst betriebener Rechner (Windows oder Linux, on-premises oder in einer anderen Cloud), auf dem Runbooks direkt laufen. Nötig für Zugriffe auf lokale Ressourcen (z. B. Active Directory Domain Services), für lange Laufzeiten oder für Prozesse, die in der Sandbox nicht erlaubt sind.
„Fair Share" – das 3-Stunden-Limit: In der Azure-Sandbox beendet Azure jeden Job, der länger als drei Stunden läuft. PowerShell- und Python-Jobs werden gestoppt und nicht neu gestartet. Für lange Läufe ist ein Hybrid Runbook Worker zu verwenden – dieser unterliegt dem Limit nicht. Weitere Sandbox-Grenzen: max. 1 GB Festplatte, 400 MB Arbeitsspeicher, Ausgabestrom max. 1 MB.
Aufbau eines produktionsreifen Runbooks
Ein Runbook, das in Produktion läuft, braucht mehr als die reine Fachlogik: strukturiertes Logging, saubere Authentifizierung mit Fehlerbehandlung und ein definiertes Ende. Das folgende Grundgerüst zeigt das Muster – eine #Requires-Zeile für die Modulabhängigkeit, eine JSON-Logging-Funktion und einen try/catch/finally-Rahmen um Authentifizierung und Hauptlogik:
#Requires -Modules @{ ModuleName = "Microsoft.Graph.Users"; ModuleVersion = "2.0.0" }
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$ListName = "UserProvisioningRequests"
)
# Logging-Funktion (strukturiert, maschinell auswertbar)
function Write-Log {
param([string]$Level, [string]$Message, [hashtable]$Data = @{})
$entry = [ordered]@{
Timestamp = (Get-Date -Format "o")
Level = $Level
Message = $Message
Data = $Data
}
$json = $entry | ConvertTo-Json -Compress
switch ($Level) {
"ERROR" { Write-Error $json }
"WARNING" { Write-Warning $json }
default { Write-Output $json }
}
}
# Authentifizierung per Managed Identity
try {
Connect-AzAccount -Identity -ErrorAction Stop | Out-Null
$graphToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token
Connect-MgGraph -AccessToken ($graphToken | ConvertTo-SecureString -AsPlainText -Force) -NoWelcome
Write-Log -Level "INFO" -Message "Authentifizierung erfolgreich"
} catch {
Write-Log -Level "ERROR" -Message "Authentifizierung fehlgeschlagen" -Data @{ Error = $_.Exception.Message }
throw
}
# Hauptlogik
try {
# ... eigentliche Aufgabe hier ...
Write-Log -Level "INFO" -Message "Runbook abgeschlossen" -Data @{ ProcessedItems = 0 }
} catch {
Write-Log -Level "ERROR" -Message "Unerwarteter Fehler" -Data @{ Error = $_.Exception.Message; StackTrace = $_.ScriptStackTrace }
throw
} finally {
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
}
Job-Output und Streams
Runbooks kennen dieselben Ausgabe-Streams wie PowerShell. Für die spätere Auswertung in Log Analytics ist der Output-Stream über Write-Output der wichtigste – am besten mit JSON-strukturierten Ausgaben, damit Downstream-Prozesse die Daten direkt parsen können:
| Stream | PowerShell | Zweck |
|---|---|---|
| Output | Write-Output | Hauptergebnis des Jobs |
| Error | Write-Error | Fehler |
| Warning | Write-Warning | Warnungen |
| Verbose | Write-Verbose | Detaillierte Infos (nur wenn aktiviert) |
| Debug | Write-Debug | Debugging |
| Progress | Write-Progress | Fortschritt (nur interaktiv) |
Module und Pakete verwalten
Ein Runbook kann nur die Cmdlets nutzen, deren Module im Automation Account importiert sind. Module lassen sich direkt aus der PowerShell Gallery importieren:
# Modul importieren (aus PSGallery)
az automation module create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "Microsoft.Graph.Users" `
--content-link "https://www.powershellgallery.com/api/v2/package/Microsoft.Graph.Users/2.0.0"
# Status prüfen
az automation module show `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "Microsoft.Graph.Users" `
--query "contentLink.version"
Stolperfalle bei Graph-Modulen: Die Microsoft.Graph-Module haben Abhängigkeiten (z. B.
Microsoft.Graph.Authentication). Importiere immer zuerst die Basis-Module. Versionskonflikte entstehen, wenn verschiedene Graph-Submodule unterschiedliche Versionen erwarten – pinne daher alle Graph-Module auf dieselbe Version.
Shared Resources
Shared Resources stehen allen Runbooks eines Accounts zur Verfügung. Sie vermeiden Code-Duplizierung und lassen sich zentral ändern, ohne jedes Runbook anzufassen.
| Resource-Typ | Zweck | Beispiel |
|---|---|---|
| Variable | Konfigurationswerte, wiederverwendbar | TenantId, SharePoint-URL |
| Credential | Benutzername + Passwort (verschlüsselt) | Service-Account-Credentials |
| Certificate | X.509-Zertifikate | App-Registration-Cert |
| Connection | Verbindungsparameter-Set | Legacy (weniger relevant mit MI) |
Variablen
Umgebungsparameter gehören als Variablen in den Account – kein Hardcoding in Runbooks. So kannst du Werte tauschen, ohne Code anzufassen. Sensible Werte immer verschlüsselt speichern:
# Variable anlegen (verschlüsselt)
az automation variable create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "SharePointSiteUrl" `
--value '"https://tenant.sharepoint.com/sites/automation"' `
--encrypted true
# Im Runbook lesen
$siteUrl = Get-AutomationVariable -Name "SharePointSiteUrl"
Credentials und Zertifikate
Credentials sind vor allem für Legacy-Szenarien gedacht (z. B. On-Prem-AD-Zugriff über einen Hybrid Worker). Für Cloud-Ressourcen bevorzugst du immer die Managed Identity:
# Im Runbook holen
$credential = Get-AutomationPSCredential -Name "SvcAutomationAccount"
Zertifikate lassen sich ebenfalls als Secure Asset ablegen und etwa für eine App-Registration ohne Client Secret nutzen:
$cert = Get-AutomationCertificate -Name "GraphAppCert"
Connect-MgGraph -ClientId "<AppId>" -TenantId "<TenantId>" -Certificate $cert
Anmeldeinformationen, Zertifikate, Verbindungen und verschlüsselte Variablen sind Secure Assets. Sie werden mit dem accountspezifischen Schlüssel im systemverwalteten Key Vault verschlüsselt. Microsoft Defender for Cloud empfiehlt, alle Automation-Variablen zu verschlüsseln.
Runbooks auslösen: Schedules und Webhooks
Runbooks lassen sich manuell im Portal, per PowerShell-Cmdlet, nach Zeitplan oder per Webhook starten.
Schedules (Zeitpläne)
Zeitpläne lösen Runbooks zu definierten Zeiten oder in Intervallen aus und sind zeitzonenfähig. Du erstellst einen Schedule und verknüpfst ihn anschließend mit einem Runbook:
# Täglicher Schedule um 06:00
az automation schedule create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "daily-0600" `
--frequency "Day" `
--interval 1 `
--start-time "2025-01-01T06:00:00+00:00" `
--time-zone "W. Europe Standard Time"
# Schedule mit Runbook verknüpfen
az automation job-schedule create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--runbook-name "Invoke-HygieneReport" `
--schedule-name "daily-0600"
Webhooks
Ein Webhook ist eine eindeutige HTTPS-URL, über die ein externer Dienst per einzelner HTTP-POST-Anfrage ein bestimmtes Runbook startet – etwa aus Power Automate, Logic Apps oder einem eigenen Frontend. Im Runbook nimmst du die Nutzdaten über den Parameter $WebhookData entgegen:
# Webhook erstellen
az automation webhook create `
--automation-account-name "aa-prod-automation" `
--resource-group "rg-automation" `
--name "wh-user-provisioning" `
--runbook-name "Invoke-UserProvisioning" `
--expiry-time "2026-12-31T00:00:00+00:00" `
--is-enabled true
# Im Runbook: Webhook-Daten lesen
param([object]$WebhookData)
if ($WebhookData) {
$payload = $WebhookData.RequestBody | ConvertFrom-Json
Write-Log -Level "INFO" -Message "Webhook empfangen" -Data @{ Payload = $payload }
}
Neben Schedules und Webhooks lässt sich ein Runbook auch aus einer Logic App auslösen: Die Aktion „Create job" wählt Automation Account und Runbook aus und übergibt die Eingabeparameter.
Sicherheit von Webhooks: Die Webhook-URL enthält einen Sicherheitstoken und ist die einzige Authentifizierung – behandle sie wie ein Passwort. Azure prüft nur, ob die URL korrekt ist; eine weitere Authentifizierung findet nicht statt. Die URL wird nur einmal beim Erstellen angezeigt und ist danach nicht mehr abrufbar. Speichere sie sofort im Key Vault. Beachte außerdem: Alle Eingabeparameter werden mit dem Job protokolliert – übergib keine Geheimnisse im Klartext.
Best Practices für den Produktivbetrieb
Aus dem Zusammenspiel der bisherigen Bausteine ergeben sich einige Grundregeln, die sich in der Praxis bewährt haben:
- Least Privilege konsequent umsetzen – die Managed Identity erhält nur exakt die Berechtigungen, die das Runbook braucht. Keine Contributor-Rolle auf ganze Subscriptions, keine Wildcard-Aktionen, für privilegierte Konten Privileged Identity Management (PIM) nutzen.
- Geheimnisse in den Key Vault – API-Schlüssel und Passwörter gehören in Azure Key Vault und werden per Managed Identity abgerufen, nicht ins Runbook geschrieben. Wo doch nötig, Secure Assets statt Klartext.
- Idempotenz – mehrfaches Ausführen führt zum gleichen Ergebnis. Vor dem Anlegen prüfen, ob ein Objekt bereits existiert:
function New-UserIdempotent {
param([hashtable]$UserProperties)
$existingUser = Get-MgUser -Filter "userPrincipalName eq '$($UserProperties.UserPrincipalName)'" -ErrorAction SilentlyContinue
if ($existingUser) {
Write-Log -Level "INFO" -Message "Benutzer existiert bereits – überspringe" -Data @{ UPN = $UserProperties.UserPrincipalName }
return $existingUser
}
$newUser = New-MgUser @UserProperties
Write-Log -Level "INFO" -Message "Benutzer erstellt" -Data @{ Id = $newUser.Id; UPN = $newUser.UserPrincipalName }
return $newUser
}
- Retry mit Backoff – transiente Fehler wie Graph-Throttling (HTTP 429) oder kurzzeitige Ausfälle (502/503/Timeout) mit einer Wiederholungslogik und exponentiellem Backoff abfedern, statt sofort abzubrechen.
- Strukturiertes Logging – JSON-Ausgaben über
Write-Output, damit Log Analytics und nachgelagerte Prozesse die Daten direkt auswerten können. - Source Control statt Portal-Editor – Runbooks über Azure DevOps oder GitHub anbinden (
az automation source-control create) und per CI/CD ausrollen. Editiere Runbooks niemals direkt im Portal, wenn Source Control aktiv ist – der nächste Sync überschreibt manuelle Änderungen.
Troubleshooting: 401/403 im Runbook
Schlägt ein Runbook mit 401 Unauthorized oder 403 Forbidden fehl, arbeite diese Checkliste ab:
- Ist die Managed Identity aktiviert? → Portal: Automation Account → Identity → Status = On.
- Hat die MI die richtige Graph App Role? →
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $miObjectId. - Ist die App Role für „Application" freigegeben (nicht nur „User")? → Prüfen, ob
AllowedMemberTypesden WertApplicationenthält. - Bei Sites.Selected: Wurde die Site-Permission per POST auf
/sites/{id}/permissionsgesetzt? - Bei Azure RBAC: Hat die MI die Rolle auf dem richtigen Scope? →
Get-AzRoleAssignment -ObjectId $miObjectId. - Token-Problem: Ist der IMDS-Endpoint erreichbar? → Nur in Azure-Sandbox-Jobs verfügbar, nicht auf einem Hybrid Worker ohne Arc-Agent.
Und denke an die Zeitverzögerung: Frisch gesetzte Graph-Berechtigungen werden erst nach etwa zwei Stunden wirksam. Ein 403 direkt nach der Vergabe heißt also nicht zwingend, dass etwas falsch konfiguriert ist.
Fazit
Azure Automation ist ein leichtgewichtiges, kostengünstiges Werkzeug für geradlinige Verwaltungsaufgaben – vom nächtlichen Hygiene-Report bis zum Provisioning neuer Benutzer. Der Weg zu einem sicheren, wartbaren Setup ist immer derselbe: einen sauber konfigurierten Automation Account anlegen (Basic-SKU, Private Endpoint, Diagnostics), die System-assigned Managed Identity aktivieren, die Berechtigungen auf den zwei getrennten Ebenen – Azure RBAC und Microsoft Graph App Roles – nach Least Privilege vergeben und im Runbook per Connect-AzAccount -Identity ganz ohne Secret authentifizieren. Wer dann noch auf textuelle PowerShell-7.x-Runbooks, Idempotenz, strukturiertes Logging und Source Control setzt, hat eine Automatisierung, die im Betrieb verlässlich bleibt und mit weiteren Prozessen mitwächst.
Merke dir vor allem die zwei Berechtigungsebenen und die zwei Stunden Wartezeit bei neuen Graph-Berechtigungen – an diesen beiden Punkten scheitern die meisten ersten Runbook-Läufe, obwohl technisch alles korrekt eingerichtet ist.
Quellen
Dieser Artikel dokumentiert den Stand zum Veröffentlichungsdatum. Microsoft-Produkte ändern sich schnell – prüfe vor der Umsetzung die verlinkten Original-Quellen. Umsetzung auf eigene Verantwortung; im Zweifel in einer Testumgebung validieren.