Restoring Windows Azure Pack: Web Sites
Applies To: Windows Azure Pack
It is highly recommended that you restore to servers that have the same names and administrative accounts as they did during the backup. For the restore to be successful, the File Server and SQL Server must be the exact same in terms of configuration, users, and permissions as they were during the backup. When restoring your Web Sites service, use the following order:
1. Restore SQL Server databases
2. Restore the File Server
3. Restore the Web Sites Controller
4. Run a repair on all Roles
You can use scripts to perform the restore operations. The steps are described below in detail.
1. Restore SQL Server databases
Restore the SQL server Hosting, Resource Metering, and master databases. The SQL server must have the same name as when it was backed up.
Sample SQL Restore script
The following example script is provided for illustrative purposes only and is unsupported. The script that you create must be run with administrative privileges.
Note
This script is not supported by Microsoft.
param ([string] $backupUser = "Administrator", $backupPassword, $sqlServer, $sqlUser = "sa", $sqlPassword, $backupLocation = "\\backupMachine\c$\backup" )
net use $backupLocation /user:$backupUser $backupPassword
xcopy /Y /q \\$backupMachine\c$\$backupLocation\Hosting.bak C:\HostingOfflineFeed\
xcopy /Y /q \\$backupMachine\c$\$backupLocation\ResourceMetering.bak C:\HostingOfflineFeed\
xcopy /Y /q $backupLocation\master.bak C:\HostingOfflineFeed\
net start "SQL Server (MSSQLSERVER)" /f
sqlcmd -S $sqlServer -U $sqlUser -P $sqlPassword -Q "RESTORE DATABASE [master] FROM DISK='C:\HostingOfflineFeed\master.bak' WITH REPLACE"
net stop "SQL Server (MSSQLSERVER)"
net start "SQL Server (MSSQLSERVER)"
sqlcmd -S $sqlServer -U $sqlUser -P $sqlPassword -Q "RESTORE DATABASE [Hosting] FROM DISK='C:\HostingOfflineFeed\Hosting.bak' WITH REPLACE"
sqlcmd -S $sqlServer -U $sqlUser -P $sqlPassword -Q "RESTORE DATABASE [ResourceMetering] FROM DISK='C:\HostingOfflineFeed\ResourceMetering.bak' WITH REPLACE"
del C:\HostingOfflineFeed\Hosting.bak
del C:\HostingOfflineFeed\ResourceMetering.bak
del C:\HostingOfflineFeed\master.bak
2. Restore the File Server
The File Server must have the same name that it had when it was backed up. You should restore the File Server components in the following order:
Restore the Certificate Share
Restore the WebSites Share
Reapply ACLs if necessary
Reapply FSRM quotas on the WebSites share
Two sample scripts are provided: one for steps a through c above, and one to reapply the FSRM quotas on the WebSites share.
Sample File Server Restore script
The following example script includes steps a-c above (it does not restore the FSRM quotas). The script is provided for illustrative purposes only and is unsupported. The script that you create must be run with administrative privileges.
Note
This script is not supported by Microsoft.
param ([string] $backupUser = "Administrator", $backupPassword, $certificateFolder = "C:\Certificates", $websiteFolder = "C:\websites", $backupLocation = "\\backupMachine\c$\backup" )
net use $backupLocation /user:$backupUser $backupPassword
mkdir $certificateFolder
mkdir $websiteFolder
xcopy /Y /q /E $backupLocation\ $certificateFolder
xcopy /Y /q /E $backupLocation\ $websiteFolder
Sample script to restore FSRM quotas
The following example script restores the FSRM quotas. The script is provided for illustrative purposes only and is unsupported. The script that you create must be run with administrative privileges.
Note
This script is not supported by Microsoft.
param ([string] $backupUser = "Administrator", $backupPassword, $backupLocation = "\\backupMachine\c$\backup" )
net use $backupLocation /user:$backupUser $backupPassword
xcopy /Y /q $backupLocation\templates.xml C:\templates.xml
dirquota template import /File:C:\templates.xml
net stop srmReports
net stop srmSvc
net stop quota
net stop Datascrn
robocopy $backupLocation\SRM "C:\System Volume Information\SRM" /E /ZB /R:3 /W:5
net start Datascrn
net start quota
net start srmSvc
net start srmReports
3. Restore the Web Sites Controller
In order to restore the Web Sites Controller, you can use the Restore.ps1 PowerShell script presented in this section.
Copy the Restore.ps1 script onto the Web Sites Controller, and then run the following command with Administrator privileges:
net use /Y $backupLocation /user:$backupMachineAdmin $backupMachinePassword
.\Restore.ps1 $backupLocation $encryptionKey
Note
The $encryptionKey flag is only needed if you used it during backup.
The Restore.ps1 script follows.
## Re-install and restore the controller from a backup
param ($backupPath,$password)
function ShowHelp
{
Write-Host '===================== RESTORE.PS1 HELP ====================='
Write-Host 'This is a script that restores based on a backup from the Hosting VSS writer'
Write-Host 'Invoke it using .\Restore.ps1'
Write-Host 'It can also be invoked as follows:'
Write-Host '.\Restore.ps1 <backup path (e.g. \\backupmachine\C$\backuplocation)> <password for keys file>'
Write-Host ("Note: before running this script you may need to run:`r`n" + ' "net use /Y <backup path> /user:<username> <password>"')
Write-Host '============================================================'
}
function CreateFeedWebAppIfNeeded ([string] $localFeedLocation)
{
import-module WebAdministration
$app = Get-WebApplication -Name HostingOfflineFeed
if ($app -eq $null)
{
New-WebApplication -Name HostingOfflineFeed -Site 'Default Web Site' -PhysicalPath $localFeedLocation -ApplicationPool DefaultAppPool -Force
}
# Add mime types needed for downloading .msp files for offline installations
$msp = Get-WebConfiguration //staticContent/* | where {$_.fileExtension -eq '.msp'}
if ($msp -eq $null)
{
Add-WebConfiguration //staticContent -Value @{fileExtension=".msp";mimeType="application/octet-stream"}
}
# Add mime types needed for downloading .msu files for offline installations
$msu = Get-WebConfiguration //staticContent/* | where {$_.fileExtension -eq '.msu'}
if ($msu -eq $null)
{
Add-WebConfiguration //staticContent -Value @{fileExtension=".msu";mimeType="application/octet-stream"}
}
}
function InstallController ([string]$offlineFeedUrl, [string]$customFeed)
{
$WebPiCmd = ([System.Environment]::ExpandEnvironmentVariables("%ProgramW6432%\Microsoft\Web Platform Installer\WebpiCmd.exe"))
if (!(Test-Path $WebPiCmd))
{
$WebPiCmd = Join-Path -Path $localFeedLocation -ChildPath "bin\WebpiCmd.exe"
}
Invoke-Command -ScriptBlock { & $WebPiCmd /Install /Products:HostingController /AcceptEula /XML:$offlineFeedUrl /SuppressReboot /Log:HostingController.log }
if ($lastexitcode -ne $null -And $lastexitcode -ne 0)
{
Write-Host "ERROR: There was a problem installing using WebPI!"
exit $lastexitcode
}
}
function DecodeBase64($string)
{
return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($string))
}
function DecryptString($EncryptedFile, $Passphrase, $salt, $init)
{
$encryptedStrings = (Get-Content $EncryptedFile)
$ret = @()
foreach ($Encrypted in $encryptedStrings)
{
# If the value in the Encrypted is a string, convert it to Base64
if($Encrypted -is [string])
{
$Encrypted = [Convert]::FromBase64String($Encrypted)
}
# Create a COM Object for RijndaelManaged Cryptography
$r = new-Object System.Security.Cryptography.RijndaelManaged
# Convert the Passphrase to UTF8 Bytes
$pass = [Text.Encoding]::UTF8.GetBytes($Passphrase)
# Convert the Salt to UTF Bytes
$salt = [Text.Encoding]::UTF8.GetBytes($salt)
# Create the Encryption Key using the passphrase, salt and SHA1 algorithm at 256 bits
$r.Key = (new-Object Security.Cryptography.PasswordDeriveBytes $pass, $salt, "SHA1", 5).GetBytes(32) #256/8
# Create the Intersecting Vector Cryptology Hash with the init
$r.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($init) )[0..15]
# Create a new Decryptor
$d = $r.CreateDecryptor()
# Create a New memory stream with the encrypted value.
$ms = new-Object IO.MemoryStream @(,$Encrypted)
# Read the new memory stream and read it in the cryptology stream
$cs = new-Object Security.Cryptography.CryptoStream $ms,$d,"Read"
# Read the new decrypted stream
$sr = new-Object IO.StreamReader $cs
# Return from the function the stream
$ret += $sr.ReadToEnd()
# Stops the stream
$sr.Close()
# Stops the crypology stream
$cs.Close()
# Stops the memory stream
$ms.Close()
# Clears the RijndaelManaged Cryptology IV and Key
$r.Clear()
}
return $ret
}
if ($backupPath -and $backupPath.Contains('/?'))
{
ShowHelp
return
}
Write-Host 'Starting the hosting restore process. Run with /? to see help.'
Write-Host ("Note: before running this script you may need to run:`r`n" + ' "net use /Y <backupPath> /user:<username> <password>"')
# argument parsing
if (!$backupPath)
{
$backupPath = Read-Host "Please enter the name of the backup path (e.g. \\backupmachine\C$\backuplocation)"
}
if (!$password)
{
$password = Read-Host "Please enter the password of the keys file" -AsSecureString
$password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
}
$systemDrive = [System.Environment]::ExpandEnvironmentVariables('%systemdrive%\')
# Fetch restore data from remote machine
$localFeedLocation = ($systemDrive +'HostingOfflineFeed\')
$c = 0
do
{
$c++
"D" | xcopy /q /Y (Join-Path -Path $backupPath -ChildPath "HostingOfflineFeed") "$localFeedLocation" /E
} while ($c -lt 10 -and !$?)
# Install the IIS cmdlets
$dismLocation = Join-Path -Path $systemDrive -ChildPath 'Windows\System32\dism.exe'
& $dismLocation /online /enable-feature /featurename:IIS-ManagementScriptingTools /all
CreateFeedWebAppIfNeeded $localFeedLocation
Stop-Service ResourceMetering -ErrorAction SilentlyContinue
# install webpi
$wpi = (dir ($systemDrive + 'hostingofflinefeed\installers\HostingWebPlatformInstaller') -r -i 'wpi.msi').DirectoryName
if ($wpi.Count -gt 1)
{
$wpi = $wpi[0]
}
$wpi = Join-Path -Path $wpi -ChildPath "wpi.msi"
msiexec /quiet /i $wpi
$offlineFeedUrl = 'https://localhost/HostingOfflineFeed/feeds/latest/WebSites0.9.0.xml'
InstallController $offlineFeedUrl
$keys = DecryptString (Join-Path -Path $backupPath -ChildPath 'encryptedkeys.txt') $password 'salt12345' 'init12345'
Stop-Service WebFarmService -ErrorAction SilentlyContinue
Add-PSSnapIn WebHostingSnapIn
# Restore the keys
# Keys are Base64 encoded
Set-ControllerConnectionString -ConnectionString (DecodeBase64($keys[0])) 3>$null
# Set-MeteringConnectionString -MeteringConnectionString (DecodeBase64($keys[1])) -ServerName (HostName)
Set-SymmetricKey -SymmetricKeyName SystemCore -SymmetricKey (DecodeBase64($keys[1])) 3>$null
Set-SymmetricKey -SymmetricKeyName SiteRuntime -SymmetricKey (DecodeBase64($keys[2])) 3>$null
Set-MeteringConnectionString -MeteringConnectionString ([Microsoft.Web.Hosting.SiteManager]::GetMeteringConnectionString()) -ServerName (HostName) 3>$null
Start-Service WebFarmService -ErrorAction SilentlyContinue
Restoring to non-file servers with different names or administrative accounts
If you must restore a server (that is not a Filer Server or SQL Server) to a server or servers with server names or administrative accounts that are different from those of the original, you must:
Import the WebSites module
Update the credentials
Remove the old server names from the farm
Add the new servers to the proper farms
First, before running any of the other commands, import the WebSites module:
Import-Module WebSites
Now run the commands 2-4 on the Controller as Administrator.
If the credentials for the role <RoleType> have changed, run the following for each changed credential:
Set-WebSitesConfig Credential -CredentialName <RoleType> Credential -UserName <RoleAdminUser> -Password <RoleAdminPassword>
Note
Possible values for <RoleType> in the Set-WebSitesConfig command are: ManagementServer, FileServer, FrontEnd, Publisher, and Worker.
For each old server name <OldName> that is no longer used, run:
Remove-WebSitesServer -Name <OldName>
For each new server name <NewName> of role <RoleType> run this:
New-WebSitesServer -Name <NewName> -ServerType <RoleType>
Note
Possible values for <RoleType> in the New-WebSitesServer command are: ManagementServer, FileServer, LoadBalancer, Publisher, and WebWorker.
Example
If you had an old Web Worker role named "OldWorker" and a new Web Worker role called "NewWorker", and you had updated the WebWorker credentials to "WebWorkerAdmin", you would run:
Import-Module WebSites
Set-WebSitesConfig Credential -CredentialName WorkerCredential -UserName WebWorkerAdmin -Password $WebWorkerPassword
Remove-WebSitesServer -Name OldWorker
New-WebSitesServer -Name NewWorker -ServerType WebWorker
4. Run a repair on all Roles
After completing the restore, run a repair on all the roles and monitor them to verify that they come back up healthy.
See Also
Backing up Windows Azure Pack: Web Sites
Deploy Windows Azure Pack: Web Sites