PowerShell function result wont be send via Email - email

I'm using this PowerShell function to check on a folder.
it works fine and when adding, changing and deleting a file happens on the folder; it shows the log message on the host screen.
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher | Get-Member -Type Properties,Event
$FileSystemWatcher.Path = "C:\Users\ali.shariaty\Desktop\test"
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
$WriteHostParams = #{
ForegroundColor = 'Green'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Changed -Action {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
$WriteHostParams = #{
ForegroundColor = 'Yellow'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Deleted -Action {
$Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath,
$Event.SourceEventArgs.ChangeType,
$Event.TimeGenerated
$WriteHostParams = #{
ForegroundColor = 'Red'
BackgroundColor = 'Black'
Object = $Object
}
Write-Host #WriteHostParams
}
My issue is when I add these lines to my code to send me the result by email, the body of my emails are empty and do not contain the log message.
$mailtxt = $WriteHostParams
$mailSmtpServer = "mail.domain.com";
$mailFrom = "ali.shariaty#domain.com";
$mailTo = "ali.shariaty#domain.com";
$mailSubject = "Folder Change"
$mailbody = $mailtxt
$mail = New-Object Net.Mail.SmtpClient($mailSmtpServer);
$msg = new-object Net.Mail.MailMessage;
$msg.IsBodyHtml = 1;
$msg.To.Add($mailTo);
$msg.From = $mailFrom;
$msg.Subject = $mailSubject;
$msg.Body = $mailbody;
$mail.Send($msg);
Can someone help and tell me what I am doing wrong?
Thank you.

Have you tried setting the body to just the message? i.e.
$mailtext = $WriteHostParams.Object
Currently you are trying to set the body of the email to a hashtable, which I would expect would just set it to "System.Collections.Hashtable" after it was casted to a string

Related

PowerShell IO.FileSystemWatcher doesn't work after restart fileserver

I need a FileSystemWatcher on a network directory (fileserver)
The script works well but fails after a reboot from the fileserver.
How can I detect if the FSW is failing and restart the watcher if the fileserver is up and running again?
Code:
$destinations = #{"\\location1" = "c:\destination1"
"\\location2" = "c:\destination2"
}
foreach ($location in $destinations.Keys) {
$Watcher = New-Object IO.FileSystemWatcher -Property #{
Path = $location
Filter = "*.*"
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier $location -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$SI = $Event.SourceIdentifier
Write-Host "The file '$name' was $changeType at $timeStamp"
Write-Host $path
Move-Item $path -Destination $destinations[$SI] -Force -Verbose
}
}
RTFM!
There is Error event from FileSystemWatcher, and there is your case exactly described.
For example, if the object is monitoring changes in a remote directory and the connection to that directory is lost, the Error event is raised.
Solution found:
$destinations = #{"\\location1" = "c:\destination1"
"\\location2" = "c:\destination2"
}
foreach ($location in $destinations.Keys) {
$Watcher = New-Object IO.FileSystemWatcher -Property #{
Path = $location
Filter = "*.*"
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$action = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$SI = $Event.SourceIdentifier
Write-Host "The file '$name' was $changeType at $timeStamp"
Write-Host $path
Move-Item $path -Destination $destinations[$SI] -Force -Verbose
}
$actionError = {
$Sender.EnableRaisingEvents = $false
$ip = $location -split "\\" | Where { $_ -ne "" } | Select -first 1
do {
Write-Host "Waiting for boot " + $ip
Start-Sleep -Seconds 5
} until (Test-Connection $ip -Quiet -Count 1)
$Sender.EnableRaisingEvents = $true
}
Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier $location -Action $action
Register-ObjectEvent $Watcher -EventName Error -SourceIdentifier $location+'ERROR' -Action $actionError
}

Run Scripts in Powershell ISE from batch file

I am executing my batch file which opens the Powershell ISE and loads the script but does nothing... I would still need to run manually. Am I missing something here?
BatchFile to call Powershell:
Powershell_ise.exe C:\Users\Desktop\myscript.ps1
That the code am using to monitor the file changes in a folder and to email:
Function Register-Watcher {
param ($folder)
$filter = "logfile.txt" #all files
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
$changeAction = [scriptblock]::Create('
# This is the code which will be executed every time a file change is detected
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file $name was $changeType at $timeStamp"
Send-email
')
Register-ObjectEvent $Watcher "Changed" -Action $changeAction
}
Function Send-email{
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "*****#gmail.com"
$Password = "*******"
$to = "*****#gmail.com"
$subject = "Email Subject"
$body = "Insert body text here"
$attachment = "C:\Users\Desktop\AccountMapping.sdl"
$message = New-Object System.Net.Mail.MailMessage
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.from = $username
$message.attachments.add($attachment)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
Write-Host "Mail Sent"
}
Register-Watcher "C:\Users\Desktop"

FileWatcher sends multiple emails with Net.Mail.MailMessage

I'm using a FileWatcher script and everything seems to be working as far as monitoring. The problem is that when a file changes, it's set to send me an email. It emails out fine but always sends 2 emails or if i'm sending to a text, 2 texts. What's going on here? Here's my code:
$fswAuctions = New-Object IO.FileSystemWatcher $auctions, $filter -Property #{IncludeSubdirectories = $true;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $fswAuctions Changed -SourceIdentifier FilesChanged4 -Action {
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$SMTPBody = "The file '$name' was $changeType at $timeStamp"
Send-ToEmail -email "myemail#whatever.com" -strBody $SMTPBody -strSubject "Auctions Module"
Write-Host "The file '$name' was $changeType at $timeStamp" -fore white
}
function Send-ToEmail([string]$email, [string]$strBody, [string]$strSubject){
$message = new-object Net.Mail.MailMessage
$message.From = "do_not_reply#site.com"
$message.To.Add($email)
$message.Subject = $strSubject
$message.Body = $strBody
$smtp = new-object Net.Mail.SmtpClient("127.0.0.1", "25")
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$smtp.send($message)
}

$Event.SourceEventArgs.<attribute> is returning a null or empty object

I have a FileSystemWatcher program that whenever an event is raised it sends an email notifying me of where the change happened.
However, $Event.SourceEventArgs.FullPath returns empty, and the other attributes returns $null.
Relevant code:
Function Watch{
$global:FileChanged = $false
$folder = "\\foo\boo\here\is\folder"
$filter = "*this is the filter*"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true
EnableRaisingEvents = $true
}
# Start-Sleep -Seconds 1
Register-ObjectEvent $watcher "Changed" -Action {$global:FileChanged = $true} > $null
# Register-ObjectEvent $watcher "Created" -Action {$global:FileChanged = $true} > $null
# Register-ObjectEvent $watcher "Deleted" -Action {$global:FileChanged = $true} > $null
# $watcher.EnableRaisingEvents = $true
while($true){
while($global:FileChanged -eq $false){
Start-Sleep -Milliseconds 250
}
$global:FileChanged = $false
$paths = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.FileName
$changetype = $Event.SourceEventArgs.ChangeType
$TimeChanged = $Event.TimeGenerated
RunScript $paths, $name, $changetype, $TimeChanged
}
}
Function RunScript
{
param($paths, $name, $changetype, $TimeChanged)
Write-Host "This should work $paths $name $changetype $TimeChanged"
$From = "email"
$ToAddress = "email"
$MessageSubject = "Form Submitted by $paths"
$MessageBody = "File Path: $Event.SourceEventArgs.FullPath
Name: $name
Change Type?: $changetype
Time Changed: $timechanged"
$SendingServer = "999.999.999.9.9"
$SMTPMessage = New-Object System.Net.Mail.MailMessage $From, $ToAddress,
$MessageSubject, $MessageBody
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
$SMTPClient.Send($SMTPMessage)
}
Watcher
Updated example. Note: I haven't tested this at all so be aware there are likely bugs. I've added some comments and a couple of different approaches for other sections of the code.
Function Watch {
$folder = "\\foo\boo\here\is\folder"
$filter = "*this is the filter*"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true
EnableRaisingEvents = $true
}
# Start-Sleep -Seconds 1
Register-ObjectEvent $watcher "Changed" > $null
# Register-ObjectEvent $watcher "Created" > $null
# Register-ObjectEvent $watcher "Deleted" > $null
# $watcher.EnableRaisingEvents = $true
while($true){
Wait-Event | Get-Event | ForEach-Object {
# $paths = $_.SourceEventArgs.FullPath
# $name = $_.SourceEventArgs.FileName
# $changetype = $_.SourceEventArgs.ChangeType
# $TimeChanged = $_.TimeGenerated
# If you comma-delimit this list you pass all of the arguments into "RunScript".
# Either name the parameters or make them positional.
# Positional:
# RunScript $paths $name $changetype $TimeChanged
# Named:
# RunScript -Paths $paths -Name $name -ChangeType $changetype -TimeChanged $TimeChanged
# Or use Splatting:
$params = #{
Paths = $_.SourceEventArgs.FullPath
Name = $_.SourceEventArgs.FileName
ChangeType = $_.SourceEventArgs.ChangeType
TimeChanged = $_.TimeGenerated
}
RunScript #params
}
}
}
Function RunScript {
param($paths, $name, $changetype, $TimeChanged)
Write-Host "This should work $paths $name $changetype $TimeChanged"
# $From = "email"
# $ToAddress = "email"
# $MessageSubject = "Form Submitted by $paths"
# $MessageBody = "File Path: $Event.SourceEventArgs.FullPath
# Name: $name
# Change Type?: $changetype
# Time Changed: $timechanged"
# $SendingServer = "999.999.999.9.9"
# $SMTPMessage = New-Object System.Net.Mail.MailMessage $From, $ToAddress,
# $MessageSubject, $MessageBody
# $SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
# $SMTPClient.Send($SMTPMessage)
# Is there a reason you're not using Send-MailMessage?
# Splatting again :)
# The body of the email isn't going to be very pretty, but it's a start.
$params = #{
From = "email"
To = "email"
Subject = "Form Submitted by $paths"
Body = "File Path: $paths
Name: $name
Change Type?: $changetype
Time Changed: $timechanged"
"
SmtpServer = "999.999.999.9.9"
}
Send-MailMessage #params
}
Now you get a caveat. The FileSystemWatcher is useful, but because of how Windows works you may well receive two event notifications for a single change (that's a deep Windows problem as opposed to a code problem). Try it and see.
Chris
Edit: Forgot to remove the action parameter. It's gone now.

powershell v2 : WebClient/UploadString never connect

with powershell v2 and pushbullet, I try to send push notification when a file in modified
$folder = 'c:\path\to\file'
$filter = '*.*'
$user = "pushbullet_token"
$url = "https://api.pushbullet.com/v2/pushes"
$fsw = New-Object IO.FileSystemWatcher $folder, $filter
$fsw.IncludeSubdirectories = $true
$fsw.NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$name = $Event.SourceEventArgs.Name
$path = $Event.SourceEventArgs.FullPath
Out-File -FilePath c:\path\to\file\outlog.txt -Append -InputObject "$path"
$title = $path
Add-Type -AssemblyName System.Web
$title = [System.Web.HttpUtility]::UrlEncode($title)
$data = "type=note&title=" + $title + "&body=body"
$webclient = new-object System.Net.WebClient
$webclient.Credentials = new-object System.Net.NetworkCredential($user, "")
Out-File -FilePath c:\path\to\file\outlog.txt -Append -InputObject "$data"
$result = $webclient.UploadString($url, "POST", $data)
Out-File -FilePath c:\path\to\file\outlog.txt -Append -InputObject "$result"
}
#Unregister-Event FileCreated
for check the script a outlog.txt file is write, but only the two first messages are writen and the notification never is submitted.
when I launch uploadstring manually
$user = "pushbullet_token"
$url = "https://api.pushbullet.com/v2/pushes"
$data = "type=note&title=title&body=body"
$webclient = new-object System.Net.WebClient
$webclient.Credentials = new-object System.Net.NetworkCredential($user, "")
$result = $webclient.UploadString($url, "POST", $data)
work ok.
The global variable $url is not available inside your event handler script block. Change your Register-ObjectEvent like so:
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -MessageData $url -Action {
$name = $Event.SourceEventArgs.Name
$path = $Event.SourceEventArgs.FullPath
$url = $Event.MessageData
...
}
The call to $webclient.UploadString(...) in the event handler is throwing an exception, which terminates the EventJob context it's running in. You can verify this by typing:
Get-Job
and looking for the failed job. You can then do Recieve-Job on the failed job to get the error message. Its probably an authentication error. By putting a valid authentication token, I was able to get your code to work.
If you want to have event handler continue even in the event of an error you'll have to use a try/catch, for example:
$result = try { $webclient.UploadString($url, "POST", $data) }
catch { $_.Exception.InnerException.Response }