How to backup your server data, (MySql, SQL Server, Directories) with Powershell
Not a very long time ago I lost some data on my Windows server. It was a totally unexpected failure of the hard disk. Back then I had a local backup system which failed along with the hard-disk failure. So later when I got a second server to host some of my websites I decided to create a backup system that would take a total backup of each server on the second server.
That included MySQL databases, SQL Server databases and some folders. There were some custom solutions available for taking MySQL and SQL Server backups, but nothing that would nicely package all my files into one unit, so I decided to roll my own using Powershell, that awesome new toy for Windows Scripting. It was a chance to learn and work with something new, so how could I let it pass me by?
So dear reader, for all three of you (hi dad!) who actually read my blog, here’s TOTAL SERVER BACKUP through Powershell.
1. Taking MySQL Backup through Powershell
That should have been trivial, after all, all I need to do is script the commandline executable mysqldump which is already present in the bin folder of the Mysql installation on the server. How hard could that have been?
It turns out that was the hardest part of the job. Some of my websites are in Hindi (using Unicode), while mysqldump worked just fine from the command line, it just didn’t encode my content right from within Powershell. I tried every encoding method I could fine, and almost all the options (Unicode, UTF8), but I was still getting gibberish.
I gave up at one stage and wrote a backup script in C# .net. It worked just fine with some encoding settings and that told me it was possible. So I went back to Powershell and dug a little more. Thank you Rene Saarsoo for this gem of wisdom which told me that the correct encoding setting wasn’t UTF8, or Unicode which I had been battling with, but ‘oem’ (whatever that is supposed to mean).
So here’s finally my correct Mysql backup function that works.
function Backup-MySqlDB([string]$dbname)
{
"Backing up mysql " + $dbName + "..."
#Backup all mysql DBs
#Set-Location D:\MySQL\bin
Set-Location 'C:\Program Files\Parallels\Plesk\Databases\MySQL\bin'
$bkfile = 'C:\autobackup\Databases\' + $dbname + '.sql'
cmd /c mysqldump -u username -ppassword $dbname | Out-File $bkfile -encoding oem
Write-Host $dbName + "backup complete"
}
Notice that this is a function which accepts the database name as the argument. I have set up a new admin level user with powers over all the databases for the backup purpose.
Calling this function is as easy as this:
Backup-MySqlDB('blogvani')
2. SQL Server Backup Through Powershell
Some of the content on my server is on legacy (yeah! I don’t use SQL Server anymore) SQL Server database. I’ve got the SQL Server Express on my servers which is free, but not powerful and I really can’t automate backups. But Powershell can tap into .Net, use .Net DLLs and the full .Net Framework, so you can do practically anything with Powershell, including writing your own .Net library and consuming that within Powershell. That should teach those BASH script kiddies a volume or two!
Back to business. So basically because you can tap into .Net from Powershell it isn’t very hard to use the SQL Server backup objects exposed in .Net. Here’s the code which I collected and modified from the Internet.
function Backup-SqlDB([string]$db)
{
"Backing Up Sql Server Databases..."
#Backup SQL Server DBs
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum") | Out-Null
$backupDir = 'c:\autobackup\databases\'
$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server") '.\SQLEXPRESS'
Write-Host $server
$smoBackup = New-Object("Microsoft.SqlServer.Management.Smo.Backup")
Write-Host $db
$smoBackup.Action = "Database"
$smoBackup.BackupSetDescription = "Full backup of " + $db
$smoBackup.BackupSetName = $db + " Backup"
$smoBackup.Database = $db
$smoBackup.MediaDescription = "Disk"
Write-Host ($backupDir + $db)
$smoBackup.Devices.AddDevice(($backupDir + $db) + ".bak", "File")
$smoBackup.SqlBackup($server) #Make the backup
Write-Host $db "Sql Server Backup Finished"
}
Like the Mysql backup function this function too takes the name of the database as an argument and backs the db up in a predefined location which I have hard-coded because I don’t want to change it.
3. Zip the DBs up
Even in the backed up state the DBs are pretty large, not to mention they are all different files. So if I want to upload all the DBs to the second server that’s a problem. So I decided to zip everything up in a single file. There could be other solutions, but the one I decided to implement was using that cute command line zip utility Infozip which I have been using since I was a teenager. All I needed to do was set up a command line call. Another little function for you:
function Zip-DBs()
{
"Zipping the files..."
#Zip the backup files
Set-Location C:\autobackup\databases
./zip backup.zip *.sql *.bak | out-null
remove-Item *.bak
remove-Item *.sql
"Files zipped."
}
Note that I called ‘Set-Location’ commandlet to set the location of the Powershell prompt to the folder where I have zip.exe and the SQL server (.bak files) and Mysql (.sql files) backup. After zipping them up I deleted the actual backup files using the ‘remove-item’ commandlet.
4. Backup Directories Through Powershell
We just finished backing up MySQL and SQL Server DBs through powershell, how hard can backing up a directory be? Since I have a zip file already set up I just wanted to add those website folder to my zip file. Here’s how the code works:
function BackupDir([string]$bkdir)
{
Set-Location C:\autobackup\databases
./zip -r backup.zip $bkdir
"Directory zipped: " + $bkdir
}
This one is a function too. We can just pass it the folder name, and it will zip it nicely up in the backup.zip file. Notice the ‘-r’ argument sent to zip.exe, that’s to make it recurse the folder and collect all the sub-directories too.
5. Upload it all
Wow! We just backed up all our DBs, and all our website folders in one zip file through Powershell. It’s resting nicely in a separate folder on our hard disk, but what good is that if we have another hard drive failure? We need to upload it our second server. At first I considered doing this from Powershell using the ftp program which is a part of Windows and then the .Net Webclient class. I even wrote some code
function Ftp-Upload {
$File = "c:\autobackup\databases"
$ftp = "ftp://userlogin:userpass@blogsvani.com/httpdocs/Server1Backup.zip"
"ftp url: $ftp"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
"Uploading $File..."
$webclient.UploadFile($uri, $File)
}
This is the webclient code, I can’t provide the ftp program code cause I deleted it, but it’s easy. Unfortunately that didn’t work for me for some reason and when I ran out of patience I decided to use that wonderful program Cobian Backup which automates backup and ftp upload. I set it up to copy my zip file to my second server using ftp everyday at a specific time.
6. Setting up Powershell to run daily
Did you miss the last piece of the puzzle? To have a completely automated backup we need to ensure that the Powershell script that we wrote so painstakingly runs daily at a certain time. We can do this using the Windows Task Scheduler. This wasn’t as easy as it was supposed to. To run the script correctly you need to run powershell, and pass it the name of the script you want to run along with some other arguments using the ‘Add Arguments’ field. Here are the arguments:
–Noninteractive –Noprofile -command "& 'c:\scripts\serverback.ps1'"
Notice that before I pass the script name I prepend ‘&’, and that I have the entire thing in double quotes. If you don’t have that your script won’t run. I found this online from another webpage helpfully provided by Kiwi Si.
Oh and yes, don’t forget to run the command – “set-executionpolicy RemoteSigned” on your Powershell command prompt once to set up the necessary permissions or the script execution won’t work.
So this finally concludes the automated backup system for our server through Powershell.
Now wasn’t that a piece of cake?