EXO PowerShell Automation

Für die Verwaltung von Exchange Online per PowerShell starten Sie ein "Connect-ExchangeOnline" und geben ihre Anmeldedaten ein. Da MFA heute quasi Pflicht ist, funktioniert das mit hinterlegten Kennworten nur noch eingeschränkt, z.B. wenn der "Conditional Access" z.B. über die IP-Adresse geregelt ist. Eine automatische Anmeldung per Programm kann ja kaum die Authenticator-App auf einem Smartphone bedienen. Aber die Exchange Online PowerShell V2 erlaubt auch die Verwendung als App mittels Zertifikat.

Nutzen Sie bitte die Exchange PowerShell V3. V2 ist zum Sommer 2023 abgekündigt.

Apps, Graph und Exchange

Viele Jahrzehnte haben wir Administratoren für automatische Prozesse entsprechende Dienstkonten angelegt oder Computerkonten genutzt Exchange 4.0-5-5 hatte noch einen Domain Benutzer zum Betreiben der Dienste und erst mit Exchange 2000 liefen die Exchange Dienste dann als "LocalSystem" und mittlerweile noch weniger Rechten. Auch in der Cloud gibt es die RoleGroup "Exchange Administratoren" und ein AzureAD-Benutzer kann die Berechtigungen bekommen. Allerdings ist eine automatische Anmeldung mit einem regulären Benutzerkonto und Kennwort recht unsicher und eignet sich wenig für eine Automatisierung. Zum einen sollten Sie privilegierte Konten mit MFA ausstatten, dass eine Automatisierung erschwert. Kritischer ist aber die Offenheit, dass sich jemand auch regulär als "Azure AD Benutzer" anmelden kann.

Graph und andere APIs kennen aber die Möglichkeit von App-Berechtigungen, d.h. der Code authentifiziert sich mit einer GUID als Benutzername und einem Kennwort oder Zertifikat. Die Applikation bekommt dann genau die Berechtigungen, die diese benötigt aber kann sich nicht interaktiv anmelden. Dieser Weg ist auch mit der Exchange PowerShell V2.0.3 oder neuer möglich. Allerdings muss ich dazu zwingend ein Zertifikat nutzen.

Die Einrichtung ist dann relativ schnell erfolgt:

  • Zertifikat generieren
    Das kann auch ein Self Signed Certificate sein. Es kann als PFX-Datei vorliegen oder im Zertifikatsspeicher des PCs abgelegt sein. PFX-Dateien sind "portabel" aber natürlich unsicherer als
  • App konfigurieren
    Hier müssen das Zertifikat und die Berechtigungen konfiguriert werden. Die erforderlichen Berechtigungen sind leider nicht per Browser direkt auswählbar, sondern müssen über ein Manifest eingerichtet werden.
  • Skript umstellen
    In ihrer Automatisierungslösung müssen Sie dann nur den Connect-Exchange Online mit den passenden Parametern anlegen

Dieser Weg kann durchaus auch für die tägliche Administration per Exchange Online PowerShell interessant sein, wenn ich kein eigenes "Admin-Konto" o.ä. brauche, sondern einfach nur eine Exchange Online PowerShell. Ich kann die PFX-Datei ja mit einem Kennwort sichern und damit sicher und ohne MFA-Einrichtung nutzen. Mein Anwendungsfall ist aber tatsächlich die Automatisierung von Exchange Online-Aufgaben wie z.B. die Verwaltung von MailContacts in einem Exchange Online-Tenant zur besseren Zusammenarbeit zwischen zwei Tenants. Microsoft beschreibt die Schritte in einer anderen Reihenfolge. Ich lege aber zuerst das Zertifikat an.

Zertifikat anlegen

Die Exchange Online PowerShell V2 erlaubt keine Anmeldung mit einer AppID und einen Kennwort sondern erfordert ein Zertifikat. Mit den folgenden PowerShell-Befehlen lege ich ein Zertifikat an.

Das Zertifikat ist per Default 1 Jahr gültig. Überlegen Sie sich daher, wie Sie jede Jahr das Zertifikat verlängern oder tauschen. Das nahe Ende wird auch im Azure Portal angezeigt.

Sie können die Gültigkeit auch auf z.B. 5 Jahre oder mehr setzen. Da es aber ein "SelfSigned"-Cert ist, gibt es keine IssuingCA, die ein Zertifikat zurückziehen kann. Bei einer Kompromittierung müssen Sie in der App Registration das Zertifikat entfernen.

Microsoft empfiehlt die Ausführung als Administrator aber bei mir haben die folgenden Schritte auch als normaler Benutzer funktioniert.

# Zertifikat generieren 
$mycert = New-SelfSignedCertificate `
             -DnsName "msxfaqdev1Jahr" `
             -CertStoreLocation "cert:\CurrentUser\My" `
             -NotAfter (Get-Date).AddYears(1) `
             -KeySpec KeyExchange 


# Export certificate in .pfx Datei mit private Key
$mycert | Export-PfxCertificate `
             -FilePath msxfaqdev1jahr.pfx `
             -Password $(ConvertTo-SecureString -String "CertPassWord!" -AsPlainText -Force) 


# Export certificate als .cer datei zum Upload in Azure
$mycert | Export-Certificate  `
             -FilePath msxfaqdev1jahr.cer

# Thumbprint ausgeben
Write-host $mycert.Thumbprint

Das Zertifikat finden Sie auch im Zertifikatsspeicher ihres Benutzers.

Wenn ein Skript immer genau auf einem System mit einem bekannten Konto ausgeführt wird, dann ist die Ablage des Zertifikat in sicheren Zertifikatsspeicher mein Favorit. Damit erspare ich mir die Hinterlegung eines Kennworts an einer weniger sicheren Stelle zum Entsperren der PFX-Datei.

App konfigurieren

Der nächst Schritt ist etwas umfangreicher aber von Microsoft sehr gut mit Bildern dokumentiert. Daher beschreibe ich hier nur eine Kurzfassung:

  • Neue App Registration anlegen
    Über die Adresse https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps sehe ich alle bereits registrierten Apps und Starte mit "New Registration".
    Ich gebe der App einen sprechenden Namen "MSXFAQDEV EXOPS2 CBA" und lasse anderen Werte auf Default
  • AppID kopieren
    Nach der Registrierung bin ich auf der Übersichtsseite und kopiere mir hier schon mal die "Application (Client) ID", denn das ist der spätere Benutzername
  • Zertifikat hochladen
    Dann nutze ich die im ersten Schritt exportierte CER-Datei, um den "Public Key" des später genutzten Zertifikats in der App zu hinterlegen.
  • App Berechtigungen
    Laut Microsoft muss ich das über eine MANIFEST-Datei machen. Aber ich habe einen Weg über die Webseite gefunden:

    Im nächsten Dialog wähle ich dann die passende "Application Permission: Exchange.ManageAsApp" aus.

    Zuletzt muss ich noch "Admin Consent" gewähren.
  • Exchange Admin Rolle vergeben
    Zuletzt muss ich der App auch noch die entsprechende AzureAD-Rolle vergeben. Dazu muss ich im Azure-Portal auf https://portal.azure.com/#blade/Microsoft_AAD_IAM/RolesManagementMenuBlade/AllRoles/ gehen und die Rolle "Exchange Administrator" suche und dann die App addiere. Erinnern sie sich noch an den Namen, den sie am Anfang vergeben haben?

Damit ist die Einrichtung im Azure AD abgeschlossen.

Wenn Sie als Dienstleister für viele Kunden arbeiten wollen, dann könnten Sie die App sogar als "Multi Tenant"-App bereitstellen, so dass Sie viele Exchange Online Tenants "as a Service" verwalten können.

RBAC Rollen zuweisen (Optional)

Wenn Sie die soeben konfigurierte App direkt nutzen, dann haben Sie alle Rechte auf die Exchange Konfiguration. Sie sind quasi "Global Admin" hinsichtlich Exchange. Das ist für eine App, die vielleicht nur einen kleinen Teilaspekte der Exchange Konfiguration verwalten soll, viel zu viel. Seit Exchange 2010 kennen wir "OnPremises" die Funktion RBAC - Role Based Access Control. Diese gibt es in vergleichbarer Form auch bei Exchange Online und kann nicht nur auf Anwender, sondern auch auf App-Registrations angewendet werden. Genau genommen können wir in Exchange damit steuern, welche App, welches Rechte auf welchen Scope anwenden darf. Eine Rollenzuweisung kombiniert diese drei Bausteine.

Damit Exchange aber die App als kennt, brauchen wir die AppID und legen damit ein "Service Principal" an. Technisch legen wir im Echange Online Forest ein Objekt an, damit wir diesem dann die Berechtigungen zuweisen können.

New-ServicePrincipal `
   -AppId <CApplicationID> `
   -ObjectId <objectID> `
   -DisplayName <name>

Die Werte für die ApplicationID und ObjectID übernehmen Sie einfach aus der App Registration im AzureAD-Portal

Sie können in Exchange Online auch noch einen "Management Scope" anlegen. Hier ein Beispiel basierend auf dem Land:

New-ManagementScope `
   -Name "Mitarbeiter in Deutschland" `
   -RecipientRestrictionFilter ""CountryOrRegion -eq 'Germany'"

Denkbar sind natürlich auch CustomAttribute, Gruppenmitgliedschaften etc.

Mit dem ServicePrincipal und dem optionalen ManagementScope kann ich dann die Rechte verknüpfen,

New-ManagementRoleAssignment `
   -Name "Rollenbezeichnung" `
   -Role <RoleIdParameter> `
   -App <AppID> `
   -CustomResourceScope <Management Scope> (oder -RecipientAdministrativeUnitScope)

Damit beschränke ich die weiter oben angelegte App auf die entsprechenden Empfänger und gewünschte Funktion

Exchange Online PowerShell verbinden

Mit der AppID der gerade angelegten Application (bei mir 9cb958d8-3f03-4f5e-b71a-8ae25a289b78) und dem Zertifikat kann ich direkt die Exchange Online PowerShell V2 verbinden.

Wichtig ist, dass Sie mit dem Parameter "-Organization" den Tenant spezifizieren, denn die PowerShell hat ja "nur" das Zertifikat und keinen UPN eines Benutzers, anhand dessen er den richtigen Tenant ermitteln kann.

# Verbinden mit PFX-Datei und Zertifikat
Connect-ExchangeOnline `
   -CertificateFilePath ".\automation-cert.pfx" `
   -CertificatePassword (ConvertTo-SecureString -String "<MyPassword>" -AsPlainText -Force) `
   -AppID "36ee4c6c-0812-40a2-b820-b22ebd02bce3" `
   -Organization "msxfaqdev.onmicrosoft.com"

Wenn ich das Zertifikat im Computerstore habe, dann brauche ich kein Kennwort.

Connect-ExchangeOnline `
   -CertificateThumbPrint "815E2AD820B297DF4ED8D278CCE2D06EDAE35267" `
   -AppID "9cb958d8-3f03-4f5e-b71a-8ae25a289b78" `
   -Organization "msxfaqdev.onmicrosoft.com"

Die Verbindung ist erstaunlich robust. Auch ein Wechsel der IP-Adresse, ein "Schlafmode" des Notebook über 8 Stunden etc. hat die PowerShell nicht gestört. Die Verbindung wurde automatisch wieder erneuert.

Alternativ kann ich mich auch mit einem "Client Secret" verbinden, wobei ich mir dazu erst ein Access-Token generieren muss. Das geht klassisch manuell mit einem Invoke-WebRequest:

$TenantID     = "msxfaqdev.onmicrosoft.com"              #GUID oder onmicrosoft.com-domain
$ClientID     = "9cb958d8-3f03-4f5e-b71a-8ae25a289b78"   #AppID der Application
$Scopes       = 'https://outlook.office365.com/.default'   # Default Scope
$ClientSecret = "Kennwort der App"


$token = Invoke-RestMethod `
   -Method POST `
   -ContentType 'application/x-www-form-urlencoded' `
   -Uri "https://login.microsoftonline.com/$($tenantid)/oauth2/v2.0/token" `
   -Body @{
       client_id     = $clientid
       client_secret = $clientsecret
       scope         = Scopes'
       grant_type    = 'client_credentials'
    }

Alternative können Sie auch MSAL.PS dazu verwenden.

Import-module msal.ps
$msalParams = @{
        TenantID = "msxfaqdev.onmicrosoft.com"              #GUID oder onmicrosoft.com-domain
        ClientID = "9cb958d8-3f03-4f5e-b71a-8ae25a289b78"   #AppID der Application
        Scopes = 'https://outlook.office365.com/.default'   # Default Scope
        ClientSecret = (ConvertTo-SecureString "hier das Kennwort rein" -AsPlainText)
}
$msalResult = Get-MsalToken @msalParams 
Connect-ExchangeOnline `
   -Organization msxfaqdev.onmicrosoft.com `
   -AccessToken $msalResult.AccessToken

Achtung: Hinterlegen Sie nie Kennworte in einem Skript. Zu oft wurde solcher Source Code dann auf Github oder anderen Plattformen irrtümlich veröffentlicht oder durch Angreifer "gefunden"

Fehlerbilder

Mögliche Fehler, auf die ich gestoßen bin:

  • Admin Consent nicht gewährt:
Exception: Connecting to remote server outlook.office365.com failed with the following error message : Zugriff verweigert For more
information, see the about_Remote_Troubleshooting Help topic.
  • Fehlende Exchange Admin Rolle:
Exception: Processing data from remote server outlook.office365.com failed with the following error message:
[AuthZRequestId=654e14bf-819d-49cc-ae3d-c993b1ff56b6][FailureCategory=AuthZ-CmdletAccessDeniedException] Die Rolle, die
der Anwendung 9cb958d8-3f03-4f5e-b71a-8ae25a289b78 zugewiesen ist, wird in diesem Szenario nicht unterstützt. Bitte
beachten Sie die Onlinedokumentation, um der Azure AD-Anwendung die richtigen Verzeichnisrollen für die EXO
Nur-App-Authentifizierung zuzuweisen. For more information, see the about_Remote_Troubleshooting Help topic.

Missbrauch

Auch wenn Sie eine Application entsprechend eingerichtet haben, so können Sie unter "Permission" auch andere Berechtigungen sehen. Ich habe daher einfach die gleichen Anmeldedaten mal genutzt, um eine Graph PowerShell zu starten. Das hat natürlich funktioniert.

PS C:\> Connect-MgGraph `
   -ClientID "9cb958d8-3f03-4f5e-b71a-8ae25a289b78" `
   -TenantId "msxfaqdev.onmicrosoft.com" `
   -CertificateThumbprint "815E2AD820B297DF4ED8D278CCE2D06EDAE35267"

PS C:\> Get-MgContext

ClientId              : 9cb958d8-3f03-4f5e-b71a-8ae25a289b78
TenantId              : 604d9047-44e5-443a-ad8f-98abe5748b0a
CertificateThumbprint : 815E2AD820B297DF4ED8D278CCE2D06EDAE35267
Scopes                :
AuthType              : AppOnly
AuthProviderType      : ClientCredentialProvider
CertificateName       :
Account               :
AppName               : MSXFAQDEV EXOPS2 CBA
ContextScope          : Process
Certificate           :
PSHostVersion         : 7.2.5
ClientTimeout         : 00:05:00

Obwohl ich keine Scope oder Rechte mit angefordert habe, konnte ich einige Commandlets nutzen z.B.: Get-MGUser, Get-MGGroup etc. ein Code, der Zugriff auf AppID, Tenantname und das Zertifikat hat, kann also durchaus einige "Default Rechte" ausüben. Achten Sie daher auf ihre Zugangsdaten.

Einschätzung

Auslöser für diese Seite war die Suche nach einem Weg, Exchange-Kontakte in einem anderen Tenant automatisiert zu pflegen. Per Graph ist es nicht möglich und mit MFA kann ich nicht mit einem Dienstkonto arbeiten. Die Nutzung der Exchange Online PowerShell V2 mittels Zertifikat und registrierter Application finde ich mehr als nur interessant, denn es ist eine effektive Lösung für die Aufgabenstellung "automatisches Provisioning". Meine Anforderungen einer Automatisierung von Exchange Online-Einstellungen kann ich damit problemlos umsetzen.

Weitere Links