CSV2EXO

Auf der Seite CSV2EX habe ich beschrieben, wie ich eine beliebige CSV-Datei als Eingabe nutzen kann, um damit Kontakte in einer anderen Exchange Organisation anzulegen. Diese Kontakte landen auch problemlos über das Microsoft 365 Identity Management, sprich ADSync, auch in Exchange Online. Was machen aber Firmen, bei denen es kein Exchange Hybrid mit ADSync gibt oder die Replikation per ADSync nicht genutzt werden kann, weil sie einen Forest in zwei Tenants replizieren (Siehe ADSync GALSync)? Auch Cross-tenant sync könnte eine Lösung sein, wenn Sie statt Kontakte lieber Gast-Konten nutzen

Einsatzbereich

Zuerst kommt natürlich die Frage auf, ob mich das Thema überhaupt betrifft. Die essentielle Frage dabei ist: "Möchten Sie im Exchange Adressbuch für alle Mitarbeiter auch Personen und Einträge sehen, deren Postfach nicht in ihrer Exchange Organisation ist. Dann geht das in der Regel über die Anlage entsprechender "MailContacts". Wenn dies  der Wunsch ist, dann müssen Sie noch unterscheiden, ob Sie "Exchange Online Only" oder Exchange im Hybrid Mode nutzen. Im Hybrid-Mode sollten Sie die Kontakte im lokalen Exchange Forest anlegen und ADSync sorgt dafür, dass diese Kontakte dann auch in Exchange Online erscheinen.

Nur wenn Sie eine reine Exchange Online-Umgebung betreiben oder ein Verzeichnisabgleich durch ADSync aus dem lokalen Active Directory nicht möglich ist, dann wird der Inhalt dieser Seite interessant.

Dann kann CSV2EXO eine Option sein, die Kontakte direkt in Exchange Online anhand einer CSV-Datei automatisiert zu verwalten.

AzureAD/EXO-Kontakte

Ehe ich an die Kodierung per PowerShell gehe, teste ich erst einmal die verschiedenen Funktionen von "New-MailContact". In einem "normalen Tenant" kann ich den Kontakt per Exchange Online PowerShell anlegen:

New-MailContact `
   -ExternalEmailAddress user@uclabor.de `
   -Name "user@uclabor.de"

Name               Alias                 RecipientType
----               -----                 -------------
User@uclabor.de    User?uclabor.de       MailContact

Die vollständigen Eigenschaften erspare ich ihnen und beschränkte mich auf die wesentlichen Felder:

Get-MailContact user@uclabor.de `
   | fl Name,ExternalEmailAddress,RecipientType*,EmailAddresses,`
        alias,IsDirSynced,primarysmtpaddress,HiddenFromAddressListsEnabled

Name                          : user@uclabor.de
ExternalEmailAddress          : SMTP:user@uclabor.de
RecipientType                 : MailContact
RecipientTypeDetails          : MailContact
EmailAddresses                : {SMTP:user@uclabor.de}
Alias                         : user?uclabor.de
IsDirSynced                   : False
PrimarySmtpAddress            : user@uclabor.de
HiddenFromAddressListsEnabled : False

Den Kontakt finden Sie natürlich auch in der AzureAD-PowerShell.

Get-AzureADContact  `
   -Filter "mail eq 'user@uclabor.de'" `
| fl

ObjectType                 : Contact
DirSyncEnabled             :
DisplayName                : user@uclabor.de
Mail                       : user@uclabor.de
MailNickName               : user?uclabor.de
ProvisioningErrors         : {}
ProxyAddresses             : {SMTP:user@uclabor.de}
SipProxyAddress            :

Die Kontakte finden Sie auch im Exchange Admin-Portal und Microsoft 365 Admin Portal aber NICHT im Azure Admin Portal unter https://portal.azure.com

Im AzureAD Portal gibt es aktuell einfach keine Anzeige für die Kontakte aber Sie sind vorhanden, wie man auch im Audit-Log sehen kann:

Es gibt in der Azure PowerShell auch interessanterweise kein "New-AzureADContact" oder "Set-AzureADContact" und auch über die Graph API kann ich OrgContacts nur auflisten und lesen aber nicht anlegen und auch nicht ändern.

Kontakte Auflisten/Anzeigen Anlegen Ändern Löschen

Exchange Online PowerShell

Get-Mailcontact
Get-Contact

New-Mailcontact

Set-Mailcontact
Set-Contact

Remove-Mailcontact

AzureAD PowerShell

Get-AzureADContact

NA

NA

Remove-AzureADContact

Exchange Online Admin Center

Ja

Ja

Ja

Ja

Microsoft Admin Portal

Ja

Ja

Stammdaten.

Ja

Azure AD Portal

nicht sichtbar

nicht sichtbar

nicht sichtbar

nicht sichtbar

Microsoft Graph

List orgContacts
Get orgContact

Nein

Nein

Nein

Interessanterweise kann ich Kontakte auch im Microsoft 365 Admin Center unter https://admin.microsoft.com anlegen, wobei die Webseite im Hintergrund eine REST-Api unter https://admin.microsoft.com/admin/api/Contact nutzt. Das ist aber nicht Microsoft Graph-URL. Per Graph könnte ich also nur Kontakte lesen, was hier aber nicht weiter hilft.

OrgContact.Read.All

EXO Mailuser

Wie in Exchange On-Premises kann es auch in der Cloud Benutzer mit einer Anmeldung aber ohne Exchange Postfach geben, die aber dennoch im Adressbuch erscheinen sollen. Sowohl im Exchange Admin Center als auch per Exchange Online PowerShell können Sie solche "Mailuser" problemlos anlegen, editieren und löschen:

Per PowerShell sind das dann die Befehle

Der Befehl "Enable-Mailuser" ist hingegen nicht in Exchange Online verfügbar.


Quelle: https://learn.microsoft.com/en-us/powershell/module/exchange/enable-mailuser?view=exchange-ps

Ich kann daher z.B.: keinen CloudOnly-User nachträglich aktivieren. Im AzurePortal sehe ich auch keinen Unterschied, warum der "Clouduser2" in einem Lab nicht in Exchange erscheint.

Die MailKontakte sind im Azure-Portal gänzlich unsichtbar. Ich kann sie aber per Azure PowerShell abfragen

Kontakte per ADSync

In Exchange Online gibt es neben der Möglichkeit, direkt entsprechende MailContacts/MailUser anzulegen auch den klassischen Weg bei einer Hybrid-Bereitstellung. Die Mailuser/Mailcontacts werden im lokalen AD angelegt und ADSync repliziert diese ins AzureAD und Exchange Online. Allerdings müssen Sie hier ein paar Besonderheiten beachten:

  • ADSync repliziert Mailcontact und MailUser
    Die Objekte sind in der Cloud "Readonly", da sie durch das lokale AD verwaltet werden
  • Kontakt in der Cloud sind im Azure Portal (portal.azure.com) nicht sichtbar
    Sie finden diese Objekte im Exchange Portal (https://admin.exchange.microsoft.com), im Microsoft Admin Portal (https://admin.microsoft.com) und natürlich in der Powershell
  • Es gibt in Exchange Online ein New-Mailuser aber kein Enable-Mailuser
    Ich kann per New-Mailuser einen CloudOnly-Benutzer anlegen, der in Exchange als externer Empfänger erscheint. Ich kann aber keinen bestehenden User ohne Exchange Mailbox zu einem MailUser konvertieren.

Die Umsetzung für ADSync ist eigentlich immer zwingend, wenn Sie Exchange Hybrid richtig nutzen wollen. Der Einsatz von CDV2EXO zielt auf Umgebungen, in denen es entweder kein lokales AD mit ADSync gibt oder sie zwei Tenants von einem Active Directory aus bedienen und daher hier nicht zugleich ein Postfach und einen Kontakt mit den gleichen Mailadressen anlegen können.

Konflikte mit Kontakten

Bei Anlegen von neuen Kontakten kann es durchaus zu Konflikten kommen. Folgende Probleme habe ich schon gesehen:

Konflikt Fehlermeldung

Konflikt mit bestehendem Gastkonto

Gerade bei der Zusammenarbeit zwischen Tenants kann es durchaus passieren, dass es schon Gastkonten gibt.

New-MailContact `
   -ExternalEmailAddress ADUserA4@dom2016.msxfaq.de `
   - Name ADUserA4@dom2016.msxfaq.de

New-MailContact: Die Proxyadresse "SMTP:ADUserA4@dom2016.msxfaq.de" wird bereits 
als Proxyadresse `
oder vom LegacyExchangeDN von "ADUserA4_dom2016.msxfaq.de#EXT#" verwendet. `
Wählen Sie eine andere Proxyadresse aus.

Konflikt mit bestehendem Kontakt

Ich kann natürlich nicht ausschließen, dass schon ein anderer Prozess einen Kontakt angelegt hat. Die Meldung ist mit dem Gast vergleichbar

New-MailContact `
   -ExternalEmailAddress user1@msxfaq.de `
   -Name "Frank@carius.de CloudOnly"
New-MailContact: Die Proxyadresse "SMTP:user1@msxfaq.de" wird bereits als Proxyadresse oder `
vom LegacyExchangeDN von  "User1 (msxfaq.de)" verwendet. 
Wählen Sie eine andere Proxyadresse aus.

New-Mailcontact ohne Exchange Lizenz

Das ist natürlich ein Sonderfall. Ich habe einen Tenant, in dem gar keine Exchange Lizenz mehr vorhanden ist. Hier versagt New-Mailcontact komplett.

New-MailContact `
   -ExternalEmailAddress user2@msxfaq.de `
   -Name "user2@msxfaq.de CloudOnly"
New-MailContact: Fehler bei Überprüfung im Agent 'Substrate Only Agent': 
'Die Organisation "NAMPR20A001.PROD.OUTLOOK.COM/Microsoft Exchange Hosted 
Organizations/fcarius.onmicrosoft.com" ist nicht für die 
Exchange-E-Mail-Funktion lizenziert. 
Die Cmdlet-Verwendung ist eingeschränkt.'

Gast nach Kontakt

Wenn Sie schon einen Kontakt haben, behindert anscheinend nicht Einladung eines Gasts mit der gleichen Adresse.

Ich bin gespannt, welche weiteren Probleme ich im produktiven Einsatz noch erwische.

*-mailcontact vs *-contact

Wenn Sie genau mitgelesen haben, gibt es mehrere Commandlets die Kontakte anlegen, bearbeiten und löschen können.

PS C:\> get-command *-*contact -Module tmpEXO_2nl2rbr3.rki

CommandType     Name                                               Version
-----------     ----                                               -------
Function        Get-Contact                                        0.0.1 
Function        Get-MailContact                                    0.0.1 
Function        New-MailContact                                    0.0.1 
Function        Remove-MailContact                                 0.0.1 
Function        Set-Contact                                        0.0.1 
Function        Set-MailContact                                    0.0.1 

Zudem gibt es in der PSGallery noch ein "ExchangeContacts"-Modul, welches per EWS aber Kontakt im Postfach verwaltet. Das hilft mit mit CSV2EXO nicht weiter und maximal "Export-EXCGALContact" könnte interessant sein, wenn der Export durch einen Benutzer, quasi unbemerkt vom IT-Betrieb, erfolgen soll.

Bei den Commandlets der Exchange PowerShell ist interessant, dass die Properties sehr unterschiedlich sind. Wenn ich "Delete-MailContact", Get-Contact und Get-Mailcontact einmal weg lasse, dann gibt es folgende schreibende Commandlets in der Exchange Online PowerShell.

So gibt es nur ein "New-MailContact" aber kein "New-Contact".

Feld New-Mailcontact Set-Mailcontact Set-Contact

Name

Ja

Ja

Ja

ExternalEmailAddress

Ja

Ja

Nein

Alias

Ja

Ja

Nein

Displayname

Ja

Ja

Ja

Firstname

Ja

Nein

Ja

Initials

Ja

Nein

Ja

Lastname

Ja

Nein

Ja

CustomAttribute1 bis
CustomAttribute15

Nein

Ja

Nein

ExtensionCustomAttribute1 bis
ExtensionCustomAttribute5

Nein

Ja

Nein

Emailaddresses

Nein

Ja

Nein

HiddenFromAddressListsEnabled

Nein

Ja

Nein

MailTip

Nein

Ja

Nein

MessageBodyFormat

Ja

Ja

Nein

SecondaryAddress

Nein

Ja

Nein

WindowsEmailAddress

Nein

Ja

Ja

City
Company
CountryorRegion
Department
Fax
HomePhone
Manager
MobilePhone
Notes
Office
OtherFax
OtherHomePhone
OtherTelephone
Pager
Phone
PostalCode
PostOfficeBox
SimpleDisplayName
StateOrProvince
StreetAddress
TelephoneAssistant
Title
webpage

Nein

Nein

Ja

Bei den Felder ist gut zu sehen, dass ich bei New-MailContact z.B. Firstname, Lastname, Phone mitgeben knan, die ich über ein Set-MailContact noch nicht setzen kann. Für einige Felder muss ich daher "Set-Contact" nutzen. Nur gut, dass es keine Felder gibt, die nur mit "New-MailContact" erreichbar sind. Dennoch muss ich beim Update eines Objekts nun nach Commandlets trennen.

Das wirkt alles noch nicht wie aus einem Guss.

Bei der unten bereitgestellten Basisversion beschränke ich mich auf die Felder, die ich per Set-MailContact setzen kann.

CustomAttribute statt Ziel-OU

Mein Skript kann niemals eine komplette Identity-Lösung ersetzen. Es verwaltet einfach nur Kontakte per Exchange Powershell oder hier per Exchange Online PowerShell. Bei CSV2EX habe ich die Kontakte allerdings in eine eigens dafür bestimmte OU im Active Directory angelegt. Damit konnte ich zuverlässig dann auch die Objekte wieder finden und in der Quelle gelöschte Objekte auch im Ziel entfernen.

Im AzureAD gibt es keine OUs im klassischen Sinn und die Benutzerdatenbank im Tenant ist quasi eine NT4-Liste. Interessanterweise kennt aber die Exchange Online-PowerShell das Commandlet "Get-OrganizationalUnit" und listet sogar zwei OUs auf.

PS C:\> Get-OrganizationalUnit | ft distinguishedname

DistinguishedName
-----------------
OU=msxfaqlab.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=DEUP281A004,DC=PROD,DC=OUTLOOK,DC=COM
OU=Soft Deleted Objects,OU=msxfaqlab.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=DEUP281A004,DC=PROD,DC=O…

Auch bei New-MailContact gibt es den Parameter "-OrganizationalUnit", den die Exchange Online PowerShell auch anbietet aber keine Funktion hat.


https://docs.microsoft.com/en-us/powershell/module/exchange/new-mailcontact?view=exchange-ps

Ich habe aber keinen Weg gefunden, weitere OUs anzulegen. Daher muss ich mir entweder in einer eigenen Datenbank merken, welche "meine" Kontakte sind oder ich kennzeichne die Kontakte in der Cloud anhand eines Feldes, welches sonst niemand verwendet. Exchange bietet dazu ja die "CustomAttribute0-15" und die "ExtensionCustomAttribute1-5" an. Da es aber mehrere Instanzen von CSV2EX geben kann, die alle in den gleichen Tenant schreiben, sollte ich in den Wert nicht nur "CSV2EX" schreiben, sondern vielleicht gleich "CSV2EX:<quelle>".

Gruppen sind Kontakte

Die zweite Herausforderung bei einem Abgleich von Kontakten sind Verteiler. Natürlich kann ich aus der Quelle auch Gruppen erhalten und diese im Ziel anlegen und mit den Kontakten befüllen. So arbeitet z.B. ADSync, wenn er Identitäten aus einem AD in einen Tenant überträgt (Siehe auch ADSync GALSync). Allerdings hat dieser Ansatz auch Nachteile, z.B.

  • Mitgliederliste nicht ReadOnly
    Da die Mitgliedschaft der synchronisierten Gruppe von der Quelle abhängt aber Änderungen durch den Administrator im Ziel nicht ausgeschlossen sind, haben wir einen Konflikt bei der nächsten Replikation. Soll das Skript die manuellen Änderungen rückgängig machen oder belassen?
  • Message Bifurcation
    Zudem wäre dann die Zielumgebung des Verzeichnisimports zugleich der Absender der Mail und würde daher vor Ort schon die Mitgliedschaften auffächern. Inkonsistenzen Mitgliedschaften führen zu falschen Empfängerkreisen.
  • Partielle Daten
    Wenn ich Gruppen als Gruppe im Ziel anlegen, dann müssen auch alle Mitglieder der Quelle zwingend ebenfalls als Kontakte im Ziel angelegt werden, da diese ansonsten keine Mail bekommen. Ist das imer gewünscht?
  • Datenschutz
    Vielleicht möchte die Quelle gar nicht, dass das Ziel genau die Mitgliedschaften der Gruppen kennt.

Daher habe ich mich auf "Kontakte" beschränkt und quasi jede primäre Mailadresse, ggfls. mit zusätzlichen Adressen, ist ein Kontakt. Das gilt für Postfächer in der Quelle, Räume, Shared Mailboxen aber auch öffentliche Ordner, Verteiler und weitere Kontakte. Letztlich erwarte ich von der Quelle eine entsprechend formatierte CSV-Datei, die dann durch das Skript im Ziel entsprechende Kontakte anlegt, aktualisiert und ggfls. wieder löscht.

Performance beim Anlegen

Natürlich wollte ich auch wissen, in welche Limits ich vielleicht laufen und habe daher mit folgendem Einzeiler einfach mal versucht 10.000 Kontakte anzulegen

1..10000 | %{new-mailcontact -ExternalEmailAddress ("contact"+$_+"@msxfaq.de") -Name "contact$($_)"}

Nach etwas 15 Minuten und 1271 Kontakte ist die Anlage aber mit folgendem wenig aussagekräftigen Fehler abgebrochen.

contact1270               contact1270                                               MailContact
contact1271               contact1271                                               MailContact
OperationStopped: Starting a command on the remote server failed with the following error message : 
  WinRM kann den Vorgang nicht abschließen. Überprüfen Sie, ob der angegebene Computername gültig, 
   der Computer über das Netzwerk erreichbar und eine Firewallausnahme für den WinRM-Dienst aktiviert 
   ist und den Zugriff von diesem Computer zulässt. Standardmäßig wird der Zugriff auf Remotecomputer 
   innerhalb desselben lokalen Subnetzes von der WinRM-Firewallausnahme für öffentliche Profile 
   eingeschränkt. For more information, see the about_Remote_Troubleshooting Help topic.
...

Creating a new Remote PowerShell session using Modern Authentication for implicit remoting of "New-MailContact" command ...
Error Acquiring Token:
System.Net.Http.HttpRequestException: Der angegebene Host ist unbekannt. (login.microsoftonline.com:443)
---> System.Net.Sockets.SocketException (11001): Der angegebene Host ist unbekannt.
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellatio

Ich bin eigentlich sicher, dass meine Internet-Verbindung fehlerfrei bestanden hat und auch die automatische Neuanforderung des Tokens selbst ist ja fehlgeschlagen.

Ich habe dann die Anlage vorgesetzt und da wurde nach ca. 3600 weiter fehlerfrei angelegten Kontakten nur die Verbindung neu problemlos aufgebaut.

contact4863               contact4863                                               MailContact
contact4864               contact4864                                               MailContact
Creating a new Remote PowerShell session using Modern Authentication for implicit remoting of "New-MailContact" command ...
contact4865               contact4865                                               MailContact
contact4866               contact4866                                               MailContact

Scheint so, als ob mein Fehler wohl nur temporärer Natur war. Aber ich bin sicher, dass es ein Throttling gibt und daher meine Prozesse damit umgehen müssen. Das Commandlet "get-throttlingpolicy/set-throttlingpolicy" gibt es aber nur On-Premises

Und auch ein "MicroDelay" habe ich noch nicht gesehen.

Der Artikel verweist auf die Möglichkeiten dies zu verhindern und benennt auch ein "RobustCloudCommand"-Module, in dessen Quelltext einige interessante Hinweise stehen

Aber diese Fehler habe ich mit der neuen Exchange Online PowerShell V2 bislang ja nicht gesehen. Also muss ich nur selbst eine Fehlerbehandlung mit einer Verzögerung einbauen oder wie ADSync beim nächsten Lauf wieder aufsetzen.

Allein mit dem Anlegen ist es aber nicht getan, denn ich möchte auch noch weitere Felder setzen, die ich beim "New-MailContact" nicht mit angeben kann. Neben Straße, Ort etc. ist das mindestens ein extensionAttribute, damit ich unter den vielen Kontakte die wieder finde, die ich verwalte. Eine OU-Struktur zur Separierung, wie bei CSV2EX gibt es im Exchange Online nicht. Das Update eines bestehenden Kontakts ist deutlich schneller (ca. 250-400ms) und ich muss auch keine Replikation abwarten, sondern kann direkt loslegen.

Kontakte Auslesen

Ich brauche gleich zweimal die Funktion, bestehende Kontakte auszulesen.

  • Lesen aus der Quelle
    Wenn die Quelle selbst ein Exchange Online Tenant ist, dann könnte ich mir die Daten dort selbst über Graph oder die Exchange Online Powershell holen. Über die PowerShell habe ich natürlich mehr Rechte, während es bei Graph die Berechtigung auf "OrgContact.Read.All" erforderlich wäre. Letztlich obliegt es aber eh dem Administrator im der Quelle, welche Daten er mir als CSV-Datei bereitstellt. Ansonsten gibt es ja immer noch die MGGraph PowerShell, mit der solche Dinge schnell umgesetzt werden können
  • Lesen im Ziel zur Delta-Erkennung
    Interessanter ist das Auslesen im Ziel, denn ich muss ja erkennen, welche Objekte es schon im Ziel von vorherigen Sync-Vorgängen gibt und vielleicht entfernt werden müssten. Ein "Get-MailContact" macht das aber recht einfach, da sowohl größere Datenmengen als auch Filter genutzt werden können.

Zumindest in meine Umfeld bin ich da noch nicht auf Probleme gestoßen.

$bestand = get-mailcontact -Filter customattribute5 -eq "test" -ResultSize unlimited

Das Auslesen von ca. 10.000 Kontakte dauerte bei mir 40 Sek, also ca. 250 Kontakte/Sekunde

CSV2EXO-Skript

Mit diesen Vorarbeiten kann ich nun an die Umsetzung meines vereinfachten DirSync gehen, der eine CSV-Datei mit entsprechenden Benutzern in einen Exchange Online-Tenant importiert.

Warnung: Dies ist eine "einfache" Lösung, die neue Objekte addiert, bestehende aktualisiert und überzählige Objekte löscht und das Feld "Mail" als primären Schlüssel nutzt. Das bedeutet aber auch, dass z.B. eine Änderung der Mailadresse aus Sicht des Programms dann ein "Löschen"/Neuanlegen" ist und der Kontakt damit aus Gruppenmitgliedschaften entfernt wird..

Ich gehe davon aus, dass ich als Eingabeformat wieder eine CSV-Datei habe, welche mindestens die erforderlichen Felder für einen Kontakt haben.

  • Name
  • Mail

Die Mailadresse ist für die weitere Betrachtung wichtig, da ich diese Feld als primären Schlüssel verwende, um nach Übereinstimmungen zu suchen. Allerdings sind die Möglichkeiten von "New-MailContact/Set-Mailcontact" leider beschränkt. So kann ich damit weder Telefonnummern noch Vorname, Nachname, Straße, Ort o.ä. füllen. Im lokalen Exchange ist dies per AD noch möglich aber im AzureAD gibt es meines Wissens keine Möglichkeit diese Informationen zu setzen.

Das Skript macht dann eigentlich nichts anderes als:

  1. CSV-Datei importieren
    Hierbei prüfe ich sicherheitshalber, dass die Mailadressen eindeutig sind und die Datei komplett ist. Der letzte Datensatz muss ein "#" enthalten. Nicht dass die Datei nur partiell übertragen wurde.
  2. Durch früheren Sync erstellte Kontakte aus dem Ziel einlesen und bereinigen
    Dabei werden die Kontakte mit dem bestehenden Objekten abgeglichen. Übereinstimmungen werden in der Lookup-Tabelle entfernt und nicht gefundene Einträge im Ziel gleich gelöscht. Übereinstimmungen werden aktualisiert.
  3. Kontakte anlegen und aktualisieren
    Was nun noch im Cache ist, muss im Ziel neu angelegt werden.

Es klingt einfach aber durch Fehlerbehandlung und Sonderfälle ist der Code schon etwas umfangreicher. Es erwartet die Übergabe als Parameter und natürlich müssen Sie vorher im Zieltenant natürlich das Skript als "App" einrichten.

Details zur Einrichtung einer App habe ich auch EXO PowerShell Automation beschrieben.

csv2exo-basic.ps1.txt

Sie müssen dann die entsprechenden Daten entweder in der "Param"-Sektion anpassen oder beim Aufruf mit übergeben.

  • CSVFile, Delimiter und Encoding
    Sie spezifizieren die Import-Datei und Formatierung
  • CertificateThumbPrint AppID, Organization
    Tragen Sie hier den AppID, den Thumbprint des Zertifikats und die Organisation ein, die bei der App-Registration (Siehe EXO PowerShell Automation) definiert wurde.
  • Keyproperty/keyvalue
    Jeder vom Skript angelegte Kontakt bekommt dieses Feld mit dem Wert gesetzt und beim späteren Lesen und Löschen werden nur diese Kontakte berücksichtigt.

Für den Import ist eine CSV-Datei erforderlich, die wenige Voraussetzungen erfüllen muss:

  • Erster Spalte "mail"
    Das ist der primäre Schlüssel für den Kontakt und muss daher vorhanden und eindeutig sein
  • weitere Spalte "name"
    Das Feld "Name" wird zum Displayname und sieht der Anwender. Es sollte entsprechend sinnvoll gefüllt sein
  • Weitere Felder
    Weitere Felder sind möglich und werden mit "Set-MailContact" als Parameter 1:1 übernommen und gesetzt.
  • Letzte Zeile "#"
    Damit stelle ich sicher, dass der Export "komplett" ist und nicht beim Export oder der Bereitstellung etwas verloren gegangen ist. Das ist ein Schutz, dass dann nicht vorhandene Kontakte entfernt werden.

Wie Sie die CSV-Datei erstellen, überlasse ich ihnen. Sie können Sie aus einer ERP-Software generieren, aus dem lokalen AD per "CSVDE" exportieren oder mit wenig Aufwand aus einem anderen Exchange Online Tenant per Get-MailContact oder Get-EXORecipient abziehen.

Durch den "Mittelweg" einer CSV-Datei können Sie Export, Transport, Transformation und Import durch verschiedene Personen mit unterschiedlichen Berechtigungen durchführen lassen und speziell den Import auch in Test-Tenants einfach verifizieren, ehe Sie an den Live-Tenant gehen.

CSV2EXO vNext

Das oben bereitgestellte Skript ist eine einfache Basisfunktion, die durchaus "verbessert" werden kann, z.B. mit Fehlerbehandlung, Logging/Protokollierung, Diagnose-Ausgaben, Laufzeit-Optimierungen, Konfliktbehandlung, ausgelagerte Konfiguration etc. ich arbeite an einem entsprechenden Skript, welches aber aktuell nur in Kundenprojekten zum Einsatz kommt und ich möchte ja auch sicher sein, dass der Einsatz möglichst ungefährlich ist.

Diese erweiterte Funktion ist noch nicht öffentlich. Wenn Sie Bedarf an einem "Cross Tenant Dirsync" haben, dann kommen Sie gerne auf mich zu
Kontakt bzw. Net at Work

Weitere Links