Powershell WebClient.DownloadFile not overwriting - powershell

I am trying to download a file from reporting services. The first time I run this code it works fine, but the next time it wont overwrite the first ones downloaded file. I have tried adding Remove-Item $file before I create the WebClient object, but when I do this I get the error The process cannot access the file 'D:\Work\RawMaterialCodes.xls' because it is being used by another process., the process being Powershell itself. I have tried calling $webClient.Dispose() thinking this might release the file, but no luck.
Does anyone have any ideas how I can overwrite the downloaded file and/or remove it before the next download attempt?
$reportServer = "http://localhost/ReportServer_TRITON"
$reportName = "RawMaterialCodes"
$file = "D:\Work\RawMaterialCodes.xls"
$startDate = "2014-01-22"
$endDate = "2014-01-24"
$category = "Cat1"
$destination = ""
$reportUrl = $reportServer + "?/" + $reportName + "&StartDate=" + $startDate + "&EndDate=" + $endDate + "&Category=" + $category + "&Destination=" + $destination + "&rs:Format=Excel"
$webClient = new-object System.Net.WebClient
$webClient.Credentials = New-Object System.Net.NetworkCredential($userName, $password, $domain)
$webClient.DownloadFile($reportUrl, $file)
$mailMessage = new-object System.Net.Mail.MailMessage
$mailMessage.From = $emailFrom
$mailMessage.To.Add($emailTo)
$mailMessage.Subject = $emailSubject
$mailMessage.Body = $emailBody
$attachment = new-object System.Net.Mail.Attachment($file, 'text/plain')
$mailMessage.Attachments.Add($attachment)
$smtpClient = New-Object System.Net.Mail.SmtpClient($smtpServer, 25)
$smtpClient.EnableSsl = $enableSsl
if ($smtpAuthUsername -ne "")
{
$smtpClient.Credentials = New-Object System.Net.NetworkCredential($smtpAuthUsername, $smtpAuthPassword)
}
$smtpClient.Send($mailMessage)

$mailMessage is keeping an open handle to your file. Add
$mailMessage.Dispose()
at the end of the script and you should be able to overwrite.

I don't see anything wrong with the code as you have it written. Disposing the WebClient is unnecessary here with respect to freeing up the file as it doesn't keep a handle to it. The most likely cause of this error is another piece of your code or another program which is opening the file and not properly disposing of it.
I would first assume it was my code at fault and carefully audit any other place I manipulated this file and see if I accidentally left a handle to it open

Related

Need help for ftp files status

i have been looking for a way to know the status of my ftp files, there are few log files are being uploading on my ftp server after every 15mints, but few times it fails to upload i just want an alert when ever a file fails to upload.
following code has been tired
function update {
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpsite.com/Script_Apps/install_firefox.exe")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
$response = $ftprequest.GetResponse().StatusDescription
$tokens = $response.Split(" ")
$code = $tokens[0]
$localfile = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc
if ($tokens -gt $localfile) {
write-host "Updating Firefox Installer..."
$File = "$dir\Apps\install_firefox.exe"
$ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
"Updated Firefox" >> $global:logfile
mainmenu
}
else {
Write-Host "Local Copy is Newer."
sleep 3
mainmenu
}
}
I'm going to assume this line deals with downloading/uploading files for your FTP site.
$ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
If so, then we can lookout for errors and do something when one occurs with try{}catch{} blocks.
You can put any code inside a try block, and when it hits a terminating error it will capture the error as $Error[0] which we can reference in a catch block.
try{
$ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.DownloadFile($uri, $File)
}
catch{
#Write out an error to the screen
"failed to download file $($ftp). `n--Ran into error $($Error[0].Exception.Message)"
#Todo - send a notification
}
This will give a result like this, when it fails to download.
Failed to download file ftp://ftpsite.com/Script_Apps/install_firefox.exe.
---Ran into error Exception calling "DownloadFile" with "2" argument(s):
"Unable to connect to the remote server"
Then, you can determine the best way to send a notification to suit your needs. There's way's to send e-mail from PowerShell, or Tweets, or even using a Push message to the Pushbullet app on your phone!

Powershell attached all file from folder

I need to email and attached all file from the folder but no include any sub-folder.
I am getting error
Cannot find an overload for "Add" and the argument count: "1".
At line:11 char:1
$emailMessage.Attachments.Add($getallfiles)
below is the powershell code.
$dir = "C:\Users\myid\Documents\comparsion\src\main\resources\excelfiles\Compare_Result_Files"
$getallfiles = Get-ChildItem "$dir\*.xlsx"
$emailSmtpServer = "mailhub-au.com"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "donotreply#company.com"
$emailMessage.To.Add( "myid#company.com" )
$emailMessage.Subject = "Selenium Report"
$emailMessage.IsBodyHtml = $true
$emailMessage.Body = "Test Summary Attached"
$emailMessage.Body = "$getallfiles"
$emailMessage.Attachments.Add($getallfiles)
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer ,$emailSmtpServerPort )
$SMTPClient.Send($emailMessage)`
You need to loop through the files and add one at a time.
Also the Attachments.Add() method requires the full file name, not a FileInfo object. See the docs
foreach ($file in $getallfiles) {
[void]$emailMessage.Attachments.Add($file.FulllName)
}
I would also suggest adding the -File switch to Get-ChildItem to ensure you only gather files, not folders.

Append data to a CSV file on an FTP server using PowerShell

I am trying to append data to a CSV file on an FTP server using PowerShell.
Currently my script works fine, however it is overwriting the data.
Here is the main bit of my code:
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$UserInfo = [pscustomobject]#{
UserName = $env:UserName
ComputerName = $env:computername
}
$contents =
(($UserInfo | ConvertTo-Csv -NoTypeInformation) -join [Environment]::NewLine) +
[Environment]::NewLine
$webclient.UploadString(($ftp + "/Installed.csv"), $contents)
Ideally, I'd like to do this without any native plugins or libraries. The only other thing I can think of is using something like get-content to get the current data from the file, and then the new data would be
getcontent myfile.csv + the new data from $UserInfo
Basically I am installing a VPN through group policy and would like to log who successfully installed the client. We don't have any software distribution platforms unfortunately so we're trying to use GP and FTP to perform this task.
Any suggestions are appreciated. Thank you in advance.
It's not possible with WebClient. But it can be done with FtpWebRequest and WebRequestMethods.Ftp.AppendFile.
$request = [System.Net.WebRequest]::Create("ftp://example.com/remote/path/file.txt")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::AppendFile
$ftpStream = $request.GetRequestStream()
$writer = New-Object System.IO.StreamWriter($ftpStream)
$writer.Write($contents)
$writer.Close()
$ftpStream.Dispose()

Cryptolocker Honeypot FileSystemWatcher

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.

Rename file on FTP with PowerShell

Is there a way to rename the file in a FTP directory?
I'm streaming live images from computer to FTP, but problem is that when it uploads the image to FTP it making instant replacement of a file. I want to firstly upload image with temporary name and then make a rename to live.jpg. It's gonna be like cached file uploading.
while($true)
{
$i++
$File = "c:\live\temp.jpg"
$ftp = "ftp://username:password#example.com/camera/temp.jpg"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
$webclient.UploadFile($uri, $File)
}
How can I use this in script properly ?
Rename-Item ..\camera\temp.jpg live.jpg
Thanx!
Try this:
$ftp = [System.Net.FtpWebRequest]::Create("ftp://username:password#example.com/camera/temp.jpg")
$ftp.KeepAlive = $true
$ftp.UsePassive = $true
$ftp.Method = "Rename"
$ftp.RenameTo = "camera/temp1.jpg"
$ftp.UseBinary = $true
$response = [System.Net.FtpWebResponse] $ftp.GetResponse()
$ftp = [System.Net.FtpWebRequest]::Create("ftp://username:password#example.com/camera/temp.jpg")
$ftp.KeepAlive = $true
$ftp.UsePassive = $true
$ftp.Method = "Rename"
$ftp.RenameTo = "camera/temp1.jpg"
$ftp.UseBinary = $true
$response = [System.Net.FtpWebResponse] $ftp.GetResponse()
I had to adjust the $ftp.RenameTo line and remove the directory to make it work for me:
$ftp.RenameTo = "temp1.jpg"
Thanks anyway