Restoring Windows Azure Pack: Web Sites

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:

1.   Restore the Certificate Share

2.   Restore the WebSites Share

3.   Reapply ACLs if necessary

4.   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 ='http://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:

1.   Import the WebSites module

2.   Update the credentials

3.   Remove the old server names from the farm

4.   Add the new servers to the proper farms

1.   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.

2.   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.

3.   For each old server name <OldName> that is no longer used, run:

Remove-WebSitesServer -Name <OldName>

4.   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