How to get PowerShell to send email with file paths - email

I have the following script where I'm trying to get the path location of some files emailed to me but I am having a problem passing a variable to a function. I get the email but the body is empty.
Can someone help me out here?
Thanks,
Lamar Thomas
function sendMail($VirusLoc){
Write-Host "Sending Email"
#SMTP server name
$smtpServer = "mailhost.maxor.com"
#Creating a Mail object
$msg = new-object Net.Mail.MailMessage
#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
#Email structure
$msg.From = "me#xxxxxx.com"
$msg.ReplyTo = "me#xxxxx.com"
$msg.To.Add("me#xxxxx.com")
$msg.subject = "CryptoLocker Virus Found On pcname"
$msg.body = $VirusLoc
#Sending email
$smtp.Send($msg)
}
Clear-Host
$arg = ("\\mxfsa01\Supsys\Red River Wholesale-old")
Set-Location $arg
$Files = Get-ChildItem -Recurse |Where-Object {$_.name -Like "Decrypt_Instruction.txt"} | % {
# Write-Host $_.FullName
$VirusLoc = $_.FullName
Write-Host $VirusLoc
}
sendMail

You need to provide an argument for the VirusLoc parameter of your function e.g.:
sendMain -VirusLoc $VirusLoc
BTW that variable is only holding a single filename. Is there are multiple files you may want to change this line:
$VirusLoc = $_.FullName
to
$VirusLoc += "`n" + $_.FullName

Related

Change Powershell script to send the email with embedded text

I would like to modify the script below, to send the email with embedded text, without removing the html attachments.
The data it should embed into the email is found at: "C:\Users\Users\Summary\SummaryCSV.txt"
Here is the Powershell Script which needs to be modified:
# Check to see we have all the arguments
If (Test-Path -Path "C:\Users\Users\Summary\SummaryCSV.txt") {
#Send Email with HTML as attachment and no text embedded
$FullPath=$args[0]
#Get an Outlook application object
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = High importance message
$mail.importance = 1
$mail.subject = "Ready to be retrieved: $(get-date)"
$mail.body = "Summary $(get-date)"
#separate multiple recipients with a ";"
$mail.To = "email#email1.com"
# Iterate over all files and only add the ones that have an .html extension
$files = Get-ChildItem $FullPath
for ($i=0; $i -lt $files.Count; $i++) {
$outfileName = $files[$i].FullName
$outfileNameExtension = $files[$i].Extension
# if the extension is the one we want, add to attachments
if($outfileNameExtension -eq ".html")
{
$mail.Attachments.Add($outfileName);
}
}
$mail.Send()
# give time to send the email
Start-Sleep 5
# quit Outlook
$o.Quit()
#end the script
#exit
}
Thanks in advance for your assistance.
Based on your specifications:
# Check to see we have all the arguments
If ((Test-Path -Path C:\Users\Users\Summary\SummaryCSV.txt) -and (Test-Path -Path C:\Users\Users\Summary\SummaryCSV.txt)) {
#Send Email with HTML as attachment and no text embedded
$FullPath=$args[0]
$embeddedText = Get-Content -Path C:\Users\Users\Summary\SummaryCSV.txt -Raw
#Get an Outlook application object
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = High importance message
$mail.importance = 1
$mail.subject = "Ready to be retrieved: $(get-date)"
$mail.body = "Summary $(get-date)`r`n$embeddedText"
#separate multiple recipients with a ";"
$mail.To = "email#email1.com"
# Iterate over all files and only add the ones that have an .html extension
$files = Get-ChildItem $FullPath
for ($i=0; $i -lt $files.Count; $i++) {
$outfileName = $files[$i].FullName
$outfileNameExtension = $files[$i].Extension
# if the extension is the one we want, add to attachments
if($outfileNameExtension -eq ".html") {
$mail.Attachments.Add($outfileName);
}
}
$mail.Send()
# give time to send the email
Start-Sleep 5
# quit Outlook
$o.Quit()
#end the script
#exit
}

How to look for a string in a filename and send that file via email?

I have a folder that contains several PDF files. They have a unique identifier within the filename that is two characters. Example: XYZ_A1_123.pdf, XYZ_QQ_456.pdf, etc. A1 and QQ are the identifiers.
The identifiers match specific email addresses. I have a CSV file that has two columns, the ID and the matching email address. I can't get my script to look for that specific identifier and send the file. If the PDF file has only the identifier in the name, such as "A1.pdf", the script works fine. Here's my code so far. Also, I'm looking to add a progress bar but not sure how. Any help is appreciated. Thanks!
$csv = Import-Csv "," -path C:\Test\emails.csv -header "id","email"
foreach ($item in $csv) {
$filename = "$($item.id).pdf"
}
$emailSmtpServer = "smtp.gmail.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "email#address.com"
$emailSmtpPass = "mypassword"
$emailFrom = "me#address.com"
$emailTo = $item.email
$emailMessage = New-Object System.Net.Mail.MailMessage( $emailFrom , $emailTo )
$emailMessage.Subject = "This is the subject"
#$emailMessage.IsBodyHtml = $true #true or false depends
$emailMessage.Body = "This is the body."
$emailMessage.Attachment = $filename
$emailMessage.Attachments.add($filename)
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer ,
$emailSmtpServerPort )
$SMTPClient.EnableSsl = $True
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(
$emailSmtpUser , $emailSmtpPass );
$SMTPClient.Send( $emailMessage )
Creating a test file to mimic the file you described with codes and e-mail addresses:
echo "AF,a#p.com" > test.csv
echo "QC,q#p.com" >> test.csv
cat .\test.csv
The following snippet will pull the file's code, compare it to the address list, and retrieve the e-mail address if present:
$addresses = Import-Csv -Path .\test.csv -Header code, address
$files = ls .\*.pdf
foreach ($file in $files) {
$file_code = $file.name.Split('_')[1]
$addresses | where { $_.code -eq $file_code } | select address
# ...send email...
}
Looks like you've got the e-mail stuff on-lock so I won't rehash that here. I'd only do the e-mail stuff if you find a matching address.

Powershell - Email does not have same format as text despite Out-string

Folks,
Googling shows me lots of folks have this problem, however the answers I'm getting do not seem to work for me. Either that, or I don't understand.
Situation: I have a script that polls and gives a file count. It works great and I pipe it to a text file
Foreach ($Directory in $Directories) {
Write-Output "You have $Results files in that folder" | Out-File "C:\Filecheck.txt" -Append
}
Filecheck looks great. It does the above loop 6 times (as I have 6 directories) and it does the carriage returns.
In email, its all jumbled up. On here, someone suggested I use the out-string, so Ive done this:
$body = GC "C:\Filecheck.txt" | Out-string
I've also seen
$body = GC "C:\Filecheck.txt" -Raw
I get the email fine, but again, its still all one line, with no carriage returns.
Anyone have any idea? I know Im so close.
You could try using the [Environment] newline. I tested with the code below and the e-mail looked good and with the correct line breaks:
$DirectoriesFiles = 2,3,4,5
$newline = [Environment]::NewLine
$body = "List of number of files" + $newline
Foreach ($numOfFiles in $DirectoriesFiles) {
$body += "You have $numOfFiles files in that folder" + $newline
}
$ol = New-Object -comObject Outlook.Application
$Mail = $ol.CreateItem(0)
$Mail.To = "someone"
$Mail.Subject = "some test e-mail"
$Mail.Body = $body
$Mail.save() #or send
For the example's sake I just assumed you have an array with the number of files in the folder, but I think you can understand how to adapt to your context from here. My resulting e-mail looked like this:
List of number of files
You have 2 files in that folder
You have 3 files in that folder
You have 4 files in that folder
You have 5 files in that folder
Thanks for your help. My company email didn't like the format, but I format in html (using ) and utilize IsBodyHTML tag, it works like a charm!
Del "D:\Filecheck.txt"
$Directories = GC "D:\Directory.txt"
Foreach ($Directory in $Directories) {
$Results = (Get-ChildItem $Directory).count
If ($Results -gt 0) {
Write-Output "...You have $Results files stuck in $Directory...<br><br> " | Out-File "D:\Filecheck.txt" -Append
} else {
Write-Output "Phew! We're good, <br><br>" | Out-File "D:\Filecheck.txt" -Append
}
$Results = $null
}
$body = GC "D:\Filecheck.txt"
Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin -erroraction silentlyContinue
$SmtpClient = new-object system.net.mail.smtpClient
$SmtpServer = "localhost"
$SmtpClient.host = "relay.me.local"
$msg = new-object Net.Mail.MailMessage
$msg.IsBodyHTML = $true
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = "TelluRyesFileCheck#me.you"
$msg.To.Add("me#you.org")
$msg.Subject = "Checking if files exist on 9901/2"
$msg.Body = $body
$SmtpClient.Send($msg)

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"
}

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.