Live database restoration to test server using Powershell
It is very common requirement during development stage to copy live database to test server on the daily/weekly basis. In such a scenario there is also very often a need to clean the database and perform some operations before restoring it in test environment.
For example if the live database is very big and you don’t need to have all tables on your test server, you can drop it during the copy process.
Similar applies if you need to adjust permissions, change the database name, optimize indexes or just shrink the database file.
The script below will help you automate the copy database process. On every run, it will check for the latest database backup file in defined location.
Next, it will copy that file to your local temp folder. After that, it will restore the database by giving it a temp name, so you can continue working while the restoration lasts (useful for big databases).
When the database is restored, it will drop the old test database and rename the current one to the same name. Also the old database files will be deleted to free-up the disc space. The database files will have the names like “TestDB_19_08_2014.mdf”.
As a result, you will have up-to-date live database restored on your test server.
param (
#local test datatabase server to restore your live database
[string]$TargetSQLServer= ".\MSSQLSERVER_2012",
#name of your live database
[string]$LiveSQLDatabase= "TestDB",
#name of your final test database being set after restoration
[string]$TargetSQLDatabase= "TestDB_DEV",
#data root of mssql server - containing .mdf files of your test database
[string]$TargetSQLDatabaseDataRoot= "C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER_2012\MSSQL\DATA",
#folder where current backup file will be copied and processed - must be local folder
[string]$TempLocalFolder= "C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER_2012\MSSQL\Backup_temp",
#backup location of your live database - can be located on the network drive
[string]$BackupLiveFolder = "C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER_2012\MSSQL\Backup",
#show copy progress or copy in bacground
[bool]$silentCopying = 0,
#log location
[string]$Logfile = "C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER_2012\MSSQL\Log\CopyLiveDBToTestLog.txt"
)
Function LogWrite
{
Param ([string]$logstring)
$currentdate = get-date -format "M-d-yyyy HH:mm:ss"
Add-content $Logfile -value "$currentdate | $logstring"
write-host "$currentdate | $logstring"
}
try
{
LogWrite "acquiring backup file..."
#get most recent backup file
$liveBackupFile = (dir -Path $BackupLiveFolder) | where {$_.extension -eq ".bak" } |
sort lastwritetime –Descending | select -first 1;
LogWrite "validating paths..."
if($liveBackupFile -eq $null -or -not (Test-path $liveBackupFile.FullName)){
LogWrite "Could not find the backup file, check the backup folder location!";
return;}
if(-not(Test-path $TempLocalFolder)) { LogWrite "Incorrect local backup location!"; return;}
#copy backup file to temp location
if(-not(Test-Path "$TempLocalFolder\$liveBackupFile"))
{
#remove all backups in temp location
dir -path $TempLocalFolder | Foreach-Object {
get-childitem -Path $_.FullName |
where-object {
($_.FullName.tolower().EndsWith(".bak") -and $_.FullName.tolower().contains($LiveSQLDatabase.ToLower()))
} |
Foreach-Object {
LogWrite "removing file: $_.FullName"
remove-item -path $_.FullName
}
}
LogWrite "copying backup file $liveBackupFile ..."
if(-not($silentCopying -eq 1))
{
#use shell to see progress
$FOF_CREATEPROGRESSDLG = "&H0&"
$objShell = New-Object -ComObject "Shell.Application"
$objFolder = $objShell.NameSpace($TempLocalFolder)
$objFolder.CopyHere($liveBackupFile.FullName, $FOF_CREATEPROGRESSDLG)
}
else
{
copy-item -Path $liveBackupFile.FullName -Destination "$TempLocalFolder\$liveBackupFile" -PassThru -Verbose
}
LogWrite "copying backup file $liveBackupFile finished"
}
else
{
LogWrite "backup file already exists: $TempLocalFolder\$liveBackupFile"
}
#----------------------------------------------------------
LogWrite "connecting to $TargetSQLServer"
$TargetSQLDatabase_temp = $TargetSQLDatabase + "_"+ (get-date).tostring("yy_MM_dd");
$mdfCurrentPath = "$TargetSQLDatabaseDataRoot\$TargetSQLDatabase_temp.mdf"
if(Test-Path $mdfCurrentPath)
{
LogWrite "database $TargetSQLDatabase_temp.mdf already exists!"
return;
}
LogWrite "restoring database from file: $TempLocalFolder\$liveBackupFile to temp database $TargetSQLDatabase_temp ..."
$DBLogicalFileName = $LiveSQLDatabase;
$DBLogicalFileName_log = $LiveSQLDatabase + "_Log";
$query = "RESTORE DATABASE [$TargetSQLDatabase_temp] FROM
DISK = N'$TempLocalFolder\$liveBackupFile' WITH FILE = 1,
MOVE N'$DBLogicalFileName' TO N'$TargetSQLDatabaseDataRoot\"+$TargetSQLDatabase_temp+".mdf',
MOVE N'$DBLogicalFileName_log' TO N'$TargetSQLDatabaseDataRoot\"+$TargetSQLDatabase_temp+".ldf',
NORECOVERY, NOUNLOAD, REPLACE, STATS = 10";
SQLCMD -S $TargetSQLServer -E -Q $query
Start-Sleep -s 10
#bring it to live from restoring mode
$query = "RESTORE DATABASE "+$TargetSQLDatabase_temp+" WITH RECOVERY"
SQLCMD -S $TargetSQLServer -E -Q $query
LogWrite "dropping old $TargetSQLDatabase database..."
#drop current database
$query = "ALTER DATABASE [$TargetSQLDatabase]
SET OFFLINE WITH ROLLBACK IMMEDIATE
DROP DATABASE [$TargetSQLDatabase]"
SQLCMD -S $TargetSQLServer -E -Q $query
Start-Sleep -s 10
LogWrite "renaming temp database $TargetSQLDatabase_temp to $TargetSQLDatabase ..."
#rename temp database
$query = "ALTER DATABASE ["+$TargetSQLDatabase_temp+"] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
ALTER DATABASE ["+$TargetSQLDatabase_temp+"] MODIFY NAME = [$TargetSQLDatabase]
ALTER DATABASE [$TargetSQLDatabase] SET MULTI_USER"
SQLCMD -S $TargetSQLServer -E -Q $query
LogWrite "recreating db permissions..."
#recreate permissions
$query = "ALTER AUTHORIZATION ON DATABASE::[$TargetSQLDatabase] TO [sa]
use [$TargetSQLDatabase]
ALTER User [domain\Administrators] with LOGIN = [domain\Administrators]"
#enable if used
#SQLCMD -S $TargetSQLServer -E -Q $query
LogWrite "cleaning and shrinking database $TargetSQLDatabase ..."
$query = "use $TargetSQLDatabase;
ALTER DATABASE $TargetSQLDatabase SET RECOVERY SIMPLE;
truncate table dbo.TestTable;
DBCC SHRINKFILE('$DBLogicalFileName_Log');
DBCC SHRINKFILE('$DBLogicalFileName')"
SQLCMD -S $TargetSQLServer -E -Q $query
LogWrite "optimizing table indexes..."
$query = "USE [$TargetSQLDatabase];
EXEC [dbo].[IndexOptimize]
@IndexFragmentationMin = 5,
@IndexFragmentationRebuildMin = 30,
@skiplargetables = 0,
@PrintInformationOnly = 0"
#enable if used
#SQLCMD -S $TargetSQLServer -E -Q $query
LogWrite "removing old database files..."
#remove all old database files
$fileEntries = [IO.Directory]::GetFiles("$TargetSQLDatabaseDataRoot");
$todayDatePart = (get-date).tostring("yy_MM_dd");
foreach($fileName in $fileEntries)
{
if($fileName.tolower().contains("$TargetSQLDatabase".tolower()) -and -not($fileName.tolower().contains($todayDatePart)))
{
LogWrite $fileName
#remove-item -path $fileName
}
}
LogWrite "Done!"
}
catch {
$msg = "Unexpected Error. Error details: $_.Exception.Message"
LogWrite $msg
Write-Host $msg -foreground Red
}
Enjoy!
CopyLiveDBToTest





