Report-MoveRequest
Wenn sie Postfächer mit einem "Remote-MoveRequest" in die Cloud verlagern, dann saugt sich Exchange Online die Daten aus dem lokalen Exchange Server. Dieses Skript erstellt einen Report über diesen Prozess.
Kleinere Firmen mit vielleicht bis zu 1000 Postfächern lassen sich recht problemlos auf einen Schlag in die Cloud umziehen. Das macht den Betrieb einfacher, da es keine "Cross Ort"-Zugriffe für Free/Busy-Zeiten, Stellvertreter oder öffentliche Ordner gibt. Die meisten Outlook Installationen schwenken alleine und nur die Mobilclients schaffen es nur mit etwas Hilfe durch Intune oder eine andere MDD-Lösung. Wenn Sie aber größere Datenmengen migrieren, dann wollen Sie schon sehen, ob die Daten auch "fließen". Dazu gibt es mehrere Wege.
Get-MoveRequest
Sie verbinden sich per PowerShell mit der Exchange Online Instanz und führen einfach Get-MoveRequest aus. Wenn Sie eine Migration per Browser im ECP einrichten, dann erstellen sie einen Migrationsbatch, in den Sie ein oder mehrere Postfächer zu einer Gruppe zusammenfassen. Im Hintergrund ist dennoch jedes Postfach ein eigener Migrationsjobs, der so aufgelistet wird:
PS C:> Get-Moverequest User1 InProgress DEUP281DG008-db049 User2 Synced DEUP281DG060-db161 User3 Completed DEUP281DG050-db074 User4 WaitJob DEUP281DG052-db073 User5 WaitingForJobPickup DEUP281DG008-db029 User6 Queued DEUP281DG018-db019
Allerdings enthalten diese Ergebnisse keine Werte zur Postfachgröße sondern nur zum Benutzerobjekt und den Link zum Batch.
- Get-MoveRequest
https://docs.microsoft.com/de-de/powershell/module/exchange/get-moverequest?view=exchange-ps
Get-MoveRequestStatistics
Wenn ich mehr Daten ermitteln will, muss ich ich mit "Get-MoveRequestStatistics" arbeiten.
- Get-MoveRequestStatistics
https://docs.microsoft.com/de-de/powershell/module/exchange/get-moverequeststatistics?view=exchange-ps
Allerdings braucht der Befehl eine Identity, so dass die Aufrufe in der Regel mit einer Liste von Benutzern oder direkt dem "Get-MoveRequest" verbunden wird.
PS C:>$movestat = Get-MoveRequest | Get-MoveRequestStatistics PS C:>$movestat DisplayName StatusDetail TotalMailboxSize TotalArchiveSize PercentComplete ----------- ------------ ---------------- ---------------- --------------- User1 IncrementalSync 3.732 GB (4,006,675,714 bytes) 2.886 GB (3,098,322,773 bytes) 32 User2 IncrementalSync 18.09 GB (19,423,930,682 bytes) 16.5 GB (17,718,101,744 bytes) 82 User2 RetryBadItems 4.551 GB (4,886,650,597 bytes) 4.416 GB (4,741,831,330 bytes) 95 user3 Completed 20.04 GB (21,521,078,244 bytes) 19.13 GB (20,537,290,604 bytes) 100 User4 InitializingMove 757 MB (793,753,448 bytes) 619.4 MB (649,482,528 bytes) 10 User5 WorkItemPickup 21.45 GB (23,027,441,158 bytes) 17.97 GB (19,298,111,191 bytes) 00 User6 Synced 1.76 GB (1,889,575,334 bytes) 1.543 GB (1,657,314,442 bytes) 00
Die Liste ist nun schon aussagekräftiger und wir sehen hier insbesondere die Menge der zu migrierenden Daten und sogar die Ausgabe, wie viel Prozent schon geschafft sind. Mit diesen Daten kann ich doch schon einiges anfangen, da ich die gesamte Datenmenge aufaddieren und auch die bereits kopierten Datenmengen ermitteln kann. Die Größe des Postfachs kann ich in der Cloud nämlich noch nicht mit "Get-Mailboxstatistics" ermitteln, da es das Postfach ja noch nicht in der Cloud gibt. Ich müsste dann die Menge erst On-Premises einsammeln und matchen
Interessant sind aus "Get-MoveRequestStatistics" folgende Properties:
PS C:>$movestat[0] QueuedTimestamp : 22.02.2022 10:10:25 StartTimestamp : 22.02.2022 10:11:09 LastUpdateTimestamp : 25.02.2022 21:15:55 LastSuccessfulSyncTimestamp : 24.02.2022 21:15:38 InitialSeedingCompletedTimestamp : 23.02.2022 21:13:30 FinalSyncTimestamp : CompletionTimestamp : SuspendedTimestamp : OverallDuration : 00:00:40.8007850 TotalSuspendedDuration : 00:00:00 TotalFailedDuration : 00:00:00 TotalQueuedDuration : 00:00:00 TotalInProgressDuration : 00:00:40.8007850 TotalStalledDueToContentIndexingDuration : 00:00:00 TotalStalledDueToMdbReplicationDuration : 00:00:00 TotalStalledDueToMailboxLockedDuration : 00:00:00 TotalStalledDueToReadThrottle : 00:00:00 TotalStalledDueToWriteThrottle : 00:00:00 TotalStalledDueToReadCpu : 00:00:00 TotalStalledDueToWriteCpu : 00:00:00 TotalStalledDueToReadUnknown : 00:00:00 TotalStalledDueToWriteUnknown : 00:00:00 TotalTransientFailureDuration : 00:00:00 MRSServerName : FR0P281MB0882.DEUP281.PROD.OUTLOOK.COM TotalMailboxSize : 18.09 GB (19,423,930,682 bytes) TotalMailboxItemCount : 67924 TotalArchiveSize : 16.5 GB (17,718,101,744 bytes) TotalArchiveItemCount : 62507 TotalPrimarySize : 1.589 GB (1,705,828,938 bytes) TotalPrimaryItemCount : 5417 SyncedItemCount : 67853 BytesTransferred : 26.41 GB (28,353,304,327 bytes) BytesTransferredPerMinute : 10.56 MB (11,068,709 bytes) ItemsTransferred : 70000 ShardBytesTransferred : 3.248 MB (3,406,038 bytes) ShardItemsTransferred : 216 ArchiveBytesTransferred : 24.58 GB (26,393,320,477 bytes) ArchiveItemsTransferred : 62510 PercentComplete : 95
Deserialized.Microsoft.Exchange.Data.ByteQuantifiedSize
Allerdings gibt es da noch kleines Problem der Exchange Remote PowerShell. Die Zahlen sind keine Zahlen, sondern "Strings"
PS C:\> $movestat[1].TotalMailboxSize 18.09 GB (19,423,930,682 bytes) PS C:\> $movestat[1].TotalMailboxSize | gm TypeName: Deserialized.Microsoft.Exchange.Data.ByteQuantifiedSize Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString(), string ToString(st
On-Premises ist es völlig normal, dass die Properties vom Typ "Microsoft.Exchange.Data.ByteQuantifiedSize" sind und ich z.B. mit einem "ToGB()" einfach den Wert in Gigabyte konvertieren kann. Auch andre Werte sind möglich. Bei der Remote Powershell ist das aber leider nicht möglich, das die Werte "deserialisiert" werden:
- ByteQuantifiedSize structure
https://docs.microsoft.com/en-us/previous-versions/office/exchange-server-api/ff339273(v=exchg.150) - How objects are sent to and from remote
sessions
https://devblogs.microsoft.com/powershell/how-objects-are-sent-to-and-from-remote-sessions/
Microsoft "dampft" quasi einige Ergebnisse auf Basistypen ein und nur primitive Typen werden 1:1 übertragen und beim Empfang wieder "hydriert". Das klingt fast wie Milchpulver und Wasser und ist auch etwa so. Wenn ich aber mit den Zahlen rechnen will, muss ich Sie konvertieren. Ich bekomme also einen String in der folgenden Schreibweise.
18.09 GB (19,423,930,682 bytes)
Wenn ich sicher bin, dass ich eine lokale Exchange PowerShell mit den "Microsoft.Exchange.Data"-DLLs habe, dann könnte ich den Werte einfach wieder parsen lassen.
$size = [Microsoft.Exchange.Data.ByteQuantifiedSize]::Parse($movestat[1].TotalMailboxSize)
- ByteQuantifiedSize members
https://docs.microsoft.com/en-us/previous-versions/office/exchange-server-api/ff345425(v=exchg.150)
Darauf kann ich mich aber besser nicht verlassen und nehme den String besser mal auseinander.
#Zuerst einmal bei der ersten Klammer trennen und dann die Einheit aufteilen "18.09 GB (19,423,930,682 bytes)".split("(")[0].replace(" ","") 18.09GB
Wenn ich den Wert "18.09GB" einfach auf der Console eingebe, dann rechnet PowerShell den direkt in Bytes um
PS C:\> 18.09GB 19423989596,16
Alle Versuche das aber per "ToInt()", [math] o.ä auch mit einem String hinzubekommen, haben aber nicht gefruchtet. Am Ende konnte ich das aber unkonventionell mit einer Addition einer Zahl mit dem String erreichen:
PS C:\> $f="18.09GB" PS C:\> $f.gettype() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object PS C:\> $f.toint64() Für "toint64" und die folgende Argumenteanzahl kann keine Überladung gefunden werden: "0". PS C:\> 0+$f 19423989596,16
So ganz funktioniert das aber noch nicht, denn "0B" wird so nicht konvertiert. Da hat die PowerShell wohl einen Fall nicht abgedeckt. Damit hatte ich aber eine Basis für weitere Berechnungen.
- PowerShell Conversions
https://docs.microsoft.com/en-us/powershell/scripting/lang-spec/chapter-06?view=powershell-7.2 - Convert from any-to-any (Bytes, KB, MB,
GB, TB) using PowerShell
https://techibee.com/powershell/convert-from-any-to-any-bytes-kb-mb-gb-tb-using-powershell/2376 - PowershellPlayground
https://GitHub.com/FredericVets/PowershellPlayground
https://GitHub.com/FredericVets/PowershellPlayground/blob/master/Modules/MyTools/MyTools/Public/Convert-Size.ps1 - #PSTip Parsing Exchange size strings
https://powershellmagazine.com/2013/10/08/pstip-parsing-exchange-size-strings/
Status ermitteln
Mit den Vorarbeiten ist es nun natürlich einfach, z.B. die Gesamtmenge der zu migrierenden Postfächer zu ermiteln.
# Gesamtes Migrationsvolumen als Einzeiler ($movestat.TotalMailboxSize | %{0+ ($_).tostring().split("(")[0].replace(" ","")} | Measure-Object -Sum).sum/1GB # Oder aufgesplittet ($movestat.TotalMailboxSize ` | %{` 0+ ($_).tostring().split("(")[0].replace(" ","")} ` | Measure-Object -Sum` ).sum/1GB
Analog erhalte ich "BytesTransferred"
($movestat.BytesTransferred | %{0+ ($_).tostring().split("(")[0].replace(" ","")} | Measure-Object -Sum).sum/1GB
Wenn sie so eine Stichtagsmigration mit vielen Postfächern einige Tage "stehen" lassen, bis alle Postfächer an die 95% gekommen sind, dann repliziert Exchange ja zwischenzeitlich immer wieder die Änderungen. Die Summen aus "BytesTransferred" steigen dabei immer weiter an und sind damit regelmäßig höher als TotalMailboxSize.
Leider scheitert der Versuch aus dem Wert "BytesTransferredPerMinute" einen Schnitt zu ermitteln. Bei vielen auch abgeschlossenen Aufträgen steht hier ein "0B". Dafür könnte aber die Auswertung der Dauer aus dem Feld "TotalInProgressDuration" genutzt werden. Aber auch hier habe ich viele Jobs gefunden, bei denen dieser Wert auf "0" gesetzt war. Entsprechend muss der Code ein paar Abfragen tätigen, damit fehlende oder inkonsistente Daten keinen negativen Einfluss haben.
Wenn ein Eintrage einen Wert in "BytesTransferredPerMinute" hat, wird er genutzt. Ansonsten schaue ich mir an, ob in TotalInProgressDuration ein Wert ungleich 00:00:00 steht, um dann anhand des Status entweder die Postfachgröße oder doch die "BytesTransferred" zu nutzen.
# Report-MoveRequest # # Try to get some thoughput counters for Remote-MoveRequests [cmdletbinding()] param() set-psdebug -strict Write-Verbose "Collect MoveRequests" $moverequests = get-moverequest Write-Verbose "Total Requests found: $($moverequests.count)" Write-Verbose "Collect MoveRequests Statistics - May take some time" $movestat = $moverequests | get-moverequeststatistics Write-Verbose "Processing statistics" foreach ($mailbox in $movestat) { Write-Verbose "----- Processing $($mailbox.identity)" $result = [pscustomobject][ordered]@{ identity = $mailbox.identity TotalMailboxSizeMB = 0 TotalArchiveSizeMB = 0 duration = $mailbox.TotalInProgressDuration MBperMinute = -1 PercentComplete = $mailbox.PercentComplete TotalMBToMigrate=-1 TotalMBDone=-1 } Write-verbose "TotalMailboxSize: $($mailbox.TotalMailboxSize.tostring())" if ($mailbox.TotalMailboxSize.tostring() -ne "0 B (0 bytes)") { $result.TotalMailboxSizeMB = [int]((0 + ($mailbox.TotalMailboxSize.tostring().split("(")[0].replace(" ","")))/1MB) } Write-verbose "TotalArchiveSize: $($mailbox.TotalArchiveSize.tostring())" if ($mailbox.TotalArchiveSize.tostring() -ne "0 B (0 bytes)") { $result.TotalArchiveSizeMB = [int]((0 + ($mailbox.TotalArchiveSize.tostring().split("(")[0].replace(" ","")))/1MB) } if ($mailbox.BytesTransferredPerMinute.tostring() -ne "0 B (0 bytes)") { $result.MBperMinute = [int]((0 + ($mailbox.BytesTransferredPerMinute.tostring().split("(")[0].replace(" ","")))/1MB) Write-Verbose "Rate from BytesTransferredPerMinute: $($result.BytesperMinute)" } elseif (($mailbox.TotalInProgressDuration.tostring() -ne "00:00:00") ` -and ($mailbox."StatusDetail" -ne "Synced")) { Write-Verbose "Collect Mailboxsize and duration" $result.MBperMinute = ($result.TotalMailboxSizeMB + $result.TotalArchiveSizeMB)/$result.duration.totalminutes Write-Verbose "Calculated Rate from TotalInProgressDuration: $($result.MBperMinute)" } else { Write-Warning "Unable to calculate messagerate for Mailbox $($mailbox.identity)" Write-Verbose "Statusdetail : $($mailbox.StatusDetail)" Write-Verbose "TotalInProgressDuration : $($mailbox.TotalInProgressDuration.tostring())" } $result.TotalMBToMigrate = $result.TotalMailboxSizeMB + $result.TotalArchiveSizeMB $result.TotalMBDone = [int]($result.TotalMBToMigrate * $result.PercentComplete / 100) $result }
Leider sind die Ausgaben nun gar nicht so nach meiner Vorstellung. So gibt es Einträge, die halbwegs plausible Werte um einige Megabyte/Minute liefern aber auch einige Werte, die auf nicht ganz realistische 2 GB/Minute ansteigen. Wieder andere Werte haben gar keine Duration oder Durchsatz-Zahlen, obwohl alle Migrationsjobs auf 95% standen.
Die von Microsoft hier gelieferte Daten sind also eher ein grobes "Schätzeisen" und weniger eine passable Möglichkeit einer Durchsatzermittlung.
Daher habe ich noch die Spalten "TotalMBToMigrate" addiert, welche sich aus der Größe von "TotalMailboxSizeMB" und "TotalArchivSizeMB" ermittelt. Zudem errechnet sich das Feld "TotalMBDone" aus "TotalMBToMigrate" und dem PercentComplete-Wert. Da die Ausgabe in eine Pipeline geht, kann man so auch schön die Summen bilden.
Weitere Einschränkung
Dieser Code basierend auf "Get-MoveRequest" und "Get-MoveRequestStatistics" funktioniert natürlich nur für die Postfächer, die noch als MoveRequest vorhanden sind. Wenn Sie die Move-Requests schon mit Remove-Moverequest entfernt haben, dann sehen Sie dies hier nicht mehr. Dann allerdings könnten Sie in das Postfach schauen, in dem auch die letzten Move-Reports enthalten sind.
Get-MigrationUserStatistics ` -Identity <MailboxID> -IncludeSkippedItems ` -IncludeReport ` -DiagnosticInfo Verbose ` | Export-Clixml C:\temp\AffectedMigUser1Stats.xml
- Get-MigrationUserStatistics
https://docs.microsoft.com/en-us/powershell/module/exchange/get-migrationuserstatistics?view=exchange-ps - Digging Into Hybrid Migration Move Report Data
https://techcommunity.microsoft.com/t5/exchange-team-blog/digging-into-hybrid-migration-move-report-data/ba-p/1675064
Ergebnis
Meine Hoffnung, über PowerShell etwas genauer auf die Performance eines Remote-MoveRequest in die Cloud schaue zu können, musste ich leider aufgeben. Die Werte sind alle nicht wirklich stimmig und zuverlässig. Das mag auch daran liegen, dass in der Cloud natürlich mehrere MRS-Instanzen parallel die Postfächer migrieren, Inhalte kopieren und die Statusmeldungen eben nicht in Echtzeit aktualisiert werden. Das ist im Prinzip auch gar nicht erforderlich, da eine Migration im Hintergrund abläuft und selten zeitkritisch ist. Ich wollte aber schon abschätzen können, wie lange die Migration braucht und da wäre der Netzwerkdurchsatz natürlich ein guter Indikator.
Das geht aber nicht aus der Cloud, so dass ich auf der lokalen Firewall oder dem lokalen Exchange Server (MRSProxy) eine Auswertung ansetzen müsste. Der MRS selbst liefert ja auch Performance Counter (Siehe MoveRequest und MigrationBatch).
Bis dahin kann ich aus dem Moverequest einfach nur die Gesamtmenge der Daten jedes Postfachs mit der Prozentangabe multiplizieren und damit einen Rückschluss auf die schon migrierte Datenmenge liefern. Zumindest habe ich gelernt, dass die Mailboxgröße als "deserialized-Object" für weitere Berechnungen erst wieder konvertiert werden muss.
Weitere Links
- MoveRequest und MigrationBatch
- Understanding Move equests
https://technet.microsoft.com/en-us/library/dd298174(v=exchg.141).aspx - Check move request status Exchange
https://www.alitajran.com/check-move-request-status-exchange/