Aan het eind van het jaar maak ik altijd een jaarbackup en loop ik mijn server(s) eens door op rare fouten in de logfiles, vreemde gebeurtenissen en andere ongeregeldheden die in de drukte van het afgelopen jaar aan mijn aandacht zijn ontsnapt. Zo kwam ik er nu dus achter dat mijn server al bijna twee maanden niet deed wat-ie moet doen, namelijk ook netjes backuppen. Elke nacht wordt een nette RSync backup van alle data gemaakt, maar belangrijke zaken zoals alle mail (!) hadden in een nachtelijke image backup mee moeten lopen. Maar dat gebeurde dus niet. Grmbl...

Omdat een nachtelijke image backup van je hele machine toch wel een beetje met een kanon op een mug schieten is, leek het me slimmer dit uit elkaar te trekken. De virtuele machines worden dus alleen apart gezet als er ook echt iets zinnigs op wijzigt (en anders hooguit twee keer per jaar) en voor de mail had ik bedacht dat een mooi scriptje om PST's apart te zetten, toch ook mooi zou zijn. Kortom: how hard can it be, dat Powershell...

 

Goed, nu we dus alleen nog maar nerds over hebben die toch echt op het linkje hebben geklikt dat ze meer willen weten... Van Powershell wist ik natuurlijk helemaal niets. Ik heb wel eens .vbs en .bat (inlog) scriptjes in elkaar geschroefd, maar daarmee hebben we het dan ook wel gehad. Maar zoals met al die moderne taaltjes is Powershell best gemakkelijk te lezen en vooral: er zijn bergen voorbeeldscriptjes op internet te vinden. In mijn geval werd ik gewezen op een scriptje van Steve Goodman. In dit script worden mailboxen naar een backuplocatie geduwd. Er wordt rekening gehouden met het feit dat een export kan mislukken, want hij slaat statistieken van mislukte exports op in een separaat logfile. Maar eerlijk gezegd heb je daar erg weinig aan. Daarom heb ik het script op twee manieren aangepast: allereerst worden de mailboxen apart gezet. Pas als de backup lukt, wordt een oud (of zelfs meerdere oude) backups weggehaald. Daarbij heb ik fatsoenlijke versiecodering voor logfiles en exports toegevoegd. Wel zo handig... Verder heb ik een mailscript toegevoegd. Ik ga niet elke dag in een directory kijken waar ik logfiles opsla. Wat ik wel doe, is elke ochtend even in mijn mail kijken of alle backups goed gelopen hebben.

Nauurlijk klikt het allemaal weer veel optimistischer dan het in werkelijkheid ging. Het basisscriptje had ik in een avondje wel aan de praat, maar het bugfixen heeft me vervolgens wel een week gekost. ;-) Hoe dan ook: nu doet-ie het. 

Ik heb deze backup opgenomen in de task manager op mijn server. Deze schiet een ouderwets cmd bestandje af, wat op zijn beurt het onderstaande .ps1 script aan de gang zet. Simpel maar wel effectief.

Hoe dan ook: voor alles wat het waard is, ben ik natuurlijk ook maar een prutser als het om scriptjes gaat. Voor slimme opmerkingen houd ik me natuurlijk aanbevolen... 

 

################################################################################

# Exchange Mailbox Export Script

# Written by Friso Wittebol

# Inspired on a script by Steve Goodman.

# http://www.stevieg.org/2010/07/using-the-exchange-2010-sp1-mailbox-export-features-for-mass-exports-to-pst/

# Use at your own risk!

###########

# Settings    #

###########

# Server

$Server = [server]


# Share to export mailboxes to. Needs R/W by Exchange Trusted Subsystem

# Must be a UNC path as this is run by the CAS MRS service.

$ExportShare = [backup path]


# After each run a report of the exports can be dropped into the directory specified below. (The user that runs this script needs access to this share)

# Must be a UNC path or the full path of a local directory.

$ReportShare = [report path]

# After each run e-mails are sent with updates about completed and not-completed exports

#mail settings

$From = [address]

$To = [address]

$SMTPServer = [mail server]

$SMTPPort = [smtp port; usually port 25.  For instance 587 for smtp.gmail.com]

$Username = [username]

$Password = [password]


# Shall we remove the PST file, if it exists beforehand? (The user that runs this script needs access to the $ExportShare share)

# Valid values: $true or $false

$RemovePSTBeforeExport = $true


###########

# Code        #

###########


    if (!(Get-ExchangeServer $Server -ErrorAction SilentlyContinue))

    {

        throw "Exchange Server $Server not found";

    }


    if (!(Get-MailboxDatabase -Server $Server -ErrorAction SilentlyContinue))

    {

        throw "Exchange Server $Server does not have mailbox databases";

    }

    $Mailboxes = Get-Mailbox -Server $Server -ResultSize Unlimited


if (!$Mailboxes)

{

    throw "No mailboxes found on $Server"

}


if (!$Mailboxes.Count)

{

    throw "This script does not support a single mailbox export."

}


###########

# Pre-checks done

###########


# Remove old requests, just to make sure

Get-MailboxExportRequest | Remove-MailboxExportRequest -Confirm:$false

# Create batch name

$date=Get-Date -Format "yyMMdd-HHmm"

$BatchName = "PST Export_$($date)"


# Queue all mailbox export requests

Write-Output "Queuing $($Mailboxes.Count) mailboxes as batch '$($BatchName)'"

foreach ($Mailbox in $Mailboxes)

{

    if ($RemovePSTBeforeExport -eq $true -and (Get-Item "$($ExportShare)\$($Mailbox.Alias).PST" -ErrorAction SilentlyContinue))

    {

        # Keeping the old backup safe by renaming the old PST file; this file will be removed when the backup is succesfull

        Rename-Item "$($ExportShare)\$($Mailbox.Alias).PST" -NewName:"$($ExportShare)\$($Mailbox.Alias)_old_$($date).PST"

    }

    New-MailboxExportRequest -BatchName $BatchName -Mailbox $Mailbox.Alias -FilePath "$($ExportShare)\$($Mailbox.Alias).PST"

    # Every mailbox gets a little time to get queued, which reduces the chance of failures. 

    Sleep 60

}


Write-Output "Waiting for batch to complete"

# Wait for mailbox export requests to complete

while ((Get-MailboxExportRequest -BatchName $BatchName | Where {$_.Status -eq "Queued" -or $_.Status -eq "InProgress"}))

{

     sleep 60

}


###########

# Export should be finished

###########


# Get completed exports

$body = ""

foreach  ($Mailbox in $Mailboxes)

{   

  if  ((Get-MailboxExportRequest -BatchName $BatchName -Mailbox $Mailbox.Alias | Where {$_.Status -eq "Completed"}))

    {

        write-output "PST $($ExportShare)\$($Mailbox.Alias) will be removed"

        # Remove the 'old' backup PST file

        Remove-Item "$($ExportShare)\$($Mailbox.Alias)_old_*.*" -Confirm:$false

         $body = $body + $Mailbox.Alias + "`r`n"

    }  

}

# Send email completed backups

if($body)

{

     $subject = "PST Backup Completed"

     $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);

     $smtp.EnableSSL = $true

     $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);

     $smtp.Send($From, $To, $subject, $body);

}


# Get exports which didn't finish properly

$body = ""

foreach  ($Mailbox in $Mailboxes)

{   

if  ((Get-MailboxExportRequest -BatchName $BatchName -Mailbox $Mailbox.Alias | Where {$_.Status -ne "Completed"}))

{

          $body = $body + $Mailbox.Alias + "`r`n"

}  

}


# Send email not completed

if($body)

{

     $subject = "PST Export NOT Completed"

     $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);

    $smtp.EnableSSL = $true

     $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);

     $smtp.Send($From, $To, $subject, $body);

}


# Write reports if required

if ($ReportShare)

{

     Write-Output "Writing reports to $($ReportShare)"

     $Completed = Get-MailboxExportRequest -BatchName $BatchName | Where {$_.Status -eq "Completed"} | Get-MailboxExportRequestStatistics | Format-List

     if ($Completed)

    {

         $Completed | Out-File -FilePath "$($ReportShare)\$($BatchName)_Completed.txt"

    }

     $Incomplete = Get-MailboxExportRequest -BatchName $BatchName | Where {$_.Status -ne "Completed"} | Get-MailboxExportRequestStatistics | Format-List

     if ($Incomplete)

    {

         $Incomplete | Out-File -FilePath "$($ReportShare)\$($BatchName)_Incomplete_Report.txt"

    }

}


# Remove Requests

Get-MailboxExportRequest  | Remove-MailboxExportRequest -Confirm:$false

exit

################################################################################

 

 

 

 Enjoy!