MS SQL databases restore with TFS Team Build and PowerShell

Currently I’m working on a web project with a few environments like Dev, POC, QA, UAT, Staging, etc. Once per sprint we have to synchronize our DBs from Staging to other servers to make sure all environments are on the same page.

To achieve this, a new build definition was created in TFS which is similar to the one described in my “Run Batch File From TFS” post. All that build does is invoke a PowerShell script.

I’ll describe the restore process for our QA server. The PowerShell script has to do the following things:

1. Call the “iisreset /stop” command on the QA server

2. Restore DBs on the SQL server

3. Call the “iisreset /start” command on the QA server

Let’s take a closer look at these steps.

Steps 1 and 3 simply invoke commands.

The challenge is to call commands on a remote server while the script is running on a local build server. PowerShell allows remote calls to be made using the
Invoke-Command cmdlet.

Now the ‘iisreset’ command will look like this:

# credentials
$userName = "user_name"
$password = convertto-securestring "user_passwd" -asplaintext -force
# servers
$QAserverName = "QAServwrName"
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $userName, $password

# should be invoked on QA server
Invoke-Command -computername $QAserverName -Credential $Cred -Authentication Credssp -ScriptBlock {

    write-host "Stopping QA server"
    iisreset /stop

}

Unfortunately, my first remote run failed and displayed this message:

Connecting to remote server failed with the following error
message : The WinRM client cannot process the request. A computer policy does n
ot allow the delegation of the user credentials to the target computer. Use gpe
dit.msc and look at the following policy: Computer Configuration -> Administrat
ive Templates -> System -> Credentials Delegation -> Allow Delegating Fresh Cre
dentials. Verify that it is enabled and configured with an SPN appropriate for
the target computer. For example, for a target computer name "myserver.domain.
com", the SPN can be one of the following: WSMAN/myserver.domain.com or WSMAN/*
.domain.com. For more information, see the about_Remote_Troubleshooting Help to

To fix this problem, I enabled credential delegation on the build server:

PS C:\> enable-wsmancredssp -role client -delegatecomputer server_name

PS C:\> enable-wsmancredssp -role server

2. DB Restore

I used Microsoft.SqlServer.Management.Smo for the DB restore and had to call the script on the remote server by again using the Invoke-Command cmdlet.

# should be invoked on QA server
Invoke-Command -computername $QAserverName -Credential $Cred -Authentication Credssp -ScriptBlock {

# SQL credentials 
 $userNameSQL = "user"
 $passwordSQL = "passwd"

 # paths
 $backupsFromAdminDir = "\\some_backup_dir"
 $tempSQLServerDir = "D:\Temp\"

 # master database info
 $masterDBName = "QA_Master" 
 $masterMDFLogicalFileName = "Sitecore.Master.Data"
 $masterMDFPhysicalFileName = "D:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\QA_Master.mdf"
 $masterLDFLogicalFileName = "Sitecore.Master.Log"
 $masterLDFPhysicalFileName = "D:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\QA_Master_1.ldf"


 $adminMasterDBNameMask = "ADMIN_Master*.bak"
 $adminWebDBNameMask = "ADMIN_Web*.bak"

 [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")

 # copy find te latest bkp file
 write-host "Selecting BAK files on Admin server"
 # master
 $MasterDB = Get-ChildItem -Path $backupsFromAdminDir -Filter $adminMasterDBNameMask | Sort CreationTime -Descending | Select -First 1
 $MasterDBLocalPath = $tempSQLServerDir+$MasterDB.Name
 write-host "--Master DB: " $MasterDB.Name
 # web

 #copy bkp file to local SQL server
 write-host "Copying BAK files to SQL server"
 # create temp dir
 New-Item -ItemType Directory -Force -Path $tempSQLServerDir | Out-Null
 Copy-Item -Path $MasterDB.FullName -Destination $tempSQLServerDir -Force 

 
 # connecting to SQL server
 write-host "Connecting to SQL server"
 $server = new-object ("Microsoft.SqlServer.Management.Smo.Server") "(local)"
 $server.ConnectionContext.LoginSecure=$false;
 $server.ConnectionContext.set_Login($userNameSQL);
 $server.ConnectionContext.set_Password($passwordSQL)

 # restore master DB
 $server.KillAllProcesses($masterDBName)
 $Database = $server.Databases.Item($masterDBName)
 write-host "Restoring " $Database.Name " database on SQL server..."
 $dbRestore = new-object ("Microsoft.SqlServer.Management.Smo.Restore")
 $dbRestore.Database = $masterDBName
 $dbRestore.Devices.AddDevice($MasterDBLocalPath , "File")
 $dbRestoreFile = new-object("Microsoft.SqlServer.Management.Smo.RelocateFile")
 $dbRestoreLog = new-object("Microsoft.SqlServer.Management.Smo.RelocateFile")
 $dbRestoreFile.LogicalFileName = $masterMDFLogicalFileName
 $dbRestoreFile.PhysicalFileName = $masterMDFPhysicalFileName 
 $dbRestoreLog.LogicalFileName = $masterLDFLogicalFileName
 $dbRestoreLog.PhysicalFileName = $masterLDFPhysicalFileName
 $dbRestore.RelocateFiles.Add($dbRestoreFile)
 $dbRestore.RelocateFiles.Add($dbRestoreLog)
 $dbRestore.SqlRestore($server)
 write-host "Restored " $Database.Name " database on SQL server...."

 #Disconnect explicitly
 $server.ConnectionContext.Disconnect() 

 # delete master temp DB
 If ((Test-Path $MasterDBLocalPath) -and ($MasterDBLocalPath -like '*'+$adminMasterDBNameMask))
 { Remove-Item $MasterDBLocalPath -Force -Recurse}


}
This entry was posted in PowerShell, TFS and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s