Friday, September 21, 2012

PowerShell Script - Mailbox Folder Import and Migration Verification

I have used this script in the past to verify that mailbox content was imported or migrated successfully. Basically, all it does is look for mailboxes that have an empty Inbox, Sent Items, Contacts, or Calendar folder. If a mailbox is found with at least one of these folders being empty, it adds the mailbox to a report with the count of each folder, then generates a CSV report at the end.
The list of mailboxes to check can be one of the following:
  1. CSV file - the script only looks for a column of emailAddress.
  2. By specifying an OU of users to check
  3. All mailboxes in the Exchange org.
The only required switch is -OutFile which requires a valid file name and path for the CSV file.
I have tested this script on Exchange 2010 SP1, Windows 2008 R2, and PowerShell 2.0 in both production and a lab environment. If you run into any issues please post a comment or question.


Reports on mailboxes with an empty Inbox, Calendar, Contact, or Sent Items folder. 
This is very useful as a quick check to see if migrated or imported mailboxes have empty
folders, but does NOT verify that ALL data was successfully migrated/imported. It does serve
well as a quick reference on mailboxes that may have blatantly failed during migration/import.

The list of mailboxes to evaluate can be sourced from a CSV, a particular OU, or all mailboxes
in the current Exchange organization. For each mailbox, we check to see if any of the following
mail folders (and their subfolders) are empty: Inbox, Sent Items, Contacts, Calendar. If any of
these are reported as having zero items, we add the mailbox's email address and each folder's
item count to a CSV.

Specifies the full path and filename to store the CSV formatted report. This is REQUIRED.

Specifies the full path and filename of the CSV to be used as input. The CSV used here only 
requires the emailAddress field in the header and for each mailbox. If both -CSV and -OU are used
together, -OU will be ignored.

Specifies the canonical name of the AD Organizational Unit containing mailboxes to be inspected.
This is the same as the -OrganizationalUnit parameter of the Get-Mailbox cmdlet in Exchange 2010.
If both -CSV and -OU are used together, -OU will be ignored.

./MbxImpMigCheck.ps1 -OutFile c:\Temp\report.csv
Generates a report on any mailbox in the Exchange Organization that has an empty Inbox,
Sent Items, Contacts, or Calendar folder, and saves the report as a CSV in C:\Temp\report.csv
WARNING: This can take a while to run in large environments

./MbxImpMigCheck.ps1 -OutFile c:\Temp\report.csv -CSV c:\Temp\inputFile.csv
Generates a report on any mailbox in the CSV that has an empty Inbox, Sent Items, Contacts, or 
Calendar folder, and saves the report as a CSV in C:\Temp\report.csv

./MbxImpMigCheck.ps1 -OutFile c:\Temp\report.csv -OU "ou=mailboxes,dc=domain,dc=com"
Generates a report on any mailbox in the Mailboxes OU of the domain that has an empty 
Inbox, Sent Items, Contacts, or Calendar folder, and saves the report as a CSV in C:\Temp\report.csv

Author: Trent Nevius (
PowerShell Modules required: [Exchange 2010]
Tested PowerShell version: [2.0]
Tested software: Microsoft Exchange 2010 SP1 on Windows 2008 R2



# Define Parameters
[string] $CSV = "",
[string] $OU = "",
[string] $OutFile

# Ensure the OutFile parameter was supplied
if ($OutFile -eq "") {
Write-Host "`nMissing parameter: The -OutFile parameter is required. Please re-run this script with a valid path and filename for the -OutFile parameter.`n"

Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue

# Define array variables
$Results = @("emailAddress,Inbox,SentItems,Calendar,Contacts`n")
$Mailboxes = @()

if ($CSV -ne "") {Import-CSV -Path $CSV | foreach {$Mailboxes += Get-Mailbox $_.emailAddress}}
elseif ($OU -ne "") {$Mailboxes = Get-Mailbox -OrganizationalUnit $OU}
else {$Mailboxes = Get-Mailbox -ResultSize unlimited}

foreach ($mailbox in $Mailboxes) {
$CalendarCount = (get-mailboxfolderstatistics $mailbox.alias -FolderScope Calendar).ItemsInFolderAndSubfolders
$InboxCount = (get-mailboxfolderstatistics $mailbox.alias -FolderScope Inbox).ItemsInFolderAndSubfolders
$ContactCount = (get-mailboxfolderstatistics $mailbox.alias -FolderScope Contacts).ItemsInFolderAndSubfolders
$SentCount = (get-mailboxfolderstatistics $mailbox.alias -FolderScope SentItems).ItemsInFolderAndSubfolders
    if (($CalenderCount -eq 0) -or ($InboxCount -eq 0) -or ($ContactCount -eq 0) -or ($SentCount -eq 0)) {
        $Results += $mailbox.PrimarySMTPAddress.ToString() + ",$InboxCount,$SentCount,$CalendarCount,$ContactCount`n"
$Results > $OutFile
Write-Host "`nReport Complete, results output formatted as CSV to " $Outfile

Tuesday, September 20, 2011

SCVMM "Migration Failed"- how to clear

When working with Microsoft SCVMM (System Center Virtual Machine Manager) in a Hyper-V virtualization environment, we have run across scenarios in which the failed migration of a VM from one host to another may leave the VM in a failed state. Specifically, in the SCVMM console, the VM has a red 'X' beside it with the "Migration failed" under the status column. While the VM is in this failed state, SCVMM will only allow you to Repair, Delete, or View networking of the VM. Furthermore, if you right-click the VM and select 'Repair', the option to 'Ignore' is unavailable, and the 'Retry' and 'Undo' options fail. Even though you may have corrected the problem outside of VMM, for example using the Hyper-V Manager console, the repair options yield no results.

We have read many articles and posts on how to deal with this scenario, such as removing the host from VMM and adding it back. Or using SQL Server Management Studio to hide failed jobs (NOT a good idea as it can eventually lead to poor SCVMM performance since the VMM cleanup process will skip old jobs that are hidden!). Once you have corrected the problem and exhausted all possibilities to remedy the situation, here is a handy PowerShell script that will help you out of this jam, which we pieced together specifically for this situation.

First, a little peek under the hood:

- When the migration fails, the ObjectState field in SQL for the VM gets set to 220, indicating a failed state.
- If the ObjectState field for a VM is set to 0 (zero) or 1, SCVMM will re-check the VM state. If you have repaired the VM and set the ObjectState in the VirtualManagerDB to 0, VMM will immediately notice the VM has been fixed, and all is well.

I have created this script and used it in a production environment, but use it at your own risk. Be sure to back up the VMM database just before using this so you have an out.

Script name: Clear-VMMFailState.ps1
PS Requirements: I have only tested this under PowerShell 2.0, and have verified that it doesn't require any special PS modules such as the VMM or SQL modules. It worked fine for us in the out-of-box PowerShell in Win2008R2 with no modules loaded. This should also work remotely with no problem.

We encourage feedback on your experience with this script, and any suggestions you may have on how to improve it.

Script Help (get-help ./Clear-VMMFailState.ps1 -Detailed)
This script will clear the failed state of a Virtual Machine in VMM.

!!!WARNING!!! back up your VMM SQL database before using this script. You have been warned!
This should only be used under the following circumstances:
- If a failure in a VMM job has a VM in a 'Failed' state
- AND -
- The repair function in VMM won't allow you to 'Ignore'
- AND - 
- The repair function in VMM fails when performing a 'Retry' or 'Discard'
- AND -
- You have corrected the issue outside of VMM (such as with Hyper-V Manager MMC etc.)
- AND -
- You have already tried refreshing the VMM Server, VM Host, and VM from within the VMM console or shell

-VMname <String> - the name of the Virtual Machine (as it appears in SCVMM)
-VmmDBserver <String> - this is the FQDN of the server that hosts the SQL instance of the SCVMM database
- VmmDBname <String> - this is the Database name of the SCVMM database; if not specified, this defaults to the value of 'VirtualManagerDB'
-IntegratedAuth <Switch> - if this switch is specified, integrated Windows authentication is used for the connection to the SCVMM database. If not specified, you must use the -UserName and -Password switches to authenticate to SQL
-UserName <String> - The username used to authenticate to the SCVMM database. If not using the -IntegratedAuth switch, this is required
-Password <String> - Password for the user specified with -UserName

Example 1:
./Clear-VMMFailState.ps1 -VMname TestVM01 -VmmDBserver -IntegratedAuth
>> Connects to the SQL server that hosts the SCVMM database ( and the default VMM database (VirtualManagerDB) using Integrated Authentication with currently logged on credentials, then clears the failed state of the Virtual Machine named 'TestVM01'.

Example 2:
./Clear-VMMFailState.ps1 -VMname TestVM01 -VmmDBserver -VmmDBname MyVmmDB -UserName\vmmAdmin -Password MyPassWord
>> Connects to the SQL server that hosts the SCVMM database ( and the VMM database named 'MyVmmDB' using the specified username and password, then clears the failed state of the Virtual Machine named 'TestVM01'.

And now for the script...