Push-FTP.ps1

function Push-FTP
{
    <#
    .Synopsis
        Pushes items to an FTP server
    .Description
        Pushes a directory to an FTP server
    .Example
        Push-Ftp -Path c:\Example -Include *.aspx
    .Link
        Get-FTP
    #>

    [OutputType([Nullable])]
    param(
    # The local path
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]$Path,    
    # If set, will only include items that match the wildcard
    [string[]]$Include,
    # If set, will exclude items that match the wildcard
    [string[]]$Exclude,
    # The FTP server
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [Uri]$FTP,
    
    # The credentail used to connect to the FTP server
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [Management.Automation.PSCredential]$Credential
    )
    
    begin {
        $progressId = Get-Random
    }
    process {
        $webClient = New-Object Net.Webclient
        $files = Get-ChildItem $path -Recurse 
        
        #region Filter the file list
        $remainingFiles = foreach ( $item in $files) {
            if ($include) {
                $found = $false
                $found =                
                    foreach ($i in $include) {
                        if ($item.Fullname -ilike "$i") {
                            $true
                            break
                        }
                    }
                if (-not $found) {
                    continue
                }
            }
            
            if ($exclude) {
                $found = $false
                $found = 
                    foreach ($ex in $exclude) {
                        if ($item.Fullname -ilike "$ex") {
                            $true
                            break
                        }
                    }
                    
                if ($found) {
                    continue
                }
            }
            $relativePath = $item.Fullname -ireplace $path.Replace("\","\\").Replace(":", "\:"),""        
            if ($item.PsIsContainer) {
                # Pre-create the directory structure. To do this, we need to use the direct FTP methods
                Write-Progress "Creating Directories" $item.Fullname -Id $progressId 
                $relativePath  = $relativePath -replace "\\", "/"    
                $url = "$ftp".TrimEnd("/")  + $relativePath
                $url = $url.TrimEnd("/") + "/"
                $ftpRequest = [Net.WebRequest]::Create($url)
                $ftpRequest.Credentials = New-Object Net.NetworkCredential $Credential.Username, $Credential.GetNetworkCredential().Password                
                $ftpRequest.Method = [Net.WebRequestMethods+Ftp]::MakeDirectory
                try { 
                    $ftpresult = $ftpRequest.GetResponse()        
                    $null = $ftpresult
                } catch {
                    Write-Verbose "Error creating directory $ftp - $($item.Fullname)"
                }
            } else {
                $item
            }
        }
        #endregion Filter the file list

        $c  = 0 
        #region Upload the files via FTP
        foreach ($f in $remainingFiles) {
            $item = $f
            $relativePath = $f.Fullname.Replace($path,"")        
            $perc = $c * 100 /@($remainingFiles).Count
            $c++
                
            Write-Progress "Uploading Files" $item.Fullname -PercentComplete $perc -Id $progressId 
            # Upload the file
            $relativePath  = $relativePath -replace "\\", "/"
            $domainSubpath = $Credential.GetNetworkCredential().Domain
            $domainSubpath = "$ftp".Substring(("$ftp".IndexOf($domainSubpath) + $domainSubpath.Length))
            $domainSubpath  = $domainSubpath.TrimEnd("/") + $relativePath
            
            
            $url = "ftp://$($Credential.GetNetworkCredential().Username):$($Credential.GetNetworkCredential().Password)@$($Credential.GetNetworkCredential().Domain)$domainSubpath"                    
            try { 
                # Net.Webclient has the most clean way to actually upload files, regardless of strangeness in the underyling server.
                $webClient.UploadFile($url, "$($item.Fullname)")
            } catch{
                Write-Verbose "Error uploading $($item.Fullname) to $domainSubpath"                        
            }
                
        }   
        #endregion Upload the files via FTP
        
    }
    
    end {
        Write-Progress "Uploading Files" "Completed" -Completed -Id $progressId 
    }   
}