Hinweis: Dieser Artikel wurde ursprünglich für evoila unter API-driven Provisioning with Microsoft Entra and Azure Logic Apps veröffentlicht.
Im August letzten Jahres hat Microsoft ein neues Feature “API-gesteuerte Bereitstellung” für ihre “Entra ID” Identity Management Lösung angekündigt1. Diese API ermöglicht das automatisierte Provisionieren von Benutzer:innen-Accounts, in Entra und Active directory, aus allen vertrauenswürdigen Datenquellen wie zum Beispiel einer HCM Software. API-gesteuerte Bereitstellung basiert auf dem SCIM-Standard2 und ist für die Verwendung in Kombination mit Entra Lebenszyklus-Workflows3 gedacht.
Der Bereitstellungsdatenfluss, wie in Abbilung 1 skizziert, beinhaltet:
- Ein “Identitätsverzeichnis” welches Daten über interne und externe Mitarbeiter:innen beinhaltet. Üblicherweise ein HR/HCM-System.
- Ein Automatisierungstool wie zum Beispiel PowerShell, Azure Runbooks oder Logic Apps, um Daten aus dem “Identitätsverzeichnis” zu laden, ein SCIM-Dokument zu erstellen und dieses an den API-Endpunkt zu senden.
- Der Entra “Bereitstellungsservice” verarbeitet das SCIM-Dokument und erstellt, aktualisiert oder entfernt Objekte in Microsoft Entra oder Active Directory.
- Wenn ein ‘on-premise’ Active Directory im Einsatz ist, ein lokal installierter “Bereitstellungsagent”.
Abbildung 1: Microsoft Entra API-gesteuerte Bereitstellung Datenfluss
In diesem Artikel beschreiben wir eine einfache Implementierung der API-gesteuerten Bereitstellung mit Logic Apps. Es ist als Beispiel bzw. Ausgangspunkt für eine eigene Implementierung gedacht und basiert lose auf Microsofts “QuickStart with Azure Logic App”.4
Für Workday HCM und SuccessFactors bietet Microsoft eine eigene Integration an, die du nicht selbst implementieren musst:
Voraussetzungen
Lizenzierung
API-gesteuerte Bereitstellung erfordert Microsoft Entra ID P1 (früher “Azure Active Directory P1”) Lizenzen.
Azure Ressourcen
Für unser Beispiel benötigen wir einige Azure Ressourcen:
- Eine Ressourcengruppe “rg-HcmProvisioning” als logischen Container für unsere Ressourcen.
- Einen Azure Storage Account “sthcmprovisioning<NNNN>” mit einer storage table5 “employeedemo” für unser Beispieldatenset.
- Eine Logic App “logic-HcmProvisioning” für unseren Workflow.
Um die Ressourcen anzulegen kannst du die Azure CLI, PowerShell oder das Portal verwenden. Das Erstellen der Logic App mit PowerShell oder der CLI benötigt eine “Workflowdefinition”6, die du im Anhang weiter unten findest.
Wir nutzen ein PowerShell Script, um die Ressourcen anzulegen:
$ProjectName = "HcmProvisioning"
$Location = "westeurope" # Azure region for our resources
$RandomId = Get-Random -Minimum -1000 -Maximum 9999 # We'll use this to make our storage account name globally unique
$StorageName = "st" + $ProjectName.ToLower() + $RandomId
$StorageTableName = "employeedemo"
$ResourceGroupName = "rg-$ProjectName"
$LogicAppName = "logic-$ProjectName"
# Create a resource group for our Azure resources
New-AzResourceGroup -Location $Location -Name $ResourceGroupName
# Create the storage account...
$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageName -Location $Location -SkuName "Standard_LRS"
# ... and our employee table
$StorageContext = $StorageAccount.Context
New-AzStorageTable -Name $StorageTableName -Context $StorageContext
# Create a Logic App, based on our "empty" workflow definition
$LogicApp = New-AzLogicApp -ResourceGroupName $ResourceGroupName -Name $LogicAppName -Location $Location -DefinitionFilePath "./workflow-definition.json"
HCM Datenquelle
Für das Beispiel verwenden wir die Azure Speichertabelle “employeedemo”, die wir eben im Azure Storage Account erstellt haben. Wir laden Microsofts Beispieldatenset von GitHub herunter und importieren es mit Azure Storage Explorer in die Tabelle.
Der Tabelleninhalt sieht nach dem Import so aus (reduziert auf die relevanten Spalten):
Company | CostCenter | Department | Devision | FirstName | FullName | LastName |
---|---|---|---|---|---|---|
Woodgrove | CC3035 | Sales | Electronics | Talya | Talya Fleeta | Fleeta |
Woodgrove | CC3035 | Sales | Electronics | Miriam | Miriam Dunware | Dunware |
Woodgrove | CC3035 | Product Engineering | Logistics | Ginnie | Ginnie Fadiman | Fadiman |
Fabrikam | CC5081 | Sales | Sales | Genevra | Genevra Melony | Melony |
Woodgrove | CC3035 | Manufacturing | Pharma | Nyssa | Nyssa Roscoe | Roscoe |
Microsoft Entra API-driven provisioning App
Um die “API-gesteuerte Bereitstellung” in einem Entra Tenant zu aktivieren muss eine “Enterprise Application” aus der Gallerie hinzugefügt werden. Melde dich im Microsoft Entra Admin Center an und Navigiere zu Identity > Applications > Enterprise applications.
Abbildung 2: Enterprise application hinzufügen
Wähle “New application”, gib “API-driven” ins Suchfeld ein und wähle anschließend die Applikation “API-driven provisioning to Microsoft Entra ID” aus. Behalte die Standardeinstellungen bei und klicke auf “Create”.
Abbildung 3: API-driven provisioning App suchen
Öffne die neu erstellte App und wechsle ins Menü “Provisioning” (Abb. 4). Klicke auf “Get started” in der “Provision User Accounts”-Kachel, setze den “Provisioning Mode” auf “Automatic” und speichere die Einstellungen.
Abbildung 4: Bereitstellung aktivieren
Nachdem die initiale Konfiguation erstellt wurde kannst links im Menü unter “Provisioning” (Abb. 5) zusätzliche Optionen sehen:
- Mappings steuern die Zuordnung von Datenfeldern zwischen der Datenquelle und Microsoft Entra. Für unser Demo genügen die Standardeinstellungen.
- Unter Settings sollte eine gültige E-Mail-Adresse für Benachrichtigungen eingetragen werden. Zusätzlich kann hier ein Grenzwert gesetzt werden, um unbeabsichtigtes Löschen vieler Benutzerkonten zu verhindern.
Abbildung 5: Bereitstellungseinstellungen
Logic App Managed Identity
Die Azure Logic App wird ein “Service Principal” nutzen, um sich bei Microsoft Entra zu authentifizieren. Dazu aktivieren wir die “System-assigned Managed Identity”7 in unserer Logic App. Anschließend nutzen wir PowerShell, um der “Managed Identity” die nötigen MS Graph Berechtigungen zu erteilen.
Öffne die Logic App Ressource im Azure Portal und navigiere zu “Identity” (Abb. 6). Ändere den Status auf “On” und klicke auf “Save”, um eine Managed Identity zu erstellen. Die Managed Identity erhält denselben Namen wie die Logic App (“logic-HcmProvisioning”) in Entra.
Abbildung 6: Managed Identity aktivieren
Im nächsten Schritt verwenden wir ein PowerShell Script und die MS Graph API, um dem Service Principal die nötigen Berechtigungen8 zu geben:
- SynchronizationData-User.Upload erlaubt der Logic App, Daten an den Identity Synchronization Service hochzuladen.
- AuditLog.Read.All erlaubt der Logic App, Logeinträge im AuditLog zu lesen.
Install-Module -Name "Microsoft.Graph" -Scope AllUsers
Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All,RoleManagement.ReadWrite.Directory"
$GraphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
# Find our Logic App's managed service principal
$ManagedSp = Get-MgServicePrincipal -Filter "DisplayName eq '$LogicAppName'" # the service principal has the same name as the logic app we created earlier
# Search for app role permission "SynchronizationData-User.Upload" and assign it to our service principal
$PermissionName = "SynchronizationData-User.Upload"
$AppRole = $GraphApp.AppRoles | Where-Object {$_.Value -eq $PermissionName -and $_.AllowedMemberTypes -contains "Application"}
New-MgServicePrincipalAppRoleAssignment -PrincipalId $ManagedSp.Id -ServicePrincipalId $ManagedSp.Id -ResourceId $GraphApp.Id -AppRoleId $AppRole.Id
# Search for app role permission "AuditLog.Read.All" and assign it to our service principal
$PermissionName = "AuditLog.Read.All"
$AppRole = $GraphApp.AppRoles | Where-Object {$_.Value -eq $PermissionName -and $_.AllowedMemberTypes -contains "Application"}
New-MgServicePrincipalAppRoleAssignment -PrincipalId $ManagedSp.Id -ServicePrincipalId $ManagedSp.Id -ResourceId $GraphApp.Id -AppRoleId $AppRole.Id
Zusätzlich erlauben wir der Logic App, unsere Datenquelle (Azure Storage Table) zu lesen:
New-AzRoleAssignment -PrincipalId $ManagedSp.Id -RoleDefinitionName "Storage Table Data Reader" -ResourceName $StorageName -ResourceType "Microsoft.Storage/storageAccounts" -ResourceGroupName $ResourceGroupName
Du kannst die MS Graph Berechtigungen des Service Principals in Entra prüfen, indem du die App “logic-HcmProvisioning” unter “Enterprise Application” auswählst (ggf. den Filter “Application type == Enterprise application” entfernen):
Abbildung 7: Service Principal Berechtigungen
Die “Storage Table Data Reader” Berechtigung siehst du im Azure Portal in der “Storage Account” Ressource unter “Access Control (IAM) > Role Assignments”:
Abbildung 8: Service Principal Rollen
Logic App Workflow konfigurieren
Logic App Designer öffnen
Nachdem wir alle Voraussetzungen erfüllt bzw. konfiguriert haben können wir endlich mit der Entwicklung unseres Logic App Workflows beginnen. Navigiere im Azure Portal zur Logic App und öffne den “Logic App Designer”. Derzeit besteht die Logic App nur aus zwei Schritten:
- Ein Trigger (Auslöser) “Recurrence 0500am every day”, welcher den Workflow jeden Tag um 05:00 UTC+1 startet
- und eine Action “Compose ProvisioningAPI Settings”, die ein später benötigtes JSON-Objekt erzeugt.
API-Eigenschaften definieren
Bearbeite die “Compose ProvisioningAPI Settings” Action und ersetze den “Uri”-Eintrag mit deinem eigenen “Provisioning API Endpoint”. Du findest diese Information in der “API-drive provisioning to Microsoft Entra ID” Enterprise application, unter Provisioning > Overview > View technical information.
Die Action sollte nun ungefähr so aussehen:
{
"Audience": "https://graph.microsoft.com",
"Uri": "https://graph.microsoft.com/beta/servicePrincipals/abdefghi-1234-5678-jklm-nopqrstuvwxy/synchronization/jobs/API2AAD.b649b126ed68488484d86210336bb33f.21803b3e-5c34-4571-b37a-bc1ce9ec3556/bulkUpload"
}
Datenquelle auslesen
Füge eine weitere Action “Get entities (V2)” vom Typ “Azure Table Storage” hinzu. Gib einen Namen für das Connection-Objekt an (z.B. “conn-sthcmprovisioning”), wähle “Logic Apps Managed Identity” als Authentication Type und klicke anschließend auf “Create.”
Unter Parameters
- für Storage Account Name wähle “enter custom value” und trage den Namen deines Azure Storage Account ein (z.B. “sthcmprovisioning1234”)
- für Table wähle “employeedemo” im Drow-Down aus.
Um den Workflow besser lesbar zu machen klicke auf den Titel der Action und ändere den Namen zu “Get employee data from storage table”.
SCIM-Objekte erstellen
Wir müssen ein SCIM/JSON-Objekt für jeden Eintrag in der Datenquelle erstellen. Um das zu tun,
- erstelle eine “For each” Action vom Typ “Control”,
- wähle “Get entities result / List of Entities” als Parameter,
- ändere den Namen der “For each” Action auf “For each employee”.
Dann erstelle eine weiter Action innerhalb des “For each employee” Blocks:
- Diesmal benötigen wir eine “Compose” Action vom Typ “Data Operations”.
- Ändere den Namen auf “Compose employee SCIM”.
- Kopiere den JSON-Code unten in das Feld “Inputs”. Der Code enthält ein SCIM-Schema zur Zuordnung der Felder unserer Datenquelle zu Microsoft Entra (z.B.
FullName
=>displayName
). Außerdem wird deractive
Status basierend auf dem FeldWorkerStatus
in unserer Datenquelle gesetzt.
{
"bulkId": "@{guid()}",
"data": {
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"userName": "@{items('For_each_employee')?['UserID']}",
"displayName": "@{items('For_each_employee')?['FullName']}",
"externalId": "@{items('For_each_employee')?['WorkerID']}",
"name": {
"familyName": "@{items('For_each_employee')?['LastName']}",
"givenName": "@{items('For_each_employee')?['FirstName']}"
},
"active": @{if(equals(items('For_each_employee')?['WorkerStatus'],'Active'),true,false)}
},
"method": "POST",
"path": "/Users"
}
Die Action sollte nun so aussehen:
Abbildung 9: SCIM-Objekt erstellen
SCIM-Objekte kombinieren
Nun müssen wir die einzelnen SCIM-Objekte zu einem gesamten bulk SCIM-Dokument welches die Daten aller Mitarbeiter:innen beinhaltet zusammenfassen
Dazu erstelle eine neue “Join” Action vom Typ “Data Operations”,
- klicke auf den “From” Parameter und wähle insert expression
- füge
outputs('Compose_employee_SCIM')
in das Textfeld ein und klicke auf “Update”. - Verwende ein Komma (”,”) für den “Join With” Parameter.
- Ändere den Namen der Action auf “Join employee SCIM”.
Anschließend erstellst du eine weitere “Compose” Action. Hier verwenden wir eine Kombination aus JSON syntax und Logic App Expressions, um unser SCIM-Dokument zu erstellen.
- Benenne die Action “Compose SCIM bulk payload”
- und Kopiere den folgenden JSON-code in das Feld Inputs:
{
"schemas": [
"urn:ietf:params:scim:api:messages:2.0:BulkRequest"
],
"Operations": @{json(concat('[',body('Join_employee_SCIM'),']'))},
"failOnErrors": null
}
SCIM-Dokument an API übermitteln
Im letzten Schritt unseres Workflows senden wir unser SCIM-Dokument an den API-Endpunkt.
Füge eine neue “HTTP” Action hinzu und
- verwende die Expression
outputs('Compose_ProvisioningAPI_Settings')?['Uri']
als URI, - wähle POST als Methode,
- füge im Abschnitt “Headers” einen neuen Eintrag
Content-Type
mit dem Wertapplication/scim+json
ein, - verwende die “Compose SCIM bulk payload” Action als Body,
- wähle “Managed Identity” als Authentication Type
- und füge die Expression
outputs('Compose_ProvisioningAPI_Settings')?['Audience']
im Feld Audience ein.
Ändere den Namen der Action auf “POST SCIM payload to API”.
Du findest die vollständige Workflowdefinition im Anhang dieses Artikels.
Bereitstellungs-Logdateien
Wir sind jetzt bereit, den Logic App Workflow auszuführen. Klicke auf “Run” und warte, bis der Workflow das SCIM-Dokument an den API-Endpunkt gesendet hat. Als Ergebnis der “POST SCIM payload to API” Action solltest du “HTTP Status 202 (Accepted)” sehen. Das bedeutet, die API hat den Request erhalten und startet in Kürze mit der Verarbeitung.
Navigiere in der Zwischenzeit zur API-driven inbound provisioning Applikation in Microsoft Entra. Klicke auf “Provisioning” und öffne anschließend die “Provisioning Logs”, um Details zu den Bereitstellungsvorgängen anzuzeigen, wie in Abbildung 10 dargestellt:
Abbildung 10: SCIM-Objekt erstellen
Nächste Schritte
Jetzt, da du unsere beispielhafte Implementierung gesehen hast fragst du dich vielleicht, wie du deine eigenen HR-Daten verwenden und dein eigenes “Identitätsverzeichnis” anbinden kannst…
Durch die Flexibilität von Azure Logic Apps und die umfangreiche Zahl an verfügbaren Konnektoren bist du nicht darauf beschränkt, Azure Tables als Datenquelle zu verwenden. Hier ein paar alternative Szenarien für Datenquellen, die du in deiner eigenen Umgebung anbinden könntest:
- CSV-Dateien auf einem SFTP-Server unter verwendung des SFTP-Konnektors
- Tabellen in einer relationalen Datenbank (Azure SQL, on-premise SQL, Oracle oder PostgreSQL)
- SAP, entweder on-premise oder in der Cloud mithilfe des SAP-Konnektors.
- Message Queuing Systeme wie zum Beispiel Azure Service Bus oder IBM MQ.
- Jegliche Applikation, die RESTful APIs exponiert, indem du deinen eigenen “custom connector” erstellst.
Das hier gezeigte Beispiel kann als Grundlage für weitere Entra Lebenszyklus-Workflows9 (“joiner-mover-leaver” Prozesse) dienen.
Quellen
- Microsoft Tech Community: Introducing a New Flexible Way of Bringing Identities from Any Source into Microsoft Entra ID!
- System for Cross-domain Identity Management: SCIM 2, the open API for managing identities is now complete and published under the IETF.
- Microsoft Entra ID Governance: What are lifecycle workflows?
- API-driven inbound provisioning tutorials: Quickstart with Azure Logic App
- Azure Storage: What is Azure Table storage?
- Logic Apps: Workflow definition language
- Managed identities for Azure resources: What are managed identities for Azure resources?
- Permissions for API-driven provisioning: Grant access to the inbound provisioning API
- Microsoft Entra ID Governance: Understanding lifecycle workflows
Anhang
Workflowdefinition (leer)
Um eine Azure Logic App mit PowerShell oder der Azure CLI zu erstellen muss eine “Workflowdefinition” angegeben werden. Du kannst die folgende “leere” Workflowdefinition verwenden, um einen einfachen Workflow mit einem Trigger und einer Action zu erstellen:
{
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose_ProvisioningAPI_Settings": {
"inputs": {
"Audience": "https://graph.microsoft.com",
"Uri": "<Your API-driven inbound provisioning URI>"
},
"runAfter": {},
"type": "Compose"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"Recurrence_0500am_every_day": {
"evaluatedRecurrence": {
"frequency": "Day",
"interval": 1,
"schedule": {
"hours": ["5"]
},
"timeZone": "W. Europe Standard Time"
},
"recurrence": {
"frequency": "Day",
"interval": 1,
"schedule": {
"hours": ["5"]
},
"timeZone": "W. Europe Standard Time"
},
"type": "Recurrence"
}
}
}
Workflowdefinition (komplett)
Du kannst die folgende (anonymisierte) Workflowdefinition als Referenz für deinen eigenen Workflow verwenden:
{
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Compose_ProvisioningAPI_Settings": {
"inputs": {
"Audience": "https://graph.microsoft.com",
"Uri": "https://graph.microsoft.com/beta/servicePrincipals/abdefghi-1234-5678-jklm-nopqrstuvwxy/synchronization/jobs/API2AAD.b649b126ed68488484d86210336bb33f.21803b3e-5c34-4571-b37a-bc1ce9ec3556/bulkUpload"
},
"runAfter": {},
"type": "Compose"
},
"Compose_SCIM_bulk_payload": {
"inputs": {
"Operations": "@json(\r\n concat('[',body('Join_employee_SCIM'),']')\r\n)",
"failOnErrors": null,
"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"]
},
"runAfter": {
"Join_employee_SCIM": ["Succeeded"]
},
"type": "Compose"
},
"For_each_employee": {
"actions": {
"Compose_employee_SCIM": {
"inputs": {
"bulkId": "@{guid()}",
"data": {
"active": "@if(equals(items('For_each_employee')?['WorkerStatus'],'Active'),true,false)",
"displayName": "@{items('For_each_employee')?['FullName']}",
"externalId": "@{items('For_each_employee')?['WorkerID']}",
"name": {
"familyName": "@{items('For_each_employee')?['LastName']}",
"givenName": "@{items('For_each_employee')?['FirstName']}"
},
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
"userName": "@{items('For_each_employee')?['UserID']}"
},
"method": "POST",
"path": "/Users"
},
"type": "Compose"
}
},
"foreach": "@body('Get_employee_data_from_storage_table')?['value']",
"runAfter": {
"Get_employee_data_from_storage_table": ["Succeeded"]
},
"type": "Foreach"
},
"Get_employee_data_from_storage_table": {
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['azuretables']['connectionId']"
}
},
"method": "get",
"path": "/v2/storageAccounts/@{encodeURIComponent(encodeURIComponent('sthcmprovisioning1234'))}/tables/@{encodeURIComponent('employeedemo')}/entities"
},
"runAfter": {
"Compose_ProvisioningAPI_Settings": ["Succeeded"]
},
"type": "ApiConnection"
},
"Join_employee_SCIM": {
"inputs": {
"from": "@outputs('Compose_employee_SCIM')",
"joinWith": ","
},
"runAfter": {
"For_each_employee": ["Succeeded"]
},
"type": "Join"
},
"POST_SCIM_payload_to_API": {
"inputs": {
"authentication": {
"audience": "@{outputs('Compose_ProvisioningAPI_Settings')?['Audience']}",
"type": "ManagedServiceIdentity"
},
"body": "@outputs('Compose_SCIM_bulk_payload')",
"headers": {
"Content-Type": "application/scim+json"
},
"method": "POST",
"uri": "@outputs('Compose_ProvisioningAPI_Settings')?['Uri']"
},
"runAfter": {
"Compose_SCIM_bulk_payload": ["Succeeded"]
},
"type": "Http"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"Recurrence_0500am_every_day": {
"evaluatedRecurrence": {
"frequency": "Day",
"interval": 1,
"schedule": {
"hours": ["5"]
},
"timeZone": "W. Europe Standard Time"
},
"recurrence": {
"frequency": "Day",
"interval": 1,
"schedule": {
"hours": ["5"]
},
"timeZone": "W. Europe Standard Time"
},
"type": "Recurrence"
}
}
}
Im grafischen Editor sollte der Workflow so aussehen:
Abbildung 11: vollständiger Logic App Workflow