Website via FTP with removal

Share your PowerShell scripts for Deployment Manager

Moderators: Mike Upton, justin.caldicott, csmith, chirayu, DavidSimner

Website via FTP with removal

Postby PeterG » Wed Aug 21, 2013 4:30 pm

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: Select all
#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
PeterG
 
Posts: 4
Joined: Tue Aug 20, 2013 10:40 am

Return to Deployment Manager PowerShell Scripts

Who is online

Users browsing this forum: Yahoo [Bot] and 0 guests