Cryptolocker Honeypot FileSystemWatcher - powershell

I am a novice when it comes to scripting so please bear with me. I am trying to create a script that will monitor a bait file that is added to all file shares on a server. When the script sees that the file was modified it will block access to the user that made the modification and send an email. The script seems to work ok other than the FileSystemWatcher. It will only monitor the last share. I have seen a similar post on here but was getting confused with the answer. Can someone please help me with the task of creating a FileSystemWatcher for each bait file? I would also like any input as to how I might improve upon the script in other ways. Your help is greatly appreciated.
$to = "joe#blow.com"
$File = "test.txt"
$FilePath = "C:\temp"
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
## SEND MAIL FUNCTION
function sendMail($s, $to) {
$smtpServer = "mail.nowhere.com"
$smtpFrom = "alert#nowhere.com"
$smtpTo = $to
$messageSubject = $s[0]
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $false
$message.Body = $s[1]
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
## Get a list of shares and Perform tasks on each location.
$cryptopaths = Get-WmiObject -Class win32_share -filter "Type=0 AND name like '%[^$]'" | ForEach ($_.Path) {
$cryptopath = $_.Path
## Copy the bait file to the share location
Copy $FilePath\$File $cryptopath\$File -Force
##Get files hash
Try {
$Origin = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$FilePath\$File")))
$Copy = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))
}
##Error on reading hash
Catch {
echo "error reading $CryptoPath\$File"
}
## If files don't match, then Send messaged and quit
if (Compare-Object $Origin $Copy){
## files don't match
$subject = "Error logged on $CryptoPath\$File by $env:username on $env:computername"
$body = "The original file does not match the witness file. Aborting monitor script."
$email =#($subject,$body)
sendMail -s $email -to "ben22#nowhere.com"
Exit
}
## CREATE WATCHER ON DIRECTORY
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $CryptoPath
$watcher.Filter = $File
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $false
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
}
## Execute Watcher
while($TRUE){
$result = $watcher.WaitForChanged([System.IO.WatcherChangeTypes]::Changed `
-bor [System.IO.WatcherChangeTypes]::Renamed `
-bor [System.IO.WatcherChangeTypes]::Deleted `
-bor [System.IO.WatcherChangeTypes]::Created, 1000);
if ($result.TimedOut){
continue;
}
if ($result.Name -eq $File) {
### Make sure the files do not match
try {
$FileCheck = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes("$CryptoPath\$File")))
if (Compare-Object $Origin $FileCheck){
## files don't match
$body = "Witness file $FilePath\$File on $env:computername has been modified."
}
}
catch {
## file deleted
$body = "Witness file $FilePath\$File on $env:computername has been deleted"
}
finally {
## Deny owner of changed file access to shares and disconnect their open sessions. Send email alert
Get-Acl "$CryptoPath\$File" | foreach ($_.Owner) {
Get-SmbShare | Block-SmbShareAccess –AccountName $_.Owner
Close-SmbSession –ClientUserName $_.Owner
}
$subject = "EMERGENCY ON FILE SERVER -- $FilePath\$File by $env:username on $env:computername"
$email =#($subject,$body)
sendMail -s $email -to "ben22#nowhere.com"
sendMail -s $email -to "5555555555#txt.bell.ca"
Exit
}
}
}

The problem is that you create FileSystemWatcher instances in a loop (ForEach ($_.Path) {}), but you assign them to the same variable $watcher, overwriting the previous reference each time. Once outside the loop, you work with the $watcher variable, which references the last FileSystemWatcher instance you created and that's why you are receiving notifications for the last file only.
To get this working, you should use a type that allows storing multiple references -- that is, an array. Example:
$watchers = #();
...
$watcher = New-Object System.IO.FileSystemWatcher;
...
$watchers += $watcher;
Also, I would propose to use event handler/callback/delegate-based approach instead of waiting for a change using WaitForChanged(), because waiting for multiple file system watchers would call for a parallelized solution (well, ideally). Use Register-ObjectEvent to register an event handler and see this example in particular: http://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b.
PowerShellPack also has a Start-FileSystemWatcher cmdlet that wraps this all up nicely, but I'm not sure about the status of PowerShellPack in general. It should be part of the Windows 7/8 Resource Kit, though.

Related

PowerShell: Check a folder for changes and if so, send an e-mail

I have a colleague with loads of creative ideas, but I've got no talent with PowerShell. He wants a folder to be checked regularly for changes. As soon as a new file has been added to the folder, he wants to be notified via mail. So, I guess I need a PS script for that.
I have absolutely no idea on how to do this, though.
I found the following code - can this be altered to do the job?
Param (
[string]$Path = "C:\Test",
[string]$SMTPServer = "SMTP IP",
[string]$From = "MyMail#domain.com",
[string]$To = "MyMail#domain.com",
[string]$Subject = "New stuff!"
)
$SMTPMessage = #{
To = $To
From = $From
Subject = "$Subject at $Path"
Smtpserver = $SMTPServer
}
$File = Get-ChildItem $Path | Where { $_.LastWriteTime -ge [datetime]::Now.AddMinutes(-1) }
If ($File) {
$SMTPBody = "`nThe following files have recently been added/changed:`n`n"
$File | ForEach { $SMTPBody += "$($_.FullName)`n" }
Send-MailMessage #SMTPMessage -Body $SMTPBody
}
Any help would be appreciated.
To get notified about changes on a particular folder, and/or its sub items, one can use .Net Frameworks FileSystemWatcher class. Instantiate a new one with PowerShell using:
$pattern = "*.*"
$watcherProperties = #{
IncludeSubdirectories = $false;
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$watcher = New-Object IO.FileSystemWatcher $Path, $pattern -Property #watcherProperties
Then, to get notified about changes, register an event using Register-ObjectEvent and add your mail block:
Register-ObjectEvent $watcher Created -SourceIdentifier FileCreated -Action {
$SMTPBody = "`nThe following file have recently been added/changed:`n`n$($Event.SourceEventArgs.Name)"
Send-MailMessage #SMTPMessage -Body $SMTPBody}
}
While this is a nice solution to get automatically notified, without heavy loops, it also has some drawbacks. In this configuration you would get one mail for every file changed.
Moving directories with bunches of files would result in - bunches of mails.
You can read more about FileSystemWatchers here:
Script Center: PowerShell FileSystemWatcher - Script by Bigteddy
Tracking Changes to a Folder Using PowerShell - Blog article by Boe Prox

PowerShell script to find log files more than 10 MB and send mail whenever a new file generated in that folder

I need to find new files in a folder which are over 10 MB in size, and then send a mail with the name of the files.
The tricky part: Mail should be send when there is a new file arrives in the folder, so I have to always keep track and differentiate between the old file and new file.
Problem: I have written the following code and am not able to build the logic for mail. How can I identify the new file comes and trigger the mail?
$namearray = #()
$n = gci 'C:\Users\RF\local\ReuseLibrary\FamilySaveDirectory' | % {get-item $.FullName| ? { $.length -gt 10mb }}
foreach($a in $n) {
$namearray += $a.name
}
$namearray
Send-MailMessage -To *#gmail.com -From '****#*.com' -Subject "Add User for $namearray NX License" -Body "Script execute $namearray successfully.." -SmtpServer 'mail.****.de'
As already pointed out in the comments, you can use the FileSystemWatcher object to monitor your filesystem. It got a little more tricky than just using the object, because you also need to be able to get the files which are already in the folder, not only the newly created ones.
Should the script really trigger the mail every time it has a new item in the $namearray? Maybe you'll get a flood of e-mails then.
It's better to use a function instead of a script to do your task because you have more flexibility, so you can check for only newly created files, and also for all files inside your folder.
Please try the function. I couldn't test it very well, because I don't have a folder where 10 MB large files get created all the time.
Usage:
To get all files (the wildcard * in the filter is important!):
Get-Files -filter '*.txt' -folder 'C:\yourfolder' -AllFiles
To monitor only new created files:
Get-Files -filter '*.txt' -folder 'C:\yourfolder' -NewFiles
Function:
Please edit the Send-MailMessage part so that you will receive the mail on your account.
function Get-Files() {
param(
[string]$filter,
[string]$folder,
[Parameter(ParameterSetName='AllFiles')]
[switch]$AllFiles,
[Parameter(ParameterSetName='NewFiles')]
[switch]$NewFiles
)
# Preparing the Name array
[string[]]$namearray = #()
if(!$NewFiles.IsPresent) {
# Getting all files which are already inside the
# folder and more than 10 MB
$files = gci $folder | % { get-item $_.FullName |
? { $_.length -gt 10mb -and $_.Extension -like $filter} } |
% { $namearray += $_.FullName }
# Send E-Mail
$secondnamearray = $namearray | out-string
Send-MailMessage -To '*#gmail.com' -From '****#*.com' -Subject "Add User for NameArray NX License" -Body $secondnamearray -SmtpServer 'mail.****.de'
$namearray = ""
}
# Monitoring of the Files
$monitoring = New-Object System.IO.FileSystemWatcher
$monitoring.Filter = $filter
$monitoring.path = $folder
$monitoring.EnableRaisingEvents = $true
$event = Register-ObjectEvent -InputObject $monitoring -EventName Created -Action {
# Checking for filesize
$x = (get-item $eventArgs.FullPath).length / 10mb
if ($x -ge 1) {
Write-Host "New Item found: $($eventArgs.FullPath). Sending E-Mail!"
$namearray += $eventArgs.FullPath
$newnamearray = $namearray | out-string
Send-MailMessage -To '*#gmail.com' -From '****#*.com' -Subject "Add User for NameArray NX License" -Body $newnamearray -SmtpServer 'mail.****.de'
$namearray = ""
}
}
}

Change $smtpTo for email based on if statement

Inside my register event action I have an if statement that checks to see if the paths match, if they do I set the email to $smtpTo to the proper email address. But I get an error "The parameter 'to' cannot be an empty string. I know the paths are correct as they Write to console in the if statement.
$MonitorFolder = Get-Content "C:\Desktop\ScanFTPDeptClients\OutgoingPathlist.txt"
$MonitorStopFile = "monitor.die"
$smtpServer = "mail.test.org"
$smtpFrom = "SYSTEMFUNCTION#test.org"
$smtpSubject = "Completed files have arrived in FTP"
$smtpTo= ""
$SourceID = "MonitorFiles"
foreach ($path in $MonitorFolder){
$i+=1
$watcher = New-Object System.IO.FileSystemWatcher $path
#Files only. Default is files + directory
$watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName,LastWrite'
#Using a thread-safe collection (in global scope so Action-block can reach it) to store the log just to be safe.
$global:newFiles = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
$newFileSubscription = Register-ObjectEvent $watcher Created -SourceIdentifier $i+"NewFileCreated" -Action {
Write-Host "New file named '$($Event.SourceEventArgs.Name)' arrived in $(split-path $Event.SourceEventArgs.FullPath)"
#Check the path
$deptClient= "$(split-path $Event.SourceEventArgs.FullPath)"
#set the email based on folder path
if ("$($deptClient)" -eq "\\vavm\FTP\C\O\RuthWebster"){
Write-host "$($deptClient)"
$smtpTo = "test#test.org"
}
#add files to content of body email
$global:newFiles.Add("`n[$(Get-Date -Format HH:mm:ss)]`t $($Event.SourceEventArgs.Name)has been completed and arrived in $(split-path $Event.SourceEventArgs.FullPath) ")
if($Event.SourceEventArgs.Name -eq $MonitorStopFile) {
Write-Host "Monitoring stopped"
#Stop monitoring
Unregister-Event -SubscriptionId $newFileSubscription.Id
#Dispose FileSystemWatcher
$watcher.Dispose()
}
}
}
$smtp = New-Object -TypeName "Net.Mail.SmtpClient" -ArgumentList $smtpServer
while ($watcher.EnableRaisingEvents -or $global:newFiles.Count -gt 0) {
#Sleep
Start-Sleep -Seconds 10
if($global:newFiles.Count -gt 0) {
#Convert list of strings to single string (multiline)
$smtpbody = $global:newFiles
$smtp.Send($smtpFrom, $smtpTo, $smtpSubject, $smtpBody)
#Mail sent, Empty array
$global:newFiles.Clear()
}
}
Try with $smtpTo as a global variable. Replace all $smtpTo with $global:smtpTo.
Btw. you don't need to wrap split-path in a string and subexpression. Try:
$deptClient= split-path $Event.SourceEventArgs.FullPath
#set the email based on folder path
if ($deptClient -eq "\\vavm\FTP\C\O\RuthWebster"){
Write-host $deptClient
$smtpTo = "test#test.org"
}

Monitor and set an alert for Invalid file names

I want to monitor the particular Folder where people are placing the excel sheets for some activities.
I want to make sure that people should take care of the below rules before placing the excel sheets.
The excel sheet should be in xls format (Excel 97-2003 Workboomk Format)
The excel file name should not contain any space
The length of the file name should not exceed 15 characters.
I want to set a email alert to me if the users placed a excel sheet without following the above 3 rules.
I need a VB or Powershell script or batch to monitor the folder. Kindly help me on this.
This sounds like two steps. Step 1, write a section of code that can be run to check files for the correct conditions. Step 2, email users the list of incorrectly named files. Step 1 is fairly simple.
Dim fso, scanFolder, files, n, fileList, fileArray
Set fso = CreateObject("Scripting.FileSystemObject")
Set scanFolder = fso.GetFolder("C:\Your\Folder\Here")
Set files = scanFolder.Files
fileList = ""
for each n in files
if len(n.name) > 15 or right(n.name, 3) <> "xls" or InStr(n.name, " ") > 0 then
fileList = fileList & n.name & "|"
end if
next
fileList = left(fileList, len(fileList) - 1)
fileArray = split(fileList, "|")
This will grab a list of all inappropriately named files.
Step 2. Unfortunately, as far as I know, there is no way for an FSO to get the owner name out of file. Best you can do is email a distribution list with the list of files named in error. I hope this helps.
Edit: this is the email code I use in my scripts.
Dim objEmail, strFileList, x
strFileList = ""
for each x in fileArray
strFileList = strFileList & x & vbNewLine
next
Set objEmail = CreateObject("CDO.Message")
with objEmail
.From = "From#email.com"
.To = ""
.CC = ""
.Subject = "List of wrong files"
.Body = strFileList
.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = 'you will need to get the code for this from IT'
.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
.Configuration.Fields.Update
end with
objEmail.Send
I have wrapped the functionality of FileSystemWatcher into a module and provided a small sample of how that can be used for your requirements.
# Module FileSystemWatcher
<#
.SYNOPSIS
Register to receive filesystemwatcher events for a file or folder.
#>
function Register-FileSystemEvent
{
[CmdLetBinding(DefaultParameterSetName="File")]
param
(
[Parameter(Mandatory=$true,Position=0,ParameterSetName="File")]
[string]$path,
[Parameter(Mandatory=$true,Position=0,ParameterSetName="Folder")]
[string]$folder,
[Parameter(Mandatory=$false,Position=1,ParameterSetName="Folder")]
[string]$filter="*.*",
[Parameter(Mandatory=$true,Position=2)]
[string]$event,
[scriptblock]$action={
# Action code here
$sourceidentifier = $Event.SourceIdentifier
$FileName = $Event.SourceEventArgs.FullPath
$EventType = $Event.SourceEventArgs.ChangeType
$EventTime = $Event.TimeGenerated
$message = ("File {0} {1} at {2:yyyyMMdd HH:mm:ss}" -f $FileName, $EventType, $EventTime)
Msg ([environment]::UserName) $message
},
$MessageData
)
if ($PSCMdLet.ParameterSetName -eq "File")
{
$folder = $path.Replace(([System.IO.Path]::GetFileName($path)), "")
$filter = [System.IO.Path]::GetFileName($path)
}
$watcher = New-Object System.IO.FileSystemWatcher -ArgumentList $folder, $filter
$uniqueId = ("{0:yyyyMMddHHmmss}" -f [DateTime]::Now)
$sourceIdentifier = ("File_{0}_{1}" -f $event, $uniqueId)
$job = Register-ObjectEvent -InputObject $watcher -EventName $event -SourceIdentifier $sourceIdentifier -Action $action -MessageData $MessageData
$Script:ActiveWatchers += #{$sourceIdentifier = $job}
}
<#
.SYNOPSIS
Unregister a FileSystem event or All
.PARAMETER All
Unregisters all events
#>
function Unregister-FileSystemEvent
{
param
(
$id,
[switch]$All
)
if ($All.IsPresent)
{
foreach ($item in $Script:ActiveWatchers.Keys)
{
Unregister-Event $item
Remove-Job $item
}
$Script:ActiveWatchers = #{}
}
else
{
Unregister-Event $id
Remove-Job $id
$Script:ActiveWatchers.Remove($id)
}
}
function Init
{
if ($Script:ActiveWatchers.Count -gt 0){Unregister-FileSystemEvent -All}
$Script:ActiveWatchers = #{}
}
Init
Export-ModuleMember -Function Register-FileSystemEvent, Unregister-FileSystemEvent `
-Variable ActiveWatchers
The basic idea with this module is the ability to specify a scriptblock to be executed once the file event is fired. The -MessageData is used to attach more data to the event once it is fired. In below sample it is used to send the smtpserver and the credentials.
Below the sample code to make use of the fileSystemWatcher module and send an email with the results every time a file is created.
Please note that the arguments to Send-MailMessage might not be exactly what is necessary for your mail server, but is what i used to send the email using office365 server.
# Demo using FileSystemWatcher to validate created files
Import-Module .\FileSystemWatcher
$smtpserver = Read-Host -Prompt "Specify your smtp server"
$mailcred = Get-Credential -Message "Specify credentials for your smtp server"
$eventid = Register-FileSystemEvent -folder C:\users\Jower\OneDrive\Documents -event Created -MessageData #{SmtpServer = $smtpserver;Credentials = $mailcred} -action {
function ValidateFile($filename)
{
$messages = #()
if ([System.IO.Path]::GetExtension($filename) -ne ".xls")
{
$messages += ("{0}: Only excel version 97 files allowed here" -f $filename)
}
if ([System.IO.Path]::GetFileName($filename).Contains(" "))
{
$messages += ("{0}: Spaces are not allowed in filename" -f $filename)
}
if ($filename.Length -gt 15)
{
$messages += ("{0}: Filenames of more than 15 characters is not allowed" -f $filename)
}
return $messages
}
$smtpserver = $Event.MessageData.SmtpServer
$credentials = $Event.MessageData.Credentials
$messages = ValidateFile $Event.SourceEventArgs.Name
if ($messages.Count -gt 0)
{
Send-MailMessage -From "filealerts#domain.com" -To "recipient#domain.com" -Subject "File validation" -SmtpServer $smtpserver -Body ([string]::Join("`n", $messages)) -port 587 -UseSsl -Credential $credentials
}
}
# To disable events again.
# Unregister-FileSystemEvent -id $eventid
# Can also use
# Unregister-FileSystemEvent -All

Upload files with FTP using PowerShell

I want to use PowerShell to transfer files with FTP to an anonymous FTP server. I would not use any extra packages. How?
I am not sure you can 100% bullet proof the script from not hanging or crashing, as there are things outside your control (what if the server loses power mid-upload?) - but this should provide a solid foundation for getting you started:
# create the FtpWebRequest and configure it
$ftp = [System.Net.FtpWebRequest]::Create("ftp://localhost/me.png")
$ftp = [System.Net.FtpWebRequest]$ftp
$ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftp.Credentials = new-object System.Net.NetworkCredential("anonymous","anonymous#localhost")
$ftp.UseBinary = $true
$ftp.UsePassive = $true
# read in the file to upload as a byte array
$content = [System.IO.File]::ReadAllBytes("C:\me.png")
$ftp.ContentLength = $content.Length
# get the request stream, and write the bytes into it
$rs = $ftp.GetRequestStream()
$rs.Write($content, 0, $content.Length)
# be sure to clean up after ourselves
$rs.Close()
$rs.Dispose()
There are some other ways too. I have used the following script:
$File = "D:\Dev\somefilename.zip";
$ftp = "ftp://username:password#example.com/pub/incoming/somefilename.zip";
Write-Host -Object "ftp url: $ftp";
$webclient = New-Object -TypeName System.Net.WebClient;
$uri = New-Object -TypeName System.Uri -ArgumentList $ftp;
Write-Host -Object "Uploading $File...";
$webclient.UploadFile($uri, $File);
And you could run a script against the windows FTP command line utility using the following command
ftp -s:script.txt
(Check out this article)
The following question on SO also answers this: How to script FTP upload and download?
I'm not gonna claim that this is more elegant than the highest-voted solution...but this is cool (well, at least in my mind LOL) in its own way:
$server = "ftp.lolcats.com"
$filelist = "file1.txt file2.txt"
"open $server
user $user $password
binary
cd $dir
" +
($filelist.split(' ') | %{ "put ""$_""`n" }) | ftp -i -in
As you can see, it uses that dinky built-in windows FTP client. Much shorter and straightforward, too. Yes, I've actually used this and it works!
Easiest way
The most trivial way to upload a binary file to an FTP server using PowerShell is using WebClient.UploadFile:
$client = New-Object System.Net.WebClient
$client.Credentials =
New-Object System.Net.NetworkCredential("username", "password")
$client.UploadFile(
"ftp://ftp.example.com/remote/path/file.zip", "C:\local\path\file.zip")
Advanced options
If you need a greater control, that WebClient does not offer (like TLS/SSL encryption, etc), use FtpWebRequest. Easy way is to just copy a FileStream to FTP stream using Stream.CopyTo:
$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials =
New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$fileStream.CopyTo($ftpStream)
$ftpStream.Dispose()
$fileStream.Dispose()
Progress monitoring
If you need to monitor an upload progress, you have to copy the contents by chunks yourself:
$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials =
New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$buffer = New-Object Byte[] 10240
while (($read = $fileStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
$ftpStream.Write($buffer, 0, $read)
$pct = ($fileStream.Position / $fileStream.Length)
Write-Progress `
-Activity "Uploading" -Status ("{0:P0} complete:" -f $pct) `
-PercentComplete ($pct * 100)
}
$ftpStream.Dispose()
$fileStream.Dispose()
Uploading folder
If you want to upload all files from a folder, see
PowerShell Script to upload an entire folder to FTP
I recently wrote for powershell several functions for communicating with FTP, see https://github.com/AstralisSomnium/PowerShell-No-Library-Just-Functions/blob/master/FTPModule.ps1. The second function below, you can send a whole local folder to FTP. In the module are even functions for removing / adding / reading folders and files recursively.
#Add-FtpFile -ftpFilePath "ftp://myHost.com/folder/somewhere/uploaded.txt" -localFile "C:\temp\file.txt" -userName "User" -password "pw"
function Add-FtpFile($ftpFilePath, $localFile, $username, $password) {
$ftprequest = New-FtpRequest -sourceUri $ftpFilePath -method ([System.Net.WebRequestMethods+Ftp]::UploadFile) -username $username -password $password
Write-Host "$($ftpRequest.Method) for '$($ftpRequest.RequestUri)' complete'"
$content = $content = [System.IO.File]::ReadAllBytes($localFile)
$ftprequest.ContentLength = $content.Length
$requestStream = $ftprequest.GetRequestStream()
$requestStream.Write($content, 0, $content.Length)
$requestStream.Close()
$requestStream.Dispose()
}
#Add-FtpFolderWithFiles -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/somewhere/" -userName "User" -password "pw"
function Add-FtpFolderWithFiles($sourceFolder, $destinationFolder, $userName, $password) {
Add-FtpDirectory $destinationFolder $userName $password
$files = Get-ChildItem $sourceFolder -File
foreach($file in $files) {
$uploadUrl ="$destinationFolder/$($file.Name)"
Add-FtpFile -ftpFilePath $uploadUrl -localFile $file.FullName -username $userName -password $password
}
}
#Add-FtpFolderWithFilesRecursive -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/" -userName "User" -password "pw"
function Add-FtpFolderWithFilesRecursive($sourceFolder, $destinationFolder, $userName, $password) {
Add-FtpFolderWithFiles -sourceFolder $sourceFolder -destinationFolder $destinationFolder -userName $userName -password $password
$subDirectories = Get-ChildItem $sourceFolder -Directory
$fromUri = new-object System.Uri($sourceFolder)
foreach($subDirectory in $subDirectories) {
$toUri = new-object System.Uri($subDirectory.FullName)
$relativeUrl = $fromUri.MakeRelativeUri($toUri)
$relativePath = [System.Uri]::UnescapeDataString($relativeUrl.ToString())
$lastFolder = $relativePath.Substring($relativePath.LastIndexOf("/")+1)
Add-FtpFolderWithFilesRecursive -sourceFolder $subDirectory.FullName -destinationFolder "$destinationFolder/$lastFolder" -userName $userName -password $password
}
}
Here's my super cool version BECAUSE IT HAS A PROGRESS BAR :-)
Which is a completely useless feature, I know, but it still looks cool \m/ \m/
$webclient = New-Object System.Net.WebClient
Register-ObjectEvent -InputObject $webclient -EventName "UploadProgressChanged" -Action { Write-Progress -Activity "Upload progress..." -Status "Uploading" -PercentComplete $EventArgs.ProgressPercentage } > $null
$File = "filename.zip"
$ftp = "ftp://user:password#server/filename.zip"
$uri = New-Object System.Uri($ftp)
try{
$webclient.UploadFileAsync($uri, $File)
}
catch [Net.WebException]
{
Write-Host $_.Exception.ToString() -foregroundcolor red
}
while ($webclient.IsBusy) { continue }
PS. Helps a lot, when I'm wondering "did it stop working, or is it just my slow ASDL connection?"
You can simply handle file uploads through PowerShell, like this.
Complete project is available on Github here https://github.com/edouardkombo/PowerShellFtp
#Directory where to find pictures to upload
$Dir= 'c:\fff\medias\'
#Directory where to save uploaded pictures
$saveDir = 'c:\fff\save\'
#ftp server params
$ftp = 'ftp://10.0.1.11:21/'
$user = 'user'
$pass = 'pass'
#Connect to ftp webclient
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
#Initialize var for infinite loop
$i=0
#Infinite loop
while($i -eq 0){
#Pause 1 seconde before continue
Start-Sleep -sec 1
#Search for pictures in directory
foreach($item in (dir $Dir "*.jpg"))
{
#Set default network status to 1
$onNetwork = "1"
#Get picture creation dateTime...
$pictureDateTime = (Get-ChildItem $item.fullName).CreationTime
#Convert dateTime to timeStamp
$pictureTimeStamp = (Get-Date $pictureDateTime).ToFileTime()
#Get actual timeStamp
$timeStamp = (Get-Date).ToFileTime()
#Get picture lifeTime
$pictureLifeTime = $timeStamp - $pictureTimeStamp
#We only treat pictures that are fully written on the disk
#So, we put a 2 second delay to ensure even big pictures have been fully wirtten in the disk
if($pictureLifeTime -gt "2") {
#If upload fails, we set network status at 0
try{
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
} catch [Exception] {
$onNetwork = "0"
write-host $_.Exception.Message;
}
#If upload succeeded, we do further actions
if($onNetwork -eq "1"){
"Copying $item..."
Copy-Item -path $item.fullName -destination $saveDir$item
"Deleting $item..."
Remove-Item $item.fullName
}
}
}
}
You can use this function :
function SendByFTP {
param (
$userFTP = "anonymous",
$passFTP = "anonymous",
[Parameter(Mandatory=$True)]$serverFTP,
[Parameter(Mandatory=$True)]$localFile,
[Parameter(Mandatory=$True)]$remotePath
)
if(Test-Path $localFile){
$remoteFile = $localFile.Split("\")[-1]
$remotePath = Join-Path -Path $remotePath -ChildPath $remoteFile
$ftpAddr = "ftp://${userFTP}:${passFTP}#${serverFTP}/$remotePath"
$browser = New-Object System.Net.WebClient
$url = New-Object System.Uri($ftpAddr)
$browser.UploadFile($url, $localFile)
}
else{
Return "Unable to find $localFile"
}
}
This function send specified file by FTP.
You must call the function with these parameters :
userFTP = "anonymous" by default or your username
passFTP = "anonymous" by default or your password
serverFTP = IP address of the FTP server
localFile = File to send
remotePath = the path on the FTP server
For example :
SendByFTP -userFTP "USERNAME" -passFTP "PASSWORD" -serverFTP "MYSERVER" -localFile "toto.zip" -remotePath "path/on/the/FTP/"
Goyuix's solution works great, but as presented it gives me this error: "The requested FTP command is not supported when using HTTP proxy."
Adding this line after $ftp.UsePassive = $true fixed the problem for me:
$ftp.Proxy = $null;
Simple solution if you can install curl.
curl.exe -p --insecure "ftp://<ftp_server>" --user "user:password" -T "local_file_full_path"