Thursday, March 5, 2015

Powershell progress bar for Exchange mailbox moves

I was at a client site recently to provide backup support for some cross-forest Exchange mailbox migrations, which meant I was there onsite but not actually doing much aside from the occasional weird error when Prepare-MoveRequest.ps1 couldn't find the target AD account and decided to create a new one, which is more than a little annoying. But anyway, I have always wanted to play with Powershell with Windows Forms, and the Exchange Management Console's move request section is sorely lacking in graphical progress. Sure, you can add the "Percent Complete" column but you still have to refresh it manually. Usually I just execute

while((get-moverequest -MoveStatus InProgress) -ne $null) {get-moverequest -MoveStatus InProgress | Get-MoveRequestStatistics; start-sleep -s 5}

But I thought it would be cool to have a GUI progress bar so I did a little Googling and found two articles that combined provided me with what I needed and I used my free time to build a cool script that my client can use for their migrations.

Using the code from the first article created the window and progress bar just fine but it wouldn't update. The second article provided the key to getting it working properly using a Timer. Without further ado, here is the final code to pop up a progress bar for an Exchange mailbox move. Although it does lock up the Powershell window while it's running, and you have to launch it from an Exchange Powershell, you can't just right click and say Open With Powershell. Maybe I'll update this in the future.

[CmdletBinding()] 
param(
[Parameter(Mandatory=$true)][string]$Username
)

Add-Type -assembly System.Windows.Forms

$mb = Get-MoveRequest $Username

$Title = "Mailbox Move Progress: $($mb.DisplayName)"
$height=100
$width=400
$color = "White"

$form1 = New-Object System.Windows.Forms.Form
$form1.Text = $title
$form1.Height = $height
$form1.Width = $width
$form1.BackColor = $color

$form1.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle 
$form1.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen

$label1 = New-Object system.Windows.Forms.Label
$label1.Text = "not started"
$label1.Left=5
$label1.Top= 10
$label1.Width= $width - 20
$label1.Height=15
$label1.Font= "Verdana"

$form1.controls.add($label1)

$progressBar1 = New-Object System.Windows.Forms.ProgressBar
$progressBar1.Name = 'progressBar1'
$progressBar1.Value = 0
$progressBar1.Style="Continuous"

$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = $width - 40
$System_Drawing_Size.Height = 20
$progressBar1.Size = $System_Drawing_Size

$progressBar1.Left = 5
$progressBar1.Top = 40

$form1.Controls.Add($progressBar1)

$label1.text="Preparing to analyze $($mb.DisplayName)"
$form1.Refresh()

start-sleep -Seconds 1

Function GetPct {
    if((Get-MoveRequest -Identity $Username).Status -eq "InProgress") {
        [int]$pct = (Get-MoveRequestStatistics $Username).PercentComplete
        $progressbar1.Value = $pct
        $label1.text="$($mb.DisplayName) Progress: $pct %"
        #Write-Host $pct
        $form1.Refresh()
    } else {
        $timer.enabled = $false
        Write-Host "Move complete, please close window when ready."
    }
}

$timer = New-Object System.Windows.Forms.Timer 
$timer.Interval = 5000

$timer.add_Tick({
GetPct
})

if((Get-MoveRequest -Identity $Username).Status -eq "InProgress") {
    $timer.Enabled = $true
    $timer.Start()

    $form1.Add_Shown({$form1.Activate()})
    $form1.ShowDialog()
} else {
    Write-Host "Move is $($mb.Status) for $($mb.DisplayName)"
}