Modern Agent mit MGGraph
Bei einem Kunden konnte HCW die Installation des Modern Agent nicht abschließen. In meinem Lab habe ich daher die Aktionen des HCW analysiert und einige Requests dokumentiert. Das lag auch daran, dass das eingebaute Management mit der HybridManagment.psm1-PowerShell doch sehr eingeschränkt ist.
Warnung
Es gibt nur wenige Seiten, denen ich einen eigenen Abschnitt "Warnung" voranstelle. Hier ist es aber angeraten, den die Beispiele hier werden mit dem Recht "Directory.ReadWrite.All" ausgeführt und bei falscher Ausführung sind die Folgen nicht absehbar.
Zusammenhänge
Wenn Sie vielleicht die Seite Exchange Modern Hybrid Agent gelesen haben, dann wissen sie, dass der lokale Agent eine HTTPS-Verbindung zu einer Gegenstelle in Azure aufbaut, um eingehende Anfragen von Exchange Online anzunehmen. Das ist eine sehr vereinfachte Sichtweise, denn in der Konfiguration gibt es gleich mehrere Objekte, die aufeinander aufbauen.
Die Komponenten sind.
Baustein | Beschreibung |
---|---|
Exchange Online Migration Service |
Der aktive Part in Exchange Online wird durch Migration Batches gesteuert, welche eine Liste der zu migrierenden Postfächer, Zeiten und vor allem einen Verweis auf den zu verwendenden Migrationsendpunkt enthalten, |
Exchange Online Migration Endpunkt |
Der Migrationsendpunkt ist hingegen nur eine Konfigurationseinstellung, welche den Migrationsweg und die Gegenstelle enthält. Sie erhalten den Endpunkt mit "Get-MigrationEndpoint" in der Exchange Online PowerShell, welcher den Link zum nächsten Baustein enthält: PS C:\> Get-MigrationEndpoint | fl Identity : Hybrid Migration Endpoint - EWS (Default WebSite) EndpointType : ExchangeRemoteMove RemoteServer : <AppID-GUID>.resource.mailboxmigration.his.msappproxy.net Username : UCLABOR\svcmig Guid : ca5e0b9a-8367-44a8-8c81-8baafbd3c1fc IsRemote : True |
AzureAD Application |
Damit der Migration Endpoint eine Gegenstelle findet, legt HCW eine "Application" an, welche HTTPS-Requests auf den Namen "<AppID-GUID>.resource.mailboxmigration.his.msappproxy.net" annimmt und über eine AzureAD Connector Group an einen Agenten weiterreicht, der dann die hinterlegte interne URL anspricht. externalUrl : https://<AppID-GUID>.resource.mailboxmigration.his.msappproxy.net/ internalUrl : https://ex01.uclabor.de/ externalAuthenticationType : passthru isTranslateHostHeaderEnabled : False isTranslateLinksInBodyEnabled : False isOnPremPublishingEnabled : True isHttpOnlyCookieEnabled : False isSecureCookieEnabled : False isPersistentCookieEnabled : False applicationServerTimeout : Default applicationType : verifiedCustomDomainCertificatesMetadata : verifiedCustomDomainKeyCredential : verifiedCustomDomainPasswordCredential : singleSignOnSettings : @{SingleSignOnMode=none;KerberosSignOnSettings=} Wie sie dies auslesen, zeige ich ihnen weiter unten auf der Seite. |
AzureAD Connector Group |
Die für die Verbindung erforderlichen Connectoren werden in einer Connectorgroup zusammengefasst, den denen es per Default auch wieder einige gibt. id : e71c6f90-34d7-4b0e-be95-1f9e952a7bed name : Default group for AD Sync Fabric region : eur connectorGroupType : syncFabric isDefault : False id : f371a12e-3cd8-4cf2-9629-2fc2d4e8d63c name : Group-UCLABOR.DE-96e5bfea-ccfd-406c-bc6e-6673d01a4722 region : eur connectorGroupType : syncFabric isDefault : False id : 61914166-6324-4790-8515-49e30ac19114 name : Default group for Exchange Online region : eur connectorGroupType : exchangeOnline isDefault : False |
AzureAD Connector |
Der letzte Eintrag in Microsoft 365 /AzureAD ist der eigentliche Connector. Das ist letztlich nur die Information über einen Namen, seine Public IP und den Status: id : <connectorID-GUID> machineName : EX01.UCLABOR.DE externalIp : 80.66.20.27 status : active version : 1.5.732.0 |
OnPremises Hybrid Agent |
Zum Connector in der Cloud gehört dann ein lokaler Dienst, der die Verbindung zu Microsoft 365 und zum lokalen Exchange Server aufbaut. Er ist das "Relay", um die über den etablierten HTTPS-Kamal eingehenden Anfragen anzunehmen und zum Exchange zu senden. |
OnPremises Exchange Server |
Der lokale Exchange Server bekommt die Zugriffe unter seiner internen URL "https://<internal EWS Url>/ews/" und beantwortet diese. |
Ein Teil der Objekte sind "Programme" aber viele Objekte sind auch einfach nur Konfigurationseinstellungen, die aber zueinander passen müssen. Über Microsoft Graph können Sie aber einige Informationen direkt abfragen.
Anmeldung mit MGGraph
Dazu ist aber erst einmal eine Anmeldung erforderlich. Microsoft stellt dazu zwar das "HybridManagement.PSM1"-Modul bereit, was aber sehr eingeschränkt ist. Mit der aktuellere MGGraph PowerShell geht das meiner Ansicht nach viel bequemer.
Für die folgenden Befehle benötigen Sie einen PC mit installierter "MGGraph-PowerShell" und ein privilegiertes Konto.
Normalerweise nutzt die MGGraph-PowerShell ihre eigene "ClientID", die aber erst vom Administrator "erlaubt" werden muss (Consent). Aus dem Modul HybridManagment.psm1 geht aber hervor, dass Microsoft selbst in diesem Modul auch einfach die ClientID der "AzureAD PowerShell" verwendet. Das mache ich dann auch.
Connect-MgGraph ` -TenantId msxfaqlab.onmicrosoft.com ` -ClientId "1950a258-227b-4e31-a9cf-717495945fc2"
Allerdings lieferte dieser Versuch eine Fehermeldung:
Connect-MgGraph: InteractiveBrowserCredential authentication failed: AADSTS65002: Consent between first party application '1950a258-227b-4e31-a9cf-717495945fc2' and first party resource '00000003-0000-0000-c000-000000000000' must be configured via preauthorization - applications owned and operated by Microsoft must get approval from the API owner before requesting tokens for that API.
Zuerst dachte ich, dass Microsoft einen besonderen Schutz in der Cloud eingebaut hat, dass sich nicht jeder als "hybridManagement.psm1" ausgeben kann. Allerdings habe ich im Sourcecode keine weiteren "Besonderheiten" gesehen, die eine Verwendung dieser ClientID unterbunden hätte. Im Code von Hybridmanagemt.psm1 ist aber eine Funktion "GetAuthToken" enthalte und damit habe ich mir ein Token besorgt und dieses angeschaut. So bin ich an die Scopes gekommen und habe meinen Aufruf einfach etwas erweitert:
Connect-MgGraph ` -TenantId msxfaqlab.onmicrosoft.com ` -ClientId "1950a258-227b-4e31-a9cf-717495945fc2" ` -Scopes AuditLog.Read.All,Directory.AccessAsUser.All,email,openid,profile
Durch die explizite Angabe der Scopes habe ich eine gültige Session bekommen, was Sie mit Get-MGContext prüfen können:
Ich habe später auch auf die ClientID verzichtet und mich mit der MGGraph-ID ohne Probleme angemeldet und konnte die gleichen Informationen abrufen, wenn ein Admin-Consent erteilt wurde.
Dennoch muss ich bei Gelegenheit mich noch mal mit der First Party Application befassen.
- MGGraph PowerShell
- First Party Application
- HybridManagment.psm1
-
Connect-MGGraph
https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.authentication/connect-mggraph?view=graph-powershell-1.0 -
Get-MGContext
https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.authentication/get-mgcontext?view=graph-powershell-1.0 - Verify first-party Microsoft applications in sign-in
reports
https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in
ConnectorGroup auslesen
Der erste Anlaufpunkt ist die Abfrage der Connectoren und Connector Groups. Dazu brauche ich keine GUIDs eines Tenant, einer Application oder eines Agenten, so dass die Abfrage ein erster Test bezüglich Verbindung und Berechtigung ist. Er ist zudem nur Lesend und damit ungefährlich.
(Invoke-MGGraphRequest ` -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/' ` -OutputType PSObject ` ).value id : e71c6f90-34d7-4b0e-be95-1f9e952a7bed name : Default group for AD Sync Fabric region : eur connectorGroupType : syncFabric isDefault : False id : f371a12e-3cd8-4cf2-9629-2fc2d4e8d63c name : Group-UCLABOR.DE-96e5bfea-ccfd-406c-bc6e-6673d01a4722 region : eur connectorGroupType : syncFabric isDefault : False id : 61914166-6324-4790-8515-49e30ac19114 name : Default group for Exchange Online region : eur connectorGroupType : exchangeOnline isDefault : False
Hinweis: Ohne "-OutputType PSObject" bekomme ich das Ergebnis als Hashtable, in der der Value zum key "value" ein Array ohne Gruppierung ist.
- List connectorGroups
https://learn.microsoft.com/en-us/graph/api/connectorgroup-list?view=graph-rest-beta&tabs=http
Bei Microsoft wird noch ein anderes Beispiel aufgeführt:
Import-Module Microsoft.Graph.Beta.Applications Get-MgBetaOnPremisePublishingProfileConnectorGroup -OnPremisesPublishingProfileId $onPremisesPublishingProfileId
Allerdings habe ich keine Idee, was er hier als "onPremisesPublishingProfileId" erwartet.
- Get-MgBetaOnPremisePublishingProfileConnectorGroup
https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.beta.applications/get-mgbetaonpremisepublishingprofileconnectorgroup - Get connectorGroup
https://learn.microsoft.com/en-us/graph/api/connectorgroup-get?view=graph-rest-beta&tabs=http
Connector/Agent auflisten
Es gibt auch eine API, um die AzureAD Connectoren aufzulisten.
- Configure Microsoft Entra application proxy using Microsoft Graph APIs -
Step 3.1: Get connectors
https://learn.microsoft.com/en-us/graph/application-proxy-configure-api?tabs=powershell#step-31-get-connectors - List applications assigned to a
connectorGroup
https://learn.microsoft.com/en-us/graph/api/connectorgroup-list-applications?view=graph-rest-beta&tabs=http
Der Aufruf der URL bringt auch keinen Fehler aber eben auch keinen "Value".
Invoke-MGGraphRequest ` -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectors'
Allem Anschein nach lassen sich die Modern hybrid-Connectoren so nicht auflisten.
Es funktioniert aber über den Umweg sich bei den ConnectorGroups auch die Member mittels "$expand=members" mit anzeigen zu lassen.
PS C:\Users\fcarius> (Invoke-MGGraphRequest ` >> -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/?$expand=members' ` >> -OutputType PSObject).value.members id : e53d89eb-331d-449d-b120-448d930bf9db machineName : EX01.UCLABOR.DE externalIp : 80.66.20.27 status : active version : 1.5.732.0
Das ist so etwas unsauber, denn wir bekommen so die Members aller Gruppen. Daher sollten wir uns vorher die id der "Default group for Exchange Online" holen und dann mit der ID einsteigen.
Vermutlich geht das mit "$filter=" als
Parameter noch einfacher aber irgendwie habe ich keine
funktionierende Syntax gefunden.
Use the $filter query parameter
https://learn.microsoft.com/en-us/graph/filter-query-parameter?tabs=http
Daher habe ich wieder per PowerShell Pipeline gefiltert:
((Invoke-MGGraphRequest ` -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/?$expand=Members' ` -OutputType PSObject).value ` | where {$_.connectorGroupType-eq "exchangeOnline"} ).members
Damit habe wird auch den Connector in der Gruppe samt ID, Servername, öffentliche IP-Adresse und sogar den Status. Dieses Wissen habe ich auf der Seite Hybrid Agent Statusabfrage gleich weiter verarbeitet.
Connector löschen
Was passiert, wenn sie lokal einen Hybrid Agent deinstallieren? Nach meiner Erfahrung bleibt der Connector im Tenant erhalten aber erhält den Status "inaktiv". Ich habe bei keinen Versuchen einer Deinstallation gesehen, dass der Connector wieder deregistriert wird.
Nach meiner Einschätzung bleibt er in dem Status "inaktiv".
Ich habe aber auch keinen Weg gefunden, den Connector zu löschen. Ich kann ihn vielleicht aus der Zuordnung zur Gruppe entfernen aber wird er damit auch komplett gelöscht?
- Configure Microsoft Entra application proxy using Microsoft Graph APIs
https://learn.microsoft.com/en-us/graph/application-proxy-configure-api?tabs=http
HybridApplication auflisten
Eine weitere Komponente ist die Hybrid Application, deren Konfiguration in AzureAD die Verbindung von einem öffentlichen Namen über die Connectorgroup zum Agenten herstellt. Ehe wir sie im übernächsten Abschnitt weiter anzeigen müssen wir sie erst einmal finden. Microsoft Graph stellt natürlich eine API zum Anzeige von AzureAD Apps bereit.
Import-Module Microsoft.Graph.Applications # Anzeigen aller Apps Get-MgApplication # Anzeigen einer bestimmten App Get-MgApplication -ApplicationId $applicationId
Bei mir liefert dies auch eine Ausgabe aber nur die "regulären" Apps, die ich auch im Azure Portal sehe.
Meine Hybrid App ist hier nicht darunter
- Manage a Microsoft Entra application using Microsoft Graph
https://learn.microsoft.com/en-us/graph/tutorial-applications-basics?tabs=http - List applications
https://learn.microsoft.com/en-us/graph/api/application-list?view=graph-rest-1.0&tabs=http - Get application
https://learn.microsoft.com/en-us/graph/api/application-get?view=graph-rest-1.0&tabs=http
So komme ich nicht an die Daten. Allerdings kann ich wieder über die ConnectorGroups gehen, bei denen ich mir nicht nur die "member", d.h. die Connectoren, sondern auch die verknüpften Applications holen kann. Ich muss nur ein "?$expand=applications" an die URL anhängen:
((Invoke-MGGraphRequest ` -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/?$expand=applications' ` -OutputType PSObject).value ` | where {$_.connectorGroupType-eq "exchangeOnline"} ).applications id appId onPremisesPublishing -- ----- -------------------- eb92551b-9c63-4472-a8dc-2be7fcf00313 25103235-c936-4d6b-8686-f1eca4e70286 @{externalUrl=… ((Invoke-MGGraphRequest ` -Uri 'https://graph.microsoft.com/beta/onPremisesPublishingProfiles/applicationProxy/connectorGroups/?$expand=applications' ` -OutputType PSObject).value ` | where {$_.connectorGroupType -eq "exchangeOnline"} ).applications[0].onpremisespublishing
Auch hier habe ich auf "connectorGroupType = exchangeOnline" gefiltert und mir die erste Application anzeigen lassen.
Hier finde ich aber keine ID o.ä. aber aus dem "externalURL"-Feld kann ich mir die AppID aus dem Hostnamen extrahieren.
HybridApplication anzeigen
Mit dem Wissen um die AppID kann ich nun über einen anderen Endpunkt auch die gleichen Details erhalten. Den Pfad habe ich auch wieder aus dem Installationsprotokolle des HCW. Im Pfad ist die TenantID und die AppID enthalten:
GET https://graph.microsoft.com/beta/<TenantID>/applications/<AppID>/OnPremisesPublishing
Meine Application im Labor-Tenant hatte zu der Zeit folgende Daten:
Invoke-MGGraphRequest ` -Uri "https://graph.microsoft.com/edu/463e98ae-b7d4-4af2-8ef8-3eda0b4d8a7c/applications/eb92551b-9c63-4472-a8dc-2be7fcf00313/OnPremisesPublishing" ` -Method get -OutputType PSObject
Die Herausforderung ist dabei natürlich, die beiden IDs zu erhalten. Die TenantID können Sie noch recht einfach im AzureAD-Portal unter https://portal.azure.com direkt auf der Startseite ablesen. Für die Ermittlung der AppID habe ich aber noch keinen offiziellen Weg gefunden.
ich habe den Verdacht, dass HCW die Information aus dem dem Namensteil der ExternalURL beim bereit konfigurierten "Migration Endpoint" ausliest und bei einer neuen Installation eine neue GUID erstellt. Auch im Code von HybridManagment.psm1 gibt es eine Funktion "New-HybridApplication", die so vorgeht:
Wenn Sie aber den HCW ausführen, dann finden sie im Log auch den Namen der Application. Suchen Sie nach dem Text "connectorapplicationname". Sie sollten etwas in der Form finden:
Eventuell kommt er auch irgendwo aus dem lokalen Active Directory und der Exchange Organization. Aber selbst ein LDIFDE-Export und eine suche nach der GUID als auch nach der BASE64 codierten GUID brachte keine Treffer.
$guid = "eb92551b-9c63-4472-a8dc-2be7fcf00313" [system.convert]::ToBase64String(([GUID]($guid)).ToByteArray())
Was dann wieder für eine Abfrage aus dem AzureAD spricht. Ich hoffe Sie haben ihre AppID aus dem HCWLog gefunden.
- onPremisesPublishing resource type
https://learn.microsoft.com/en-us/graph/api/resources/onpremisespublishing?view=graph-rest-beta - Reporting on Entra Application Proxy
published applications – Graph PowerShell
https://f12.hu/2024/04/04/reporting-on-entra-application-proxy-published-applications-graph-powershell/
HybridApplication löschen
Aus dem Code des Moduls HybridManagment.psm1 kann ich auch die Funktion "Remove-HybridApplication" analysieren. Das Modul nutzt Invoke-Webrequest, um mit einem jedes mal neu erstellten AuthToken einen Graph-Aufruf zu starten. Das geht mit MGGraph einfacher:
Invoke-MGGraphRequest ` -Uri "https://graph.microsoft.com/beta/463e98ae-b7d4-4af2-8ef8-3eda0b4d8a7c/applications/eb92551b-9c63-4472-a8dc-2be7fcf00313" ` -Method PATCH ` -Body "{"onPremisesPublishing": null}"
Indem man den Body mit "Null" setzt, wird die App gelöscht.
Ich habe Kundenumgebungen gesehen, in denen es scheinbar keine Apps mehr gegeben hat aber die InternalURL des Exchange Servers die Neuanlage einer App mittels HCW verhindert hat.
Ich habe noch keinen Weg gefunden, wie ich im AzureAD-Verzeichnis nach solchen Werten suchen kann
HybridApplication anlegen
Wenn ihr HCW die Applikation nicht anlegen kann, dann könnte es helfen, wenn Sie die Applikation anlegen. Bei der Installation durch HCW erfolgt dies alles im Hintergrund. Mit Fiddler habe ich aber den entsprechenden Aufruf mitgeschnitten:
PATCH https://graph.microsoft.com/beta/463e98ae-b7d4-4af2-8ef8-3eda0b4d8a7c/applications/eb92551b-9c63-4472-a8dc-2be7fcf00313 HTTP/1.1 Authorization: Bearer eyJ0e..... Content-Type: application/json; charset=utf-8 Host: graph.microsoft.com Content-Length: 563 Expect: 100-continue { "onPremisesPublishing":{ "applicationServerTimeout":"Default", "applicationType":"microsoftapp", "externalAuthenticationType":"passthru", "externalUrl":"https://eb92551b-9c63-4472-a8dc-2be7fcf00313.resource.mailboxmigration.his.msappproxy.net:443/", "internalUrl":"https://ex01.uclabor.de:443/", "isOnPremPublishingEnabled":true, "isTranslateHostHeaderEnabled":false, "isTranslateLinksInBodyEnabled":false, "singleSignOnSettings":null, "verifiedCustomDomainCertificatesMetadata":null, "verifiedCustomDomainKeyCredential":null, "verifiedCustomDomainPasswordCredential":null } }
Hier legt HCW in meinem Tenant (GUID = 463e98ae-b7d4-4af2-8ef8-3eda0b4d8a7c) eine neue Application (GUID = eb92551b-9c63-4472-a8dc-2be7fcf00313) an. Auch das geht per Powershell und in HybridManagment.psm1 ist auch ein Beispiel im Code von "Update-HybridApplication" enthalten. Eine Neuanlage ist nämlich auch nur ein "Update" auf eine nicht vorhandene Application:
param ( $TenantID="463e98ae-b7d4-4af2-8ef8-3eda0b4d8a7c", #MSXFAQLAB $AppID="eb92551b-9c63-4472-a8dc-2be7fcf00313", $targetUri = "https://ex01.uclabor.de" ) $application = @{ "onPremisesPublishing" = @{ "applicationServerTimeout" = "Default" "applicationType" = "microsoftapp" "externalAuthenticationType" = "passthru" "externalUrl" = "https://$($AppId).resource.mailboxmigration.his.msappproxy.net:443/" "internalUrl" = $targetUri "isOnPremPublishingEnabled" = $true "isTranslateHostHeaderEnabled" = $false "isTranslateLinksInBodyEnabled" = $false "singleSignOnSettings" = $null "verifiedCustomDomainCertificatesMetadata" = $null "verifiedCustomDomainKeyCredential" = $null "verifiedCustomDomainPasswordCredential" = $null } } Invoke-MGGraphRequest ` -Uri "https://graph.microsoft.com/edu/$($tenant)/applications/$($appId)" ` -Method PATCH ` -Body ($application | ConvertTo-Json)
Stelle Sie natürlich sicher, dass die Parameter passen. Ansonsten dürfen Sie gleich den vorherigen Abschnitt zum Entfernen wieder durchführen.
All diese Apps sind nicht im Azure Portal zu sehen!