Samstag, 2. Mai 2009

Move Mailbox Manager

Für einen großen Kunden mit deutlich über 70.000 Postfächern auf Exchange 2003 habe ich in den letzten Wochen ein sehr interessantes Teilprojekt gemacht. Davon möchte ich heute berichten.

Das gesamte Projekt geht darum Exchange 2003 abzulösen und Exchange 2007 einzuführen. Es gäbe sehr viel über die interessante neue Architektur, über Kosteneinsparung etc. zu erzählen, aber ich möchte mich mit dieser Erzählung auf ein “kleines” Teilprojekt konzentrieren, bei dem ich ein Tool entwickelt habe, um die ganzen Mailboxen zu moven. So viele Mailboxen zu moven bedeutet in erster Linie einen hohen logistischen Aufwand:

- verschiedene Zeitzonen rund um die Welt wollen bedacht werden
- die User sollen vor dem move automatisch informiert werden
- Resourcenpostfächer sollen als solche erkannt und als “Room” deklariert werden
- Postfächer von wichtigen Personen sollen erkannt und der Administrator gewarnt werden
- Es wurden im Vorfeld 4 neue Mailboxgrößen festgelegt, die anhand der aktuellen Mailboxgröße automatisch zugeordnet werden.

Außerdem sollte es drei Möglichkeiten geben, wie User, die gemoved werden sollen, definiert werden können:

- Import von CSV File
- Import per Distribution List (alle Mitglieder)
- Import von Exchange Datenbank(en)

Soweit die gröbsten Anforderungen. Ich entschied mich dazu mit Visual Studio eine GUI zu bauen, die Powershell Scripte für die Moves erzeugt. Die Powershell Skripte wollte ich dann per Windows Taskscheduler steuern. Gesagt getan:

image
(Ausschnitt)

Im oberen, linken Teil der GUI hat der Admin also die Möglichkeit über CSV, Distribution List oder von einer Exchange Datenbank Mailboxen in das Grid, ganz unten zu importieren. Während des Imports können verschiedene Filter applied werden, die oben rechts zu sehen sind. VIP Mailboxen oder Ressourcen können z.B. ignoriert werden und die Anzahl der Ergebnisse im Grid festgelegt werden.

Als nächstes gibt man in der “Finetune Move” Area an, aus welcher Region die Mailboxen kommen. Aus dem Ini File wird dann ausgelesen, zu welchen  Zeiten Moves für diese Region erlaubt sind. Momentan unterstützt das Tool nur Moves von Mailboxen, die alle aus der selben Region stammen. Bald soll es auch über ein AD Property dynamisch auslesen können, woher die User stammen und die Moves dann entsprechend planen.

Prinzipiell hat man sich dazu entschieden alle Mailboxen über alle möglichen Target Datenbanken (Exchange 2007) zu verteilen. Das Tool holt  sich also alle Exchange 2007 Server und alle darin enthaltenen Datenbanken schreibt sie abwechselnd in die TargetDB Spalte hinter den Usern im Grid. Die Inhalte des Grids werden dann in die entsprechenden Scripte gepackt und gescheduled. In den Optional Settings kann man dieses Verhalten aber auch umgehen, indem man sagt “Do not spread mailboxes accross databases”. In diesem Fall gibt man dann eine definierte target Datenank an.

Außerdem kann man einen LDAP Pfad angeben, in den die AD Objekte zusätzlich zum Mailboxmove verschoben werden.

Kommentare zur verwendeten Technik

Wie meistens, funktioniert natürlich nicht alles so reibungslos, wie man sich das gedacht hat. Die Mailboxen sollten natürlich multithreaded gemoved werden. D.h. man wollte nicht warten bis eine Mailbox gemoved ist, bevor der 2te Move startet. Dafür war die Anzahl der zu movenden Mailboxen einfach zu hoch und die dafür zur Verfügung gestellte Zeit zu knapp. Die einzige Möglichkeit Mailboxen per move-mailbox cmdlet multithreaded zu moven ist, indem man move-mailbox aus einem array von Mailboxen füttert und dann den “threads” parameter angibt – also ungefähr so:

$allUsers = import-csv c:\myusers.csv
$allUsers | move-mailbox –targetDatabase tdb –threads 10

Das funktioniert auch wunderbar. Nur: der –targetDatabase Parameter kann auf diese Weise nicht dynamisch angegeben werden. Vielmehr ist er für das gesamte Array gleich. Das heißt ich hatte entweder das Problem, dass die User nicht gleichmäßig auf alle (sehr vielen) Datenbanken verteilt wurden, oder das die Verteilung nicht multithreaded ablaufen konnte. Nun, ich klagte mein Leid dem Entwicklungschef meines Vertrauens. Und der hatte die rettende Idee: Wir erzeugen für jeden neuen Mailbox Move eine neue Powershell-Instanz die von der Mutterinstanz überwacht und nach Ablauf der max. erlaubten Duration abgeschossen wird. Je nachdem, wie viel Threads in der GUI angegeben werden, werden mehr oder weniger parallele Powershells aufgerissen.

Nach ein paar Schwierigkeiten mit Unicode hat das alles super geklappt.

Die Tasks im Windows Taskscheduler (Windows 2008) erzeuge ich übrigens über das eingebaute Schtasks.exe:

image

Ein Exchange Powershell Script aufzurufen ist schwerer als man das im ersten Moment erwarten würde. Z.B. ist das auch per Shortcut nicht ganz so einfach, wie ich schon einmal beschrieben habe. Wie man in dem oben abgedruckten Snippet erkennen kann, rufe ich auch nicht die Exchange Powershell sondern die “08/15” Powershell auf. Aber wie kann die normale Powershell ein Exchange Script ausführen? So:

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin 

Wenn man diese Zeile in sein PS Script integriert werden die Exchange Erweiterungen automatisch geladen.

Wie man generell von C# aus mit der Exchange Powershell arbeitet wird hier gut beschrieben:

http://msdn.microsoft.com/en-us/library/bb332449.aspx

Eine Weile hatte ich mich gefragt, wie man Filter in cmdlets die man von C# aus aufruft applien kann ( | where{$_.gedöns –eq ‘gedöns’} ). Nun, ich glaube das geht nicht, aber man kann ganz einfach per IF Schleife filtern:

image

An der Stelle, an der man sich also durch die results des Powershell Commands hangelt, fragt man per if das zu filternde Property ab und agiert entsprechend.

So, das sollte genügen um einen Überblick und ein paar Tipps für ähnliche Vorhaben zu geben. Wen Details interessieren kann mich gerne ansprechen.