Red Gate forums :: View topic - Website via FTP with removal
Return to www.red-gate.com RSS Feed Available

Search  | Usergroups |  Profile |  Messages |  Log in  Register 
Go to product documentation

Deployment Manager PowerShell Scripts forum

Website via FTP with removal

Search in Deployment Manager PowerShell Scripts forum
Post new topic   Reply to topic
Jump to:  
Author Message
PeterG



Joined: 20 Aug 2013
Posts: 4

PostPosted: Wed Aug 21, 2013 4:30 pm    Post subject: Website via FTP with removal Reply with quote

Here's a script for uploading a website via ftp, then deleting the old files.

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


Code:

#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
Intern - Deployment Manager
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic All times are GMT + 1 Hour
Page 1 of 1

 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group