Dismount Recovery Storage Group using Exchange 2007 Shell

Dismounting the RSG from and Exchange Mailbox server is the last step after you have recovered data you needed. I would like to stress here that my experience tells me this is the step when you will encounter issues, not necesarily issues with the process itself but mostly with the GUI interface. I’ve spent a lot of time trying to figure out which a RSG won’t dismount, when the GUI reported success. So I have come up with a script that does this using exchange management shell, which gives you a little more control and additional debugging information.

Here is what you will need to accomplish this:

  • Exchange Server Administrator Rights where the RSGs are located.
  • Exchange Management Shell since this entire procedure is best done using powershell scripting.
  • Run the script as Administrator to avoid errors due to enabled UAC

The Script

#Dismount DB, Remove, DB, Remove Storage Group
$DB_in_RSG = Get-MailboxDatabase -Server <ENTERSERVER> | where {$_.Recovery -like '*true*'}
Write-Host -ForegroundColor Green "The Database in the RSG ($($DB_in_RSG.Name)) will be Dismounted. Press Ctrl+C to cancel or Enter to continue"
Read-Host
$DB_in_RSG | Dismount-Database -Verbose -Debug

Write-Host -ForegroundColor Green "The Database in the RSG will be Removed. Press Ctrl+C to cancel or Enter to continue"
Read-Host
$DB_in_RSG | Remove-MailboxDatabase -Verbose -Debug

#Now deleting the files of the database
$SG_Path = (Get-StorageGroup $DB_in_RSG.StorageGroup).SystemFolderPath
Write-Host -ForegroundColor Green "The actual DB files will be removed (stored in $SG_Path). Please check previous steps completed successfully before continuing. Press Ctrl+C to cancel or Enter to continue."
Read-Host
get-item $SG_Path | del -Force -Recurse -Verbose -Debug

#Now removing storage group
Write-Host -ForegroundColor Green "The RSG will be Removed. Press Ctrl+C to cancel or Enter to continue"
Read-Host
Remove-StorageGroup -Identity $DB_in_RSG.StorageGroup -Verbose –Debug

Learning Points

There are a few steps you need to do:

  1. Grab the RSG (there is only one per server) – stored in the script in $DB_in_RSG
  2. You dismount the database from the server using Dismount-Database cmdlet.
  3. After disomunting the database you remove the Database using Remove-MailboxDatabase cmdlet. This will just remove the DB from Exchange, the files will remain on the file system.
  4. The actual files on the file system get removed using the path from variable $SG_Path. Use del cmdlet with -force -recurse to bypass any confirmation prompts.
  5. The last step is removing the Recovery Storage Group from the Exchange. The commandlet for this is Remove-StorageGroup.

That’s about it, pretty easy actually. Although this script is the last in the series I reccomend you actually run this before anything, before mounting the RSG or at least do a check to see if a RSG is already mounted.

Restore a mailbox in the Recovery Storage Group

Finally I managed to find some more time about posting the next bits of the Exchange mailbox recovery process. Last post we discussed how to mount the database into the RSG. This time we will be discussing how to restore a user’s mailbox in the original mailbox location and how to restore the mailbox data to another temporary mailbox and exporting that out of the temporary mailbox.

Here is what you will need to accomplish this:

  • Exchange Server Administrator Rights where the mailbox(es) are located.
  • Full mailbox access for exporting emails out of the temp mailbox I was talking about
  • Exchange Management Shell since this entire procedure is best done using powershell scripting.
  • Obviously free space both on the Database disk aswell as disk space where the PST file will be saved.

The Script

#Parameters Section
param (
 [parameter(Mandatory = $true)]
 [string]$paramRestoredUser
)

cls
#Restore mailbox section
$Filter = "SamAccountName -like '$paramRestoredUser'"
$SourceAlias = Get-Mailbox -Filter $Filter -IgnoreDefaultScope

If ($SourceAlias -eq $null) {
 Write-Host -foregroundcolor Red "No Mailbox for SamAccountName $paramRestoredUser found.Script will quit"
exit
 }

$RSG_DB = Get-MailboxDatabase -Server $SourceAlias.ServerName | where {$_.Recovery -like '*true*'}
if ($RSG_DB -eq $null) {
 Write-Host -ForegroundColor Red "No RSG was found on $($SourceAlias.ServerName). Script will quit now!"
 exit }

$TargetAlias = Get-Mailbox e2k7_Restore_MBX
Write-Host -ForegroundColor Green "Input the date of the restored mailbox:"
$RestoreDate = Read-Host

$TargetFolder = "$($SourceAlias)_$RestoreDate"
Restore-Mailbox -Identity $TargetAlias.Alias -RSGMailbox $SourceAlias.ExchangeGuid -RSGDatabase $RSG_DB -TargetFolder $TargetFolder -BadItemLimit 1000 -Verbose -Debug -Confirm -ValidateOnly

echo "-ValidateOnly switch from the command above is removed. Please check no errors occured above and everything is configured properly. Press Enter to continue CTRL+C to cancel the script!"
Read-Host
Restore-Mailbox -Identity $TargetAlias.Alias -RSGMailbox $SourceAlias.ExchangeGuid -RSGDatabase $RSG_DB -TargetFolder $TargetFolder -BadItemLimit 1000 -Verbose -Debug -Confirm

#building an excluded folders list, just in case we did another restore and the previous content was not deleted
$IncludedFolders = $TargetFolder
$ExclFoldersList = @()
$ExcludedFolders = Get-MailboxFolderStatistics $TargetAlias | where-object { $_.FolderPath -notlike "*$($IncludedFolders)*"} | Select-Object FolderPath | foreach-object {
 $ExclFoldersList += $_.FolderPath }
$ExclFoldersList = ([string]::join(",",$ExclFoldersList)).Replace("/","\")

$UserName = $TargetAlias
$PSTPath = "Q:\UserPersonalFolders"
"Excluded folders list: $ExclFoldersList"
Read-Host
export-Mailbox -Identity $UserName.SamAccountName -BadItemLimit 1000 -DeleteContent $True -PSTFolderPath $PSTPath -ExcludeFolders $ExclFoldersList

The details for the Script

First the script gathers data about the restored mailbox, and from that info the Recovery Storage Group ($SourceAlias). It also needs a target mailbox, where the restored data will be stored ($TargetAlias) (needless to say I did in a “forest friendly way, using -Filter cmdlet looking up the given SamAccountName). The data will be moved from the mailbox in the RSG into the $TargetAlias mailbox. It will be placed in a folder, named with the date of the restored data ($RestoreDate). Restoring the data is done with this command:

Restore-Mailbox -Identity $TargetAlias.Alias -RSGMailbox $SourceAlias.ExchangeGuid -RSGDatabase $RSG_DB -TargetFolder $TargetFolder -BadItemLimit 1000 -Verbose -Debug -Confirm -ValidateOnly

Now we want to export the data from the $TargetAlias into a PST file. This is easy to do via the Export-Mailbox cmdlet. The problem appears when you do for instance multiple exports in the same target mailbox and then a mass export, or just have left old data in the target mailbox. You do get data separated into folders, if your $TargetFolder name is unique, but the Export-Mailbox cmdlet cannot export data based on this information (it can only do some filtering (date,content,subject) and I believe it is resource intensive). What Export-Mailbox does have is the “-Excludefolders” parameter which lets you not export certain data.

The trick I came up with was to scan the $TargetAlias mailbox for foldernames (I used Get-MailboxFolderStatistics for that) and build a list of the folders that did not contain the value I entered in the $TargetFolder variable upon restore. Besides that you also have to do some parsing of the output from get mailboxfolderstatistics into something Export-Mailbox understands. This is what I did:

$IncludedFolders = $TargetFolder
$ExclFoldersList = @()
$ExcludedFolders = Get-MailboxFolderStatistics $TargetAlias | where-object { $_.FolderPath -notlike "*$($IncludedFolders)*"} | Select-Object FolderPath | foreach-object {
 $ExclFoldersList += $_.FolderPath }
$ExclFoldersList = ([string]::join(",",$ExclFoldersList)).Replace("/","\")
downtime romtelecom

In the end I just ran the Export-Mailbox on the target mailbox, specifying the excluded files and that was that.

The whole process is pretty easy, the actual restore is done in a one liner (Restore-Mailbox), but as you can see error checking and failproofing the script make up the rest of the work. Hope you enjoy this, next up is how to dismount a RSG, the scripted “clean-way”.

Restoring mailboxes in Exchange 2007 (part 1)

Lately I’ve been doing a number of mailbox restore procedures on Exchange 2007, so I thought it would be a good idea to make my own posts about it (yes it involves scripting), because things are not always as straightforward as MS or TechNet say it is. This is going to be a multi-part post: Create the RSG and mount the DB to be restored, Restore mailbox(es), Remove the restored DB and RSG. Before you think about it I’m going to answer it for you:

Q: But why don’t we use the nice GUI Tool from Exchange Management Console (Extra.exe) and do it from there, “we don’t need no scripting”?

A: My experience tells me the scripted method is safer and works “as expected” unlike the GUI, which says it did something, when it didn’t (I’ve spent days trying to figure out why a RSG Database won’t actually dismount when the GUI said: “Completed Successfully”.

OK, let’s get on with it. All that I am about to explain requires Exchange Administrator privileges on the Exchange servers.

We will be creating a Recovery Storage Group, this is the first step in the restore process. To create the RSG you need following:

  • Adequate disk-space to restore the mailbox database, locally on the Exchange Server where the DB was residing
  • Exchange Management Shell running as Administrator (especially on CCR clusters)
  • No other Recovery Storage Group already created on that server with an existing RSG database (you can only have 1 RSG with 1 DB in the RSG). It is best to remove any previous RSG completely then recreate it for your needs.
  • Specific information like which DB to link to the RSG and the list of mailboxes to restore.

Creating a Recovery Storage Group can be as easy as this:

New-StorageGroup -Server <MBX Role Server Name> -Name <StorageGroup Name> -LogFolderPath <Logs Folder> -SystemFolderPath <SystemFiles Path> -Recovery -Verbose

The command is very similar to creating a new SG, except for the -Recovery switch, designating it as a Recovery Storage Group. I added the -Verbose switch so you can see what is going on behind the scenes.

New-MailboxDatabase -MailboxDatabaseToRecover <Mailbox Name> -StorageGroup <Recovery Storage Group Name> -EdbFilePath <path to store edb file> -Verbose

Here it is just as easy as creating a new mailbox database, only you are creating it in the recovery storage group you created with the previous command. The key thing to remember here is that the value of the “MailboxDatabaseToRecoverParameter” must be the exact same name of the mailboxDB of which you want to recover from. If the name is different you will not be able to run any restore commands, because it will not be able to find any mailboxes when it searches the recovered database.

A working script for creating the RSG

Below I’m sharing with you a working snippet that should help in creating a recovery storage group and DB. In short here is what the code does:

Using a given UserPrincipalName…

  • Attempts to retrieve the mailbox for the UPN (it is a “forest friendly” coding for retrieving the mailbox). If it fails it quits
  • Checks if a folder structure for placing, logs, system files and the edb file exists (I used a location called d:, use a variable if you like).
  • If folders already exist, it will quit, otherwise it will create a folder with the MDB name, and logs and edb subfolders,
  • Next it checks if a Recovery Storage Group already exists, unless you cancel the script it will continue to use this RSG, with the given details. Otherwise it will create a RSG on its own.
  • It will then create a mailbox database where you / your backup admin will restore your exchange backup.
$MBX_UPN = Read-Host
$Filter = "UserPrincipalName -like '$MBX_UPN'"
$SourceMBX =  get-mailbox -IgnoreDefaultScope -Filter $Filter
If ($SourceMBX -eq $null) {
	Write-Host -foregroundcolor Red "No Mailbox for $MBX_ID found`nScript will Quit"
	exit }
Write-Host -ForegroundColor Green "Source Mailbox is`n $SourceMBX"

$LinkedMDB = Get-MailboxDatabase -Identity $SourceMBX.Database
Write-Host -ForegroundColor Green "Ok, Database ($($LinkedMDB.StorageGroup.Name)) is grabbed, now creating RSG Folders and RSG`nPress Enter to continue or Ctrl+C to Cancel"
Read-Host

#Checking if the RSG folders already exist, if not attempt to create them
If ((Test-Path "d:\$($LinkedMDB.StorageGroup.Name)")) {
 Write-Host -ForegroundColor Red "Folder already exists. Please remove d:\$($LinkedMDB.StorageGroup.Name) before running this script again.`nScript will quit"
 exit
 }
$SysPath = New-Item -Type Directory -Path d: -Name $LinkedMDB.StorageGroup.Name | Get-Item
If ((Test-path $SysPath)) {
 $DBPath = New-Item -Type Directory -Path $SysPath -Name DB | Get-Item
 $LogsPath = New-Item -Type Directory -Path $SysPath -Name Logs | Get-Item
 }
#If folders were created successfully we can continue
If ((Test-path $SysPath) -and (Test-path $DBPath) -and (Test-path $LogsPath)) {
 #Checking if RSG already exists
$RSG_check = Get-StorageGroup -Server $LinkedMDB.ServerName | where {$_.Recovery -like "True"}
 If ($RSG_check -ne $null) {
 Write-Host -ForegroundColor Magenta "A RSG was found on $($RSG_check.ServerName). Here are RSG Details:"
 $RSG_Check | select-object Name,Identity,Recovery,LogFolderPath,SystemFolderPath | fl
 Write-Host -ForegroundColor Magenta "To use this RSG Press Enter, to cancel Press Ctrl+C"
 Read-Host
 }
 Else {
 Write-Host -ForegroundColor Green "Now creating Recovery Storage Group..."
 New-StorageGroup -Server $LinkedMDB.Server -Name "Recovery Storage Group" -LogFolderPath $LogsPath.FullName -SystemFolderPath $SysPath.FullName -Recovery -Verbose
 }
 Write-Host -ForegroundColor Green "OK! No RSG found. Now creating RSG Database..."
 New-MailboxDatabase -MailboxDatabaseToRecover $LinkedMDB.AdminDisplayName -StorageGroup "$($LinkedMDB.ServerName)\Recovery Storage Group" -EdbFilePath "$($DBPath.FullName)\$($LinkedMDB.Name).edb" -Verbose
 }
Else {
 Write-Host -ForegroundColor Red "Could not Create folder or folder structure in d:\$($LinkedMDB.StorageGroup.Name). Check messages above for errors! Script will quit."
 exit
 }

This is about it with creating a Recovery Storage Group, it is actually not difficult, just remember to name the MDB inside the RSG with the same name as the source MDB (this was also required on Exchange 2003, as far as I know). Also you cannot have more than one RSG per Maibox Server, it is best to remove any RSG you have after you are finished recovering data. Next post we will discuss how to restore data from a MDB and how to remove the RSG.

As always I value your feedback and hope you found this post useful.