Cross-Tenant Sync - API/Graph
Konfiguration und Überwachen per Browser unter https://portal.azure.com ist einfach und bei Fehlern eine Mail zu erhalten, ist auch kein Enterprise Monitoring Diese Seite beschäftigt sich dem Zugriff per Graph und anderen APIs zum Cross-Tenant Sync.
Verfügbare APIs
Da "Cross-Tenant Sync" im Juni 2023 noch ein sehr junges Produkt ist, und ich auf die Schnelle erst einmal keine API gefunden habe, bin ich auf https://portal.azure.com gegangen und habe mir angeschaut, was der Browser denn im Hintergrund so macht. Quasi alle Browseroberflächen nutzen heute ein clientbasiertes Rendering mit JavaScript und rufen Daten von REST-Services ab. Über den F12-Debugger im Browser kann ich gut verfolgen, welche Schnittstellen angesprochen werden.
Wenn ich im Browser z.B. den Dirsync stoppe, starte oder einen Restart anstoße, dann sehe ich direkt HTTP POST-Requests auf folgende URLs:
https://graph.microsoft.com/beta/servicePrincipals/<guid>/synchronization/jobs/Azure2Azure.eef62a097718406382dbd7582dc8916f.<guid>/start https://graph.microsoft.com/beta/servicePrincipals/<guid>/synchronization/jobs/Azure2Azure.eef62a097718406382dbd7582dc8916f.<guid>/stop https://graph.microsoft.com/beta/servicePrincipals/<guid>/synchronization/jobs/Azure2Azure.eef62a097718406382dbd7582dc8916f.<guid>/restart
Damit ist schon mal klar dass Microsoft hier mit Graph und servicePrincipals arbeitet. Aber das ist nicht alles, denn ich habe immer noch API-Aufrufe gegen das Portal selbst gesehen. Microsoft selbst dokumentiert insbesondere die "B2B-Connec"-Einstellungen sehr gut. Das mag auch daran liegen, dass in der Beta-Phase einfach noch keine GUI da war und die frühen Piloten zwangsweise per PowerShell die gewünschten Einstellungen für Teams Shared Channel gesetzt werden mussten. Aber auch eine Abfrage des Audit Log per Azure-Portal hat letztlich nur folgenden Aufruf gestartet:
https://graph.microsoft.com/v1.0/auditLogs/provisioning? $filter=( ( contains(tolower(sourceIdentity/id),%20%27aduser%27) %20or%20contains(tolower(sourceIdentity/displayName),%20%27aduser%27) %20or%20contains(tolower(targetIdentity/id),%20%27aduser%27) %20or%20contains(tolower(targetIdentity/displayName),%20%27aduser%27) ) %20and%20( contains(tolower(servicePrincipal/id),%20%27804f3a94-2255-42a4-98de-c012d5574488%27) %20or%20contains(tolower(servicePrincipal/displayName),%20%27804f3a94-2255-42a4-98de-c012d5574488%27) ) %20and%20activityDateTime%20gt%202023-06-08T14:28:11.405Z %20and%20activityDateTime%20lt%202023-06-09T14:28:11.405Z ) &$top=500 &$orderby=activityDateTime%20desc
- Cross-tenant access settings API
overview
https://learn.microsoft.com/en-us/graph/api/resources/crosstenantaccesspolicy-overview?view=graph-rest-1.0 - Configure Cross-Tenant Synchronization using PowerShell or
Microsoft Graph API
https://learn.microsoft.com/en-us/azure/active-directory/multi-tenant-organizations/cross-tenant-synchronization-configure-graph?tabs=ms-powershell
Statusabfrage
Auch wenn ich bislang alle Einstellungen per Browser im Azure-Portal verwalten konnte, sollte ich schon wissen, welche APIs der Browser anspricht und welche APIs ich direkt nutzen kann, z.B.: um den Status und die Protokolle automatisiert abzufragen und damit zu überwachen. Im Portal gibt es dazu eine Übersichtsseite:
Die Werte werden per REST-Aufruf gegen folgende URL nachgeladen.
https://graph.microsoft.com/beta/servicePrincipals/<guid>/synchronization/jobs
Die JSON-Antwort enthält noch viel mehr Informationen, als das GUI anzeigt.
{ "@odata.context": "https://graph.microsoft.com/beta/$metadata#servicePrincipals('<guid>')/synchronization/jobs", "value": [ { "id": "Azure2Azure.eef62a097718406382dbd7582dc8916f.<guid>", "templateId": "Azure2Azure", "schedule": { "expiration": null, "interval": "PT40M", "state": "Active" }, "status": { "countSuccessiveCompleteFailures": 0, "escrowsPruned": false, "code": "Active", "lastSuccessfulExecutionWithExports": null, "quarantine": null, "steadyStateFirstAchievedTime": "2023-06-12T07:34:21.4337447Z", "steadyStateLastAchievedTime": "2023-06-12T08:33:56.0416458Z", "troubleshootingUrl": "", "lastExecution": { "activityIdentifier": "<guid>", "countEntitled": 0, "countEntitledForProvisioning": 0, "countEscrowed": 1, "countEscrowedRaw": 1, "countExported": 0, "countExports": 0, "countImported": 2, "countImportedDeltas": 0, "countImportedReferenceDeltas": 0, "state": "Succeeded", "error": null, "timeBegan": "2023-06-12T08:33:54.9829697Z", "timeEnded": "2023-06-12T08:33:56.0426453Z" }, "lastSuccessfulExecution": { "activityIdentifier": null, "countEntitled": 0, "countEntitledForProvisioning": 0, "countEscrowed": 0, "countEscrowedRaw": 0, "countExported": 3, "countExports": 0, "countImported": 0, "countImportedDeltas": 0, "countImportedReferenceDeltas": 0, "state": "Succeeded", "error": null, "timeBegan": "0001-01-01T00:00:00Z", "timeEnded": "2023-06-12T07:34:21.4337447Z" }, "progress": [], "synchronizedEntryCountByType": [ { "key": "User to User", "value": 9 } ] }, "synchronizationJobSettings": [ { "name": "AzureIngestionAttributeOptimization", "value": "True" }, { "name": "LookaheadQueryEnabled", "value": "False" } ] } ] }
Wenn ich diese URL nun einfach mit einem "Invoke-Webrequest" aufrufen, dann bekomme ich natürlich einen Fehler:
{"error": { "code":"InvalidAuthenticationToken", "message":"Access token is empty.", "innerError": { "date":"2023-06-12T08:58:25", "request-id":"e0800998-6781-4968-9334-601246658921", "client-request-id":"e0800998-6781-4968-9334-601246658921" } } }
So einfach bekomme ich es natürlich nicht gemacht. Ich muss mich schon entsprechend anmelden. Im F12-Debugger sehe ich im Request das Bearer-Token:
Den kann ich z.B. auf https://jwt.io oder https://jwt.ms einfach decodieren. In diesem Token stehen sehr viele Berechtigungen darin:
AccessReview.ReadWrite.All AuditLog.Read.All ConsentRequest.ReadWrite.All Directory.AccessAsUser.All Directory.Read.All Directory.ReadWrite.All Directory.Write.Restricted DirectoryRecommendations.Read.All DirectoryRecommendations.ReadWrite.All email EntitlementManagement.Read.All Group.ReadWrite.All IdentityProvider.ReadWrite.All IdentityRiskEvent.ReadWrite.All IdentityUserFlow.Read.All openid Policy.Read.All Policy.Read.IdentityProtection Policy.ReadWrite.AuthenticationFlows Policy.ReadWrite.AuthenticationMethod Policy.ReadWrite.ConditionalAccess Policy.ReadWrite.ExternalIdentities Policy.ReadWrite.IdentityProtection Policy.ReadWrite.MobilityManagement profile Reports.Read.All RoleManagement.ReadWrite.Directory SecurityEvents.ReadWrite.All TrustFrameworkKeySet.Read.All User.Export.All User.ReadWrite.All UserAuthenticationMethod.ReadWrite.All"
Das ist aber einfach dadurch zu erklären, dass die "App Azure-Portal" natürlich nicht nur den "Cross-Tenant Sync" konfiguriert, sondern ich als Global Admin auch alle anderen Aspekte des Tenants verändern könnte und daher mein Token sehr groß ist.
Service Principal finden
Um per Graph weitere Details zu einer Sync-Konfiguration zu ermitteln, muss ich aber erst einmal die GUID des Service Principal ermitteln. Zwar gibt es mit List servicePrincipals (https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list?view=graph-rest-1.0&tabs=http) einen Weg alle Einträge zu listen aber wonach muss ich denn suchen? Auch hier hilft der F12-Debugger, mit dem ich den relevanten Aufruf ermitteln konnte. Es war nicht auf den ersten Blick sichtbar, da es ein "Batch"-Abruf ist.
- Combine multiple requests in one HTTP
call using JSON batching
https://learn.microsoft.com/en-us/graph/json-batching - List servicePrincipals
https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list?view=graph-rest-1.0&tabs=http
Da ich aber zwei Konfigurationen hatte, reichte eine Suche nach den Strings um die Antwort und den dazu passenden Request zu finden:
Die Abfrage des Webseite nutzt folgenden Query.
GET /servicePrincipals? $count=true &$filter=(applicationTemplateId eq '518e5f48-1fc8-4c48-9387-9fdf28b0dfe7') &$top=20 &$orderby=createdDateTime asc
Interessanterweise bekam ich im GraphExplorer die Fehlermeldung, dass "Sorting" bei der Anfrage nicht erlaubt wäre. Ich habe sie dann etwas gekürzt:
https://graph.microsoft.com/v1.0/servicePrincipals?$count=true&$filter=(applicationTemplateId eq '518e5f48-1fc8-4c48-9387-9fdf28b0dfe7')
Damit habe ich beide Sync-Jobs bekommen. Mit der GUID des ServicePrincipal komme ich dann auch an die Einstellungen heran:
Ich habe es hier aber auf "Lesen" belassen.
Reduzierte Rechte
Mit dem Graph Explorer kann ich aber den Request auch einfach ausführen und mir dabei anzeigen lassen, welche Berechtigungen ich effektiv für diesen Aufruf benötige:
Um den Status auszulesen liefert mit GraphExplorer die Berechtigungen "Synchronization.Read.All" und "Synchronization.ReadWrite.All". Ich denke, dass dies aber allein zum Lesen des Status auch schon zu viel ist und sich ein Skript auch mit "Synchronization.Read.All" begnügen könnte. Bei der Einrichtung eine "App" kann das Recht entsprechend spezifiziert werden:
- Securing service principals in Azure
Active Directory
https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/service-accounts-principal - List servicePrincipals
https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list?view=graph-rest-1.0&tabs=http
Weitere Links
- Cross-Tenant Sync
- Cross-Tenant Sync - Konfiguration
- Cross-Tenant Sync - Details
- Verbinden von Organisationen
- Metadirectory
- Generally Availability - Cross-Tenant Synchronization
https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/whats-new#generally-availability---cross-tenant-synchronization - What is Cross-Tenant Synchronization?
https://learn.microsoft.com/en-us/azure/active-directory/multi-tenant-organizations/cross-tenant-synchronization-overview - What is Cross-Tenant Synchronization? :
License requirements
https://learn.microsoft.com/en-us/azure/active-directory/multi-tenant-organizations/cross-tenant-synchronization-overview#license-requirements - Overview: Cross-tenant access with Azure AD External Identities
https://learn.microsoft.com/en-us/azure/active-directory/external-identities/cross-tenant-access-overview - B2B direct connect overview
https://learn.microsoft.com/en-us/azure/active-directory/external-identities/b2b-direct-connect-overview - What is a multi-tenant organization in Azure Active Directory?
https://learn.microsoft.com/en-us/azure/active-directory/multi-tenant-organizations/overview - Mergers, Acquisitions and Day 1 – Azure AD Cross-Tenant Synchronization
https://shehanperera.com/2023/03/24/aad-cross-tenant-sync/ - Configure cross-tenant access settings
for B2B collaboration
https://learn.microsoft.com/en-us/azure/active-directory/external-identities/cross-tenant-access-settings-b2b-collaboration - Billing model for Azure AD External
Identities
https://learn.microsoft.com/en-us/azure/active-directory/external-identities/external-identities-pricing - A look under the hood of Azure AD
cross-tenant synchronization
https://goodworkaround.com/2023/03/08/a-look-under-the-hood-of-azure-ad-cross-tenant-synchronization/ - Eight things you should know about Azure
AD Cross-Tenant Synchronization
https://dirteam.com/sander/2023/06/02/eight-things-you-should-know-about-azure-ad-cross-tenant-synchronization/ - Use Cross-Tenant Synchronization in
Azure AD to Experience Seamless
Collaboration
https://o365reports.com/2023/04/12/use-cross-tenant-synchronization-in-azure-ad/ - Introducing Azure AD Cross-Tenant Synchronization
https://community.dynamics.com/business/b/stefanodemiliani/posts/introducing-azure-ad-cross-tenant-synchronization
https://www.powercommunity.com/introducing-azure-ad-cross-tenant-synchronization/
John Savill's Technical Training:
Azure AD Cross-Tenant Sync
https://www.youtube.com/watch?v=z0J5kteqUVQ