Exchange Online Adressbuch Export

Wenn alle Postfächer und andere Empfänger in Exchange Online sind, dann ist das lokale Active Directory zwar oft noch vorhanden aber hinsichtlich der Mailadressen nicht mehr vollständig. Teams, Office Groups und andere Empfänger erscheinen On-Premises nicht mehr. Wen Sie einen 3rd Party Mailfilter wie z.B. NoSpamProxy einsetzen, dann kommt schnell der Wunsch auf die aktuelle GAL von Exchange Online auszulesen. Die Liste der Mailadressen und Empfänger kann natürlich auch noch für andere Zwecke hilfreich sein. Diese Seite beschreibt, wie ich einen möglichst minimal beschränken Serviceaccount in Office 365 einrichte, damit z.B. ein 3rd Party Programm solche Daten abrufen kann.

Rechte und Lizenzen

Natürlich ist auch eine Liste aller Empfänger durchaus "schützenswert" aber diese Information kann auch jeder "Domainbenutzer" heute quasi per LDAP aus dem lokalen Active Directory auslesen und ich bin sicher, dass nur wenige Firmen so einen Export überhaupt bemerken würden. Mit Bordmitteln ist dies nämlich nicht so einfach möglich.

Als deutlich schwerwiegender würde ich aber eine Konfiguration einschätzen, bei der ein Dienstkonto mehr Berechtigungen hat, als für die Funktion erforderlich sind. Die Verwaltung und Nutzung von Exchange per PowerShell ist seit Exchange 2010 über RBAC - Role Based Access Control zusteuern. Der Benutzer selbst hat also keine direkten Berechtigungen mehr sondern sendet seine Wünsche an den Exchange Server, der dann abhängig von der Mitgliedschaft des Anwenders in verschiedenen Berechtigungsgruppen die Befehle als "Computer" ausführt.

Das gleiche Prinzip gibt es auch in Exchange Online, wenn gleich die Berechtigungen etwas abgeschwächt sind.

Ich habe also erst mal mit der Exchange Online PowerShell als Administrator geschaut, in welcher Rolle "Get-Recipient" enthalten ist.

PS C:\> Get-ManagementRole -Cmdlet get-recipient
 
Name                                   RoleType
----                                   --------
Journaling                             Journaling
Mail Recipients                        MailRecipients
Transport Rules                        TransportRules
User Options                           UserOptions
Legal Hold                             LegalHold
Retention Management                   RetentionManagement
Security Group Creation and Membership SecurityGroupCreationAndMembership
View-Only Recipients                   ViewOnlyRecipients
Mail Recipient Creation                MailRecipientCreation
Compliance Admin                       ComplianceAdmin
Security Reader                        SecurityReader
Reset Password                         ResetPassword
Mailbox Search                         MailboxSearch
Data Loss Prevention                   DataLossPrevention
Remote and Accepted Domains            RemoteAndAcceptedDomains
UM Mailboxes                           UMMailboxes
Role Management                        RoleManagement
MyDistributionGroups                   MyDistributionGroups
Move Mailboxes                         MoveMailboxes
Distribution Groups                    DistributionGroups
Unified Messaging                      UnifiedMessaging
Message Tracking                       MessageTracking
MyBaseOptions                          MyBaseOptions
Security Admin                         SecurityAdmin
MyDistributionGroupMembership          MyDistributionGroupMembership

Wer eine Übersicht aller Commandlets zu den Rollen haben möchte, kann folgendes Code-Schnipsel nutzen:

"ManagementRole,Commandlet" | out-file .\exoroleentry.csv -Append
foreach ($mr in Get-ManagementRole){
   foreach ($rg in $mr.roleentries) {
      "$($mr.name),$($rg)" | out-file .\exoroleentry.csv -Append
   }
}

Mich interessieren hier die "Admin Rollen", da ich dem Dienstkonto eigentlich keine Exchange Lizenz zuweisen will. Eine Rollengruppe kombiniert Benutzer (oder weitere Gruppen) mit entsprechenden Rollen. Eine Rolle beschreibt quasi die PowerShell-Befehle und deren Parameter. Exchange Online kennt einige vordefinierte "Role Groups", die mit einer Ausnahme aber steuern, welche Einstellungen der Administrator verwalten darf. Ich möchte aber nur lesen und so kommt erst einmal nur die Rollengruppe "View-Only Organization Management" in Betracht, da Sie nur "lesen" darf.


Quelle: https://docs.microsoft.com/de-de/exchange/permissions-exo/permissions-exo

Aber nur um die Liste der Empfänger zu lesen muss das Dienstkonto doch nicht gleich alle Konfigurationen auslesen dürfen. Daher habe ich mir die Role Group genauer angeschaut. Sie sehen gut, dass es hier zwei Rollen gibt.

Damit habe ich doch schon fast meine Lösung. Ich muss einfach eine neue Administrative Rolle anlegen, die nur die Rolle "View-Only Recipients" mit dem Dienstkonto verbindet. Das geht sogar komplett ohne PowerShell per Browser im "Exchange Admin Center" von Exchange Online. Vorher sollte ich aber im Office 365 Admin-Center erst ein Dienstkonto anlegen. Da der Dienst nur Exchange Online verwendet, habe ich mich gegen einen lokalen AD-Benutzer entschieden, der dann mit ADSync erst ins AzureAD meines Tenant repliziert wird.

Hinweis: Der Name der RoleGroup muss eindeutig sein. Dabei stört schon der Name des Dienstkontos. Ich hatte zuerst einen Benutzer "NSPSYNC" angelegt und wollte die Role Group auch so nennen. das geht natürlich nicht-

Über das "+" im EAC lege ich einfach eine neue "Role Group" an und konfiguriere die Einstellungen.

Microsoft hat auch die "Rolle" auf einer eigenen Seite für Exchange 2013 dokumentiert:

Danach habe ich eine Exchange Online PowerShell mit diesem Benutzer instanziert und geschaut, welche Commandlets ich habe.

$Session = New-PSSession `
    -ConfigurationName Microsoft.Exchange `
    -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
    -Credential (get-credential) `
    -Authentication Basic `
    -AllowRedirection 
$result = import-PSSession $session
$result.ExportedCommands

Die Liste war schon relativ lang, d.h. "View-Only Recipients" beinhaltet viel mehr, als ich benötige. Es sind sogar einige "SET*"-Commandlets dabei. Allerdings hat kein Schreibversuch mit mehreren Commandlets funktioniert. Das ist aber keine Sicherheit und da werde ich noch mal nachfragen. Aber das Commandlet "Get-Recipient" und "Get-Distributiongroup" hat funktioniert.

Hier ist noch etwas Feinarbeit zu leisten.

Synchronisation

Mit einem einfachen Export ist es ja nicht getan. Das funktioniert beim ersten Mal vielleicht super aber in der Quelle gibt es laufenden Betrieb immer Änderungen, auf die ein Export gerüstet sein muss, z.B.

  • Rename (Heirat, Scheidung, Domänenumzug, Namenskonzept)
    Es muss sichergestellt sein, dass der Prozess das Objekt dennoch eindeutig wieder erkennt und die Änderung im Zielobjekt nachhält. Würde diese Erkennung nicht gelingen, dann würde im Ziel ein neuer zusätzliche Benutzer angelegt. Ein Skript muss also ein Feld finden, welches sich in der Quelle nicht ändert und auch im Ziel vorhanden ist.
  • Löschungen
    Wenn in der Quelle ein Objekt "gelöscht" wird, dann ist es beim nächsten Export einfach nicht mehr dabei. Bei einem Import im Ziel muss man also prüfen, welches der Objekte im Ziel auch in der Quelle noch vorhanden sind, um verwaiste Objekte zu entfernen. Bei größeren Umgebungen wird meist schon beim Export ein Vergleich mit dem vorherigen Export erfolgen, um danach nur Deltas weiter zu verbarbeiten. Achten Sie dabei auf jeden Fall auf Fehler beim Export. Es wäre nicht das erste mal, dass ein abgebrochener Export mit eine Teilmenge der Daten als Import weiter verwendet wird und irrtümlich Objekte im Ziel gelöscht werden.
  • Delta oder Full
    Kleine Umgebungen können sicher jedes Mal die komplette Liste exportieren und importieren. Größere Umgebungen werden versuchen nur die Änderungen zu ermitteln. Bei Get-Recipient können Sie auf "WhenChanged" filtern aber damit finden Sie dann keine "Deleted"-Objekte mehr, die verschwunden sind. Insofern habe ich über Get-Recipient noch keinen Weg gefunden die gelöschten Objekte mit einem Delta-Export zu erkennen.

Der wichtigste Aspekt ist aber das "Matching" von Objekten im Ziel, wenn diese in der Quelle verändert wurden. Ich denke, ich habe dazu zwei mögliche Felder in der Quelle erkannt, die zum einen eindeutig sind und sich vermutlich nicht ändern werden. Sie haben der Form einer GUID

  • ExchangeObjectId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    Vermutlich durch Exchange Online generiert und unveränderlich. Wäre zu prüfen, ob nach einem "Delete/Undelete" der Wert erhalten bleibt
  • ExchangeGuid : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    Entspricht dem Feld "GUID" bei GET-USER aus dem AzureAD

Der UPN oder die Mailadresse sind ein schlechter Wert und auch beim DistinguishedName bin ich nicht sicher, ob er sich nicht bei einem Rename des Objekts ändern. Da das Commandlet "Get-User" nicht die Mailadressen liefert, sind die dort vorhanden Felder wie "ExternalDirectoryObjectId", "SID" oder "LegacyExchangeDN" nicht nutzbar.

Import in z.B. NoSpamProxy

Da ich natürlich nicht weiß, in welches Zielsystem Sie die Informationen ihrer Exchange Online Empfänger importieren wollen, kann ich ihnen dazu auch keine Beschreibung liefern. Diese Seite ist aber auch entstanden, weil wir für NoSpamProxy eine gültige Empfängerliste benötigen. Ein wichtiger Aspekt beim Filtern von Mails ist die Ablehnung on ungültigen Adressen. Ansonsten müssten wir strenggenommen für eine eingehend angenommen Mail, deren Empfänger nicht gültig ist, selbst eine Unzustellbarkeitsmeldung senden. Dann ist es besser eine Mail direkt beim Empfang schon mit einer Fehlermeldung abzulehnen und diese Arbeit damit dem einliefernden Mailserver aufzubürden.

Hier daher ein Ausschnitt zum automatischen Export aller Exchange Online-Empfänger in eine CSV-Datei und eine Liste der Mailadressen in eine Text-Datei.

$password = (convertto-securestring -string "geheim" -asplaintext -force)
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "dienstkonto@tenant.onmicrosoft.com",$password

$Session = New-PSSession `
    -ConfigurationName Microsoft.Exchange `
    -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
    -Credential $cred `
    -Authentication Basic `
    -AllowRedirection 
$result = import-PSSession $session

#Export der Empfaenger mit Dsplayname und Mailadressen
Get-Recipient -ResultSize unlimited `
| select displayname,emailaddresses `
| export-csv "recipients.csv" -force

# Export eine Liste aller SMTP-Adressen
Get-Recipient -ResultSize unlimited `
| foreach{ `
    foreach ($address in $_.emailaddresses){$address.tostring()}`
  } `
| Out-File .\emailaddresses.csv -Force

# und hier kommt dann ihr Code

Was sie dann mit den Dateien machen, bleibt ihnen überlassen.

Und was ist mit Graph?

Die Exchange PowerShell ist natürlich nur ein Weg die Daten zu erhalten. Die Graph API ist eine sehr viel leistungsfähigere Schnittelle und erlaubt auch den Zugriff auf "Personen" und "Gruppen". Auf der Seite Get-O365Usage habe ich ein einfaches Beispiel zur Nutzung von Graph veröffentlicht. Ein Anbieter einer Software oder Sie selbst können natürlich eine vergleichbar Applikation schreiben, die sich am Tenant als Applikation authentifiziert und die Informationen vom Typ "Benutzer" und Gruppen" abruft

Beide Objekte liefern ein Feld "ProxyAddresses", welches alle Mailadressen den Objekts enthält. Allerdings ist das Feld nicht im Standard Antwortsatz enthalten. Sie müssen beim Request schon mitgeben, dass Sie auch dieses Feld zusätzliche erhalten wollen.

Der Prozess eine Applikation in ihrem Tenant zu berechtigen, hängt etwas von der App selbst ab. Sie können über die GUI eine eigene App anlegen und die so erhaltenen ClientID und Tokens in den Code hinterlegen. Wenn die App durch eine Firma bereit gestellt wird, dann könnten Sie die App einfach aus dem Store in Azure ihrem Tenant hinzufügen. Sie taucht dann unter "App registrations" auf:

Für jede App werden Berechtigungen gesteuert. Auch hier wird ihnen die Software in der Regel dabei helfen und selbst die Rechte anfragen, die sie als Administrator dann nur noch bestätigen müssen.

Für dieses Fenster wird ihnen der Entwickler z-B eine URL in der folgenden Form zu senden

https://login.microsoftonline.com/<tenantid>/adminconsent?client_id=<ClientID der App>&state=<stateu>&redirect_uri=<Redirect URL>

Sie können als Administrator auch jederzeit die Berechtigungen einsehen und ändern.:

Nach diesen Vorarbeiten können Sie in NoSpamProxy dann auch den Benutzerimport über Graph aktivieren:

Hinweis: Dies ist ein Screenshot aus einer Beta-Version, die noch ausgiebigen Tests unterworfen wird. Ob die Funktion so kommt, hängt von den Ergebnissen ab
Sie können bis zur allgemeinen Verfügbarkeit natürlich über den oben beschriebenen Weg per PowerShell einfach eine TXT-Datei um Import generieren.

Damit vereinfacht sich der Betrieb ihrer NoSpamProxy-Instanz bei einem Cloud-Anbieter oder Hoster in Verbindung mit Exchange Online, da Sie in ihrem lokalen Active Directory keine Komponenten zum Export und Upload der Benutzer und Mailadressen zu NoSpamProxy mehr installieren müssen.

Welcher Weg ist der Richtige?

Ehe Sie sich für einen der beiden Wege entscheiden, sollten Sie prüfen, ob sie überhaupt einen der beiden Wege gehen müssen. NoSpamProxy z.B. hat eine "Intranet-Rolle", welche per LDAP die benötigten Daten alleine ausliest und an die Gateways sendet. Der Weg per Exchange Online PowerShell oder Graph ist eigentlich nur für folgende Installationsvarianten interessant:

  • Hoster von NoSpamProxy ohne Intranet-Rolle
    Es gibt Firmen, die NoSpamProxy als "Hosting" für mehrere Kunden betreiben und der Kunde die Installation einer Intranet-Rolle im Kunden-Active Directory nicht erlaubt,
  • NoSpamProxy Betreiber, die Exchange Online Only betreiben
    Das sind aber dann eher kleine Firmen, denn sobald Sie ein lokales Active Directory haben und mit ADSync die Benutzer abgleichen, benötigen Sie (noch, Stand 2019 ) einen lokalen Exchange Server zum Provisioning. Siehe auch ADSync mit Exchange Online. Und dann können Sie wieder die Standard Intranet-Rolle installieren.

Aber es muss ja gar nicht um NoSpamProxy gehen. Wenn ein Service, der ihre Stammdaten oder Mailadressen braucht, einen Graph-Zugang erlaubt, dann würde ich diesen Weg einer Exchange PowerShell vorziehen. Es ist einfach der modernere und "Cloud-freundliche" Weg. Die Exchange PowerShell benötigt immer ein Dienstkonto mit Zugangsdaten zur Anmeldung. Eine App identifiziert sich durch eine GUID und ein Client Secret oder ein Zertifikat. Insofern ist Graph die Zukunft aber wir leben in der Gegenwart und für einige Zeit wird der bekannte Weg über die Exchange Online Powershell für die meisten Administratoren noch leichter umzusetzen sein.

Weitere Links