Competition: What’s your favorite Redgate tool? Enter now.

Windows Azure Web Site

DavidRGDavidRG Posts: 60 Bronze 4
This is a Deployment Manager Deploy.ps1 PowerShell script to deploy a Windows Azure Web Site.

To use it, you will need to both add some variables in DM, as well as having configured the machine where the DM agent runs, in the user account that the DM agent run as, to have your credentials to the Windows Azure Web Site so that it can stop + start the website.

In more detail, the variables are:
    * RedGateAzureFtpServer * RedGateAzureFtpUserName * RedGateAzureFtpPassword
The manual setup that you'll need to do on the DM agent for the user account that the agent runs as is:
    * Install "Windows Azure PowerShell" from
http://www.windowsazure.com/en-us/downloads/
* Add git to %PATH%
* Get-AzurePublishSettingsFile
* Import-AzurePublishSettingsFile <filename>


Then you'll need this in a Deploy.ps1 file in your package:
#Inputs:
#$RedGateAzureFtpServer = waws-prod-db3-003.ftp.azurewebsites.windows.net
#$RedGateAzureFtpUserName = User01
#$RedGateAzureFtpPassword = ********

# Uploads the contents of the deployed package to the given ftp site, then delete the old files

# This is done by uploading to a temporary file
# checking the upload worked
# and then swap the directories to `deploy` the website
# the finally delete the old wwwroot

$errors = ""
if &#40;$null -eq $RedGateAzureFtpServer&#41; &#123;
	$errors = $errors + ", RedGateAzureFtpServer"
&#125;
if &#40;$null -eq $RedGateAzureFtpUserName&#41; &#123;
	$errors = $errors + ", RedGateAzureFtpUserName"
&#125;
if &#40;$null -eq $RedGateAzureFtpPassword&#41; &#123;
	$errors = $errors + ", RedGateAzureFtpPassword"
&#125;

if &#40;0 -ne $errors.length&#41; &#123;
# Throw an error and stop deployment
	Throw &#40;$errors.trim&#40;", "&#41; + " must be set to push to ftp site"&#41;
&#125;

$webrootDir = "site/wwwroot"
$guid1 = &#91;guid&#93;::NewGuid&#40;&#41;
$guid2 = &#91;guid&#93;::NewGuid&#40;&#41;
$ftpUrl = "ftp://$RedGateAzureFtpServer/$webrootDir"
$ftpTemp1 = "ftp://$RedGateAzureFtpServer/$guid1"
$azureWebsite = $RedGateAzureFtpUserName.split&#40;'\'&#41;&#91;0&#93;
$ftpTemp2 = "ftp://$RedGateAzureFtpServer/$guid2"
$oldWebrootDir = "/$guid2"

# Upload the files
Function FtpUpload &#40;&#91;string&#93;$webroot, &#91;string&#93;$localdirectory&#41;
&#123;
	cd $localdirectory
	$files = Get-ChildItem -recurse

	FTPCreateFolder &#40;New-Object System.Uri&#40;$webroot&#41;&#41; 

	foreach&#40;$file in $files&#41;
	&#123;
		#Ignore the nuget package used for deployment
		if &#40;$file.Name -eq "$RedGatePackageNameAndVersion.nupkg"&#41; &#123;
			continue
		&#125;
	
    	$uri = New-Object System.Uri&#40;$webroot + $file.FullName.Replace&#40;$localdirectory,""&#41;&#41;
		
    	# if a folder
    	if&#40; $file.DirectoryName -eq $null&#41;
    	&#123;
	        Write-Host "Create folder, URI: $uri"
        	FtpCreateFolder $uri
    	&#125;
    	else
    	&#123;
        	Write-Host "Upload file, URI: $uri, File: " + $file.FullName
        	FtpUploadFile $uri $file.FullName
    	&#125;
	&#125;
&#125;

Function FtpUploadFile &#40;&#91;System.Uri&#93;$uri, &#91;string&#93;$filePath&#41;
&#123;
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::UploadFile
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
    $ftp.UseBinary = $true
    $ftp.UsePassive = $true
    $content = &#91;System.IO.File&#93;::ReadAllBytes&#40;$filePath&#41;
    $ftp.ContentLength = $content.Length
    $rs = $ftp.GetRequestStream&#40;&#41;
    $rs.Write&#40;$content, 0, $content.Length&#41;
    $rs.Close&#40;&#41;
    $rs.Dispose&#40;&#41;
&#125;

Function FtpCreateFolder &#40;&#91;System.Uri&#93;$uri&#41;
&#123;
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::MakeDirectory
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
    try
    &#123;
        $rs = $ftp.GetResponse&#40;&#41;
    &#125;
    catch &#91;System.Exception&#93;
    &#123;
    &#125;
    if&#40; $rs -ne $null &#41;
    &#123;
        $rs.Close&#40;&#41;
    &#125;
&#125;

# Reads the entire stream in one go and return it
# instead of reading stream line by line
Function Receive-Stream &#40;&#91;System.IO.Stream&#93;$reader&#41;
&#123;
	$encoding = &#91;System.Text.Encoding&#93;::GetEncoding&#40; $null &#41;
	$output = ""
	&#91;byte&#91;&#93;&#93;$buffer = new-object byte&#91;&#93; 4096
	&#91;int&#93;$total = &#91;int&#93;$count = 0
	do
	&#123;
		$count = $reader.Read&#40;$buffer, 0, $buffer.Length&#41;
		$output += $encoding.GetString&#40;$buffer, 0, $count&#41;
	&#125; while &#40;$count -gt 0&#41;
 
	$reader.Close&#40;&#41;
	$output
&#125;

# Delete the directory $path
# Start by recursively deleting files inside
# then remove the directory itself
Function FtpDelete &#40;&#91;string&#93;$path&#41;
&#123;
	$itemsOnFTPtoDelete = New-Object System.Collections.ArrayList
	
	$uri = New-Object System.Uri&#40;$path&#41;
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::ListDirectoryDetails
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
	
	try
	&#123;
		$rs = $ftp.GetResponse&#40;&#41;
		$cache = &#40;Receive-Stream $rs.GetResponseStream&#40;&#41;&#41;
		
		# Split up the cache into a useable format with an item on each line
		$list = $cache -replace "&#40;?:.|\n&#41;*&lt;PRE&gt;\s+&#40;&#40;?:.*|\n&#41;+&#41;\s+&lt;/PRE&gt;&#40;?:.|\n&#41;*",'$1' -split "`n"

		# Convert the list of items into a useable format
		foreach&#40;$line in $list&#41; &#123;
			$null, $null, &#91;string&#93;$length, $name = $line -split '\s+'
			$name = &#40;$name -join " "&#41;
			
			if &#40;$name.Length -eq 0&#41; &#123;
				continue
			&#125;

			$ftpItem = New-Object PSObject -Property @&#123;
				FullName      = $path.trim&#40;&#41;.trim&#40;'/'&#41; + '/' + $name
				Type          = $&#40;if&#40;$length -eq &#40;&#40;$length -as &#91;int&#93;&#41; -as &#91;string&#93;&#41;&#41; &#123; "File" &#125; else &#123; $length &#125;&#41;
			&#125;
		
			&#91;void&#93;$itemsOnFTPtoDelete.Add&#40;$ftpItem&#41;
   		&#125;

		$rs.Close&#40;&#41;
		
		# Delete each file, recursively deleting directories
		foreach &#40;$item in $itemsOnFTPtoDelete&#41; &#123;
			$uri = New-Object System.Uri&#40;$item.FullName&#41;
			if &#40;"&lt;DIR&gt;" -ne $item.Type&#41; &#123;
				FtpDeleteFile $uri
			&#125; else &#123;
				FtpDelete $item.FullName
			&#125;
		&#125;
		FtpDeleteEmptyDirectory &#40;New-Object System.Uri&#40;$path&#41;&#41;
	&#125;
	catch
	&#123;
	&#125;
&#125;

Function FtpDeleteFile &#40;&#91;System.Uri&#93;$uri&#41;
&#123;
	Write-Host "Deleting $uri"
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::DeleteFile
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$frpPassword&#41;
	
    try
    &#123;
        $rs = $ftp.GetResponse&#40;&#41;
    &#125;
    catch &#91;System.Exception&#93;
    &#123;
    &#125;
    if&#40; $rs -ne $null &#41;
    &#123;
        $rs.Close&#40;&#41;
    &#125;
&#125;

Function FtpDeleteEmptyDirectory &#40;&#91;System.Uri&#93;$uri&#41;
&#123;
	Write-Host "Deleting directory $uri"
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp = &#91;System.Net.FtpWebRequest&#93;$ftp
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::RemoveDirectory
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
	
    try
    &#123;
        $rs = $ftp.GetResponse&#40;&#41;
    &#125;
    catch &#91;System.Exception&#93;
    &#123;
    &#125;
    if&#40; $rs -ne $null &#41;
    &#123;
        $rs.Close&#40;&#41;
    &#125;
&#125;

Function FtpRename &#40;&#91;System.Uri&#93;$uri, &#91;string&#93;$newname&#41;
&#123;
	Write-Host "Renaming $uri to $newname"
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::Rename
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
	$ftp.RenameTo = $newname
	
    try
    &#123;
        $rs = $ftp.GetResponse&#40;&#41;
    &#125;
    catch &#91;System.Exception&#93;
    &#123;
    &#125;
    if&#40; $rs -ne $null &#41;
    &#123;
        $rs.Close&#40;&#41;
    &#125;
&#125;

# Checks the files in $webroot versus $localdirectory
# recursively called for directories
Function FtpValidate &#40;&#91;string&#93;$webroot, &#91;string&#93;$localdirectory&#41;
&#123;
	Write-Host "Validating directory $localdirectory was uploaded"
	$itemsOnFTP = New-Object System.Collections.ArrayList
	cd $localdirectory
	$uploadedFiles = Get-ChildItem
	
	$uri = New-Object System.Uri&#40;$webroot&#41;
    $ftp = &#91;System.Net.FtpWebRequest&#93;::Create&#40;$uri&#41;
    $ftp.Method = &#91;System.Net.WebRequestMethods+Ftp&#93;::ListDirectoryDetails
    $ftp.Credentials = new-object System.Net.NetworkCredential&#40;$RedGateAzureFtpUserName,$RedGateAzureFtpPassword&#41;
	
	try
	&#123;
		# Get the items stored on the ftp at the webroot location
		$rs = $ftp.GetResponse&#40;&#41;
		$cache = &#40;Receive-Stream $rs.GetResponseStream&#40;&#41;&#41;
		
		$list = $cache -replace "&#40;?:.|\n&#41;*&lt;PRE&gt;\s+&#40;&#40;?:.*|\n&#41;+&#41;\s+&lt;/PRE&gt;&#40;?:.|\n&#41;*",'$1' -split "`n"

		foreach&#40;$line in $list&#41; &#123;
			$null, $null, &#91;string&#93;$length, $name = $line -split '\s+'
			$name = &#40;$name -join " "&#41;.Trim&#40;&#41;
			
			if &#40;$name.Length -eq 0&#41; &#123;
				continue
			&#125;

			$ftpItem = New-Object PSObject -Property @&#123;
				Name		  = $name
				FullName      = $webroot.trim&#40;&#41;.trim&#40;'/'&#41; + '/' + $name
				Length        = $&#40;if&#40;$length -eq &#40;&#40;$length -as &#91;int&#93;&#41; -as &#91;string&#93;&#41;&#41; &#123; &#91;int&#93;$length &#125; else &#123; $null &#125;&#41;
				Type          = $&#40;if&#40;$length -eq &#40;&#40;$length -as &#91;int&#93;&#41; -as &#91;string&#93;&#41;&#41; &#123; "File" &#125; else &#123; $length &#125;&#41;
			&#125;
			
			&#91;void&#93;$itemsOnFTP.Add&#40;$ftpItem&#41;
   		&#125;

		$rs.Close&#40;&#41;
	&#125; catch &#123;&#125;
	foreach &#40;$file in $uploadedFiles&#41; &#123;
		# ignore the package used for deployment
		if &#40;$file.Name -eq "$RedGatePackageNameAndVersion.nupkg"&#41; &#123;
			continue
		&#125;
		# Check each item has been uploaded correctly
		
		Write-Host "Validating item: $file"
		
		$found = $null
		foreach &#40;$ftpItem in $itemsOnFTP&#41; &#123;
			if &#40;$ftpItem.Name -eq $file.Name&#41; &#123; $found = $ftpItem &#125;
		&#125;
		# If it doesn't exist on ftp then error
		if &#40;$found -eq $null&#41; &#123;
			throw "$file failed to uploaded"
		&#125;
		
    	# if a folder
    	if&#40; $file.DirectoryName -eq $null&#41;
    	&#123;
			# check the internals are the same
	        FtpValidate $found.FullName $file.FullName
    	&#125; else &#123;
			# check the file lengths are equal
			if &#40;$found.Length -ne $file.Length&#41; &#123;
				throw "$file was not uploaded correctly"
			&#125;
		&#125;
	&#125;
&#125;

$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path
Write-Host "Uploading new files"
FtpUpload $ftpTemp1 $myDir
Write-Host "Validating upload"
FtpValidate $ftpTemp1 $myDir
Write-Host "Validation Successful"
Write-Host "Swapping to new website"
Stop-AzureWebsite $azureWebsite
FtpRename &#40;New-Object System.Uri&#40;$ftpUrl&#41;&#41; $oldWebrootDir
FtpRename &#40;New-Object System.Uri&#40;$ftpTemp1&#41;&#41; $webrootDir
Start-AzureWebsite $azureWebsite
Write-Host "Delete old files"
FtpDelete $ftpTemp2
Sign In or Register to comment.