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