Website via FTP with removal
Here's a script for uploading a website via ftp, then deleting the old files.
Some notes on using this:
Some notes on using this:
- The website will go down for a very short period between renaming the current directory and renaming the new directory
- If the upload fails then the current website is untouched, but the new temporary directory remains on the FTP server
#Inputs: #$ftpDomain = MyDomain.com #$ftpUserName = User01 #$ftpPassword = ******** # 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 ($null -eq $ftpDomain) { $errors = $errors + ", ftpDomain" } if ($null -eq $ftpUserName) { $errors = $errors + ", ftpUserName" } if ($null -eq $ftpPassword) { $errors = $errors + ", ftpPassword" } if (0 -ne $errors.length) { # Throw an error and stop deployment Throw ($errors.trim(", ") + " must be set to push to ftp site") } $webrootDir = "wwwroot" $guid1 = [guid]::NewGuid() $guid2 = [guid]::NewGuid() $ftpUrl = "ftp://$ftpDomain/$webrootDir" $ftpTemp1 = "ftp://$ftpDomain/$guid1" $ftpTemp2 = "ftp://$ftpDomain/$guid2" # Upload the files Function FtpUpload ([string]$webroot, [string]$localdirectory) { cd $localdirectory $files = Get-ChildItem -recurse FTPCreateFolder (New-Object System.Uri($webroot)) foreach($file in $files) { #Ignore the nuget package used for deployment if ($file.Name -eq "$RedGatePackageNameAndVersion.nupkg") { continue } $uri = New-Object System.Uri($webroot + $file.FullName.Replace($localdirectory,"")) # if a folder if( $file.DirectoryName -eq $null) { Write-Host "Create folder, URI: $uri" FtpCreateFolder $uri } else { Write-Host "Upload file, URI: $uri, File: " + $file.FullName FtpUploadFile $uri $file.FullName } } } Function FtpUploadFile ([System.Uri]$uri, [string]$filePath) { $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) $ftp.UseBinary = $true $ftp.UsePassive = $true $content = [System.IO.File]::ReadAllBytes($filePath) $ftp.ContentLength = $content.Length $rs = $ftp.GetRequestStream() $rs.Write($content, 0, $content.Length) $rs.Close() $rs.Dispose() } Function FtpCreateFolder ([System.Uri]$uri) { $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::MakeDirectory $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) try { $rs = $ftp.GetResponse() } catch [System.Exception] { } if( $rs -ne $null ) { $rs.Close() } } # Reads the entire stream in one go and return it # instead of reading stream line by line Function Receive-Stream ([System.IO.Stream]$reader) { $encoding = [System.Text.Encoding]::GetEncoding( $null ) $output = "" [byte[]]$buffer = new-object byte[] 4096 [int]$total = [int]$count = 0 do { $count = $reader.Read($buffer, 0, $buffer.Length) $output += $encoding.GetString($buffer, 0, $count) } while ($count -gt 0) $reader.Close() $output } # Delete the directory $path # Start by recursively deleting files inside # then remove the directory itself Function FtpDelete ([string]$path) { $itemsOnFTPtoDelete = New-Object System.Collections.ArrayList $uri = New-Object System.Uri($path) $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) try { $rs = $ftp.GetResponse() $cache = (Receive-Stream $rs.GetResponseStream()) # Split up the cache into a useable format with an item on each line $list = $cache -replace "(?:.|\n)*<PRE>\s+((?:.*|\n)+)\s+</PRE>(?:.|\n)*",'$1' -split "`n" # Convert the list of items into a useable format foreach($line in $list) { $null, $null, [string]$length, $name = $line -split '\s+' $name = ($name -join " ") if ($name.Length -eq 0) { continue } $ftpItem = New-Object PSObject -Property @{ FullName = $path.trim().trim('/') + '/' + $name Type = $(if($length -eq (($length -as [int]) -as [string])) { "File" } else { $length }) } [void]$itemsOnFTPtoDelete.Add($ftpItem) } $rs.Close() # Delete each file, recursively deleting directories foreach ($item in $itemsOnFTPtoDelete) { $uri = New-Object System.Uri($item.FullName) if ("<DIR>" -ne $item.Type) { FtpDeleteFile $uri } else { FtpDelete $item.FullName } } FtpDeleteEmptyDirectory (New-Object System.Uri($path)) } catch { } } Function FtpDeleteFile ([System.Uri]$uri) { Write-Host "Deleting $uri" $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$frpPassword) try { $rs = $ftp.GetResponse() } catch [System.Exception] { } if( $rs -ne $null ) { $rs.Close() } } Function FtpDeleteEmptyDirectory ([System.Uri]$uri) { Write-Host "Deleting directory $uri" $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp = [System.Net.FtpWebRequest]$ftp $ftp.Method = [System.Net.WebRequestMethods+Ftp]::RemoveDirectory $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) try { $rs = $ftp.GetResponse() } catch [System.Exception] { } if( $rs -ne $null ) { $rs.Close() } } Function FtpRename ([System.Uri]$uri, [string]$newname) { Write-Host "Renaming $uri to $newname" $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::Rename $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) $ftp.RenameTo = $newname try { $rs = $ftp.GetResponse() } catch [System.Exception] { } if( $rs -ne $null ) { $rs.Close() } } # Checks the files in $webroot versus $localdirectory # recursively called for directories Function FtpValidate ([string]$webroot, [string]$localdirectory) { Write-Host "Validating directory $localdirectory was uploaded" $itemsOnFTP = New-Object System.Collections.ArrayList cd $localdirectory $uploadedFiles = Get-ChildItem $uri = New-Object System.Uri($webroot) $ftp = [System.Net.FtpWebRequest]::Create($uri) $ftp.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails $ftp.Credentials = new-object System.Net.NetworkCredential($ftpUserName,$ftpPassword) try { # Get the items stored on the ftp at the webroot location $rs = $ftp.GetResponse() $cache = (Receive-Stream $rs.GetResponseStream()) $list = $cache -replace "(?:.|\n)*<PRE>\s+((?:.*|\n)+)\s+</PRE>(?:.|\n)*",'$1' -split "`n" foreach($line in $list) { $null, $null, [string]$length, $name = $line -split '\s+' $name = ($name -join " ").Trim() if ($name.Length -eq 0) { continue } $ftpItem = New-Object PSObject -Property @{ Name = $name FullName = $webroot.trim().trim('/') + '/' + $name Length = $(if($length -eq (($length -as [int]) -as [string])) { [int]$length } else { $null }) Type = $(if($length -eq (($length -as [int]) -as [string])) { "File" } else { $length }) } [void]$itemsOnFTP.Add($ftpItem) } $rs.Close() } catch {} foreach ($file in $uploadedFiles) { # ignore the package used for deployment if ($file.Name -eq "$RedGatePackageNameAndVersion.nupkg") { continue } # Check each item has been uploaded correctly Write-Host "Validating item: $file" $found = $null foreach ($ftpItem in $itemsOnFTP) { if ($ftpItem.Name -eq $file.Name) { $found = $ftpItem } } # If it doesn't exist on ftp then error if ($found -eq $null) { throw "$file failed to uploaded" } # if a folder if( $file.DirectoryName -eq $null) { # check the internals are the same FtpValidate $found.FullName $file.FullName } else { # check the file lengths are equal if ($found.Length -ne $file.Length) { throw "$file was not uploaded correctly" } } } } $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" FtpRename (New-Object System.Uri($ftpUrl)) $guid2 FtpRename (New-Object System.Uri($ftpTemp1)) $webrootDir Write-Host "Delete old files" FtpDelete $ftpTemp2
Peter Gerrard
Software Engineer
Redgate Software
Software Engineer
Redgate Software