I'm noob in PowerShell and trying to use it to send email with attachment.
Today's date is 2021-02-18. I have a folder which have many files created by ERP system everyday, the files name are as follows:
2021-2-17WO_rate.xlsx
2021-2-16WO_rate.xlsx
2021-2-15WO_rate.xlsx
2021-2-14WO_rate.xlsx
2021-2-13WO_rate.xlsx
2021-2-12WO_rate.xlsx
...
I would like the script always choose the newest file as the attachment
(Get-Date).AddDays(-1).ToString('yyyy-MM-dd') can get the yesterday date, but when I intergrate it in to below script, it fails. I have no idea how to solve it after search the many websites.
Anyone can help me? much appreciate.
The code I use is as follows:
$UserName = "XXXX#XXXXX.com"
$Password = ConvertTo-SecureString XXXXXX -AsPlainText –Force
$cred = New-Object System.Management.Automation.PSCredential($UserName,$Password)
$a = (Get-Date).AddDays(-1).ToString('yyyy-MM-dd')
$mailParams = #{
SmtpServer = 'smtp.office365.com'
Port = '587' # or '25' if not using TLS
UseSSL = $true ## or not if using non-TLS
BodyasHtml = $true
Credential = $cred
From = $UserName
To = 'XXXXXX'
Subject = "WORK ORDER STATUS"
Body = 'XXXXXXX'
Attachments = '\\us-fs\us-Groups\Operationsus\Public\erpoutput\date\${a}WO_rate.xlsx'
DeliveryNotificationOption = 'OnFailure', 'OnSuccess'
}
Send-MailMessage #mailParams
The reason why the name is not populated is because you used single quotation mark '. If you want to have the value from the variable, you need to use double quotation string ":
<# Only relevant parts of the script, other lines removed #>
# Specify correct formatting string, based on the information from the question it should be either
$dataFormat = 'yyyy-M-d'
# or
$dataFormat = 'yyyy-M-dd'
$formattedDate = (Get-Date).AddDays(-1).ToString($dataFormat)
$mailParams = #{
<# other params #>
Attachments = "\\us-fs\us-Groups\Operationsus\Public\erpoutput\date\${formattedDate}WO_rate.xlsx"
}
Send-MailMessage #mailParams
NOTE: I also changed variable name so that it's obvious what it is used for.
Edit: corrected date format based on #Theo's comment
If the file is not modified since yesterday as well you can retrieve the path of the last file modified yesterday in the folder and use that as the attachment
$file = Get-ChildItem -Path 'C:\path\to\folder' -Filter '*.xlxs' -File | # get all files with xlxs extension in path
Where-Object { $_.LastWriteTime.Date -eq (Get-Date).AddDays(-1).Date } | # files that were last written to yesterday
Sort-Object LastWriteTime | # sort by lastwrite time in ascending order
Select-Object -Last 1 | # select only the last file on the list
ForEach-Object FullName # output the full file path
$UserName = "XXXX#XXXXX.com"
$Password = ConvertTo-SecureString XXXXXX -AsPlainText –Force
$cred = New-Object System.Management.Automation.PSCredential($UserName,$Password)
$a = (Get-Date).AddDays(-1).ToString('yyyy-MM-dd')
$mailParams = #{
SmtpServer = 'smtp.office365.com'
Port = '587' # or '25' if not using TLS
UseSSL = $true ## or not if using non-TLS
BodyasHtml = $true
Credential = $cred
From = $UserName
To = 'XXXXXX'
Subject = "WORK ORDER STATUS"
Body = 'XXXXXXX'
Attachments = $file # <--------------
DeliveryNotificationOption = 'OnFailure', 'OnSuccess'
}
Send-MailMessage #mailParams
Related
Okay so i'm trying to send one email using powershell v2 to some recipients in a text file named emailtargets formatted in new line. below is the sendmailer script.
$emailSmtpServer = "mail.xxxxxxxxx.com"
$emailSmtpServerPort = "587"
$SourceRCPT = "xxxxx#xxxxxxxxx.com"
$DestFile = "emailTargets.txt"
$BodyFile = "emailBody.txt"
$SubjectLine = "testing"
$Pass = "xxxxxxx"
# Start Loop for Email Sending
$file = Get-Content $DestFile
$body = Get-Content $BodyFile
foreach ($line in $file){
$DestinationRCPT = $line.Split(",")[0]
$firstName = $line.Split(",")[1]
$lastName = $line.Split(",")[2]
$body =(Get-Content $BodyFile) | foreach-object {$_ -replace '\[firstName\]',$firstName}| foreach-object {$_ -replace '\[lastName\]',$lastName}
$emailSmtpUser = "$SourceRCPT"
$emailSmtpPass = "$Pass"
$emailFrom = "$SourceRCPT"
$emailTo = "$DestinationRCPT"
$emailMessage = New-Object System.Net.Mail.MailMessage( $emailFrom , $emailTo )
$emailMessage.Subject = $SubjectLine
$emailMessage.IsBodyHtml = $true
$emailMessage.Body = $body
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer , $emailSmtpServerPort )
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $emailSmtpUser.Split("#")[0] , $emailSmtpPass );
$SMTPClient.Send( $emailMessage )
Write-Host "Sending Email to $DestinationRCPT"
Now the question is i keep getting the error below while testing with office 365
And i get the below error while testing with a different smtp server
What am i doing wrong?
Its a certificate validation issue, If you are okay in suppressing it, you can do
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
before sending the message.
Here a set of samples that should assist you in re-writing this to function.
Overview of Cmdlets Available in Windows PowerShell Windows
PowerShell 2.0
https://technet.microsoft.com/en-us/library/ff714569.aspx
Use this example
https://gallery.technet.microsoft.com/scriptcenter/Send-MailMessage-3a920a6d
...
$params = #{
InlineAttachments = $images
Attachments = 'C:\temp\attachment1.txt', 'C:\temp\attachment2.txt'
Body = $body
BodyAsHtml = $true
Subject = 'Test email'
From = 'username#gmail.com'
To = 'recipient#domain.com'
Cc = 'recipient2#domain.com', 'recipient3#domain.com'
SmtpServer = 'smtp.gmail.com'
Port = 587
Credential = (Get-Credential -Credential 'username#gmail.com')
UseSsl = $true
}
Send-MailMessage #params
Or
# Using the PS built-in / online help files.
Get-Command -Name Send-MailMessage
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Send-MailMessage
3.1.0.0 Microsoft.PowerShell.Utility
# get function / cmdlet details
(Get-Command -Name Send-MailMessage).Parameters.Keys
Attachments
Bcc
Body
BodyAsHtml
Encoding
Cc
DeliveryNotificationOption
From
SmtpServer
Priority
Subject
To
Credential
UseSsl
Port
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
# Get the examples and modify per your use case, splatting used to make it easier to read on the forum.
Get-help -Name Send-MailMessage -Examples
# Example 1: Send an email from one user to another
$Params = #{
To = "User01 <user01#example.com>"
From = "User02 <user02#example.com>"
Subject = "Test mail"
Credential = (Get-Credential -Credential 'user#fabrikam.com')
SmtpServer = "smtp.fabrikam.com"
UseSsl = $true
Port = 587
}
Send-MailMessage #Params
# This command sends an email message from User01 to User02.
#
# The mail message has a subject, which is required, but it does not have a
# body, which is optional. Also, because the SmtpServer parameter is not
# specified,
#
# Send-MailMessage uses the value of the $PSEmailServer preference variable
# for the SMTP server.
# Example 2: Send an attachment
$Params = #{
From = "User01 <user01#example.com>"
To = "User02 <user02#example.com>", "User03 <user03#example.com>"
Subject = "Sending the Attachment"
Body = "Forgot to send the attachment. Sending now."
Attachments = "data.csv"
Priority = High
dno = 'onSuccess, onFailure'
Credential = (Get-Credential -Credential 'user#fabrikam.com')
SmtpServer = "smtp.fabrikam.com"
UseSsl = $true
Port = 587
}
Send-MailMessage #Params
# This command sends an email message with an attachment from User01 to two
# other users.
# It specifies a priority value of High and requests a delivery notification
# by email when the email messages are delivered or when they fail.
# Example 3: Send email to a mailing list
$Params = #{
To = "User01 <user01#example.com>"
From = "ITGroup <itdept#example.com>"
Cc = "User02 <user02#example.com>"
bcc = "ITMgr <itmgr#example.com>"
Subject = "Don't forget today's meeting!"
Credential = 'domain01\admin01'
UseSsl = $true
SmtpServer = "smtp.fabrikam.com"
Port = 587
}
Send-MailMessage #Params
# This command sends an email message from User01 to the ITGroup mailing list
# with a copy (Cc) to User02 and a blind carbon copy (Bcc) to the IT manager
# (ITMgr).
#
# The command uses the credentials of a domain administrator and the UseSsl
# parameter.
Get-help -Name Send-MailMessage -Full
Get-help -Name Send-MailMessage -Online
What I am hoping to do is once a new email hits a folder containing a specific subject. That email is then forwarded to another inbox and a specific file is automatically attached. The code I have been able to cobble together so far is this. Is a .Net method the appropriate method to achieve my goal or would using Send-MailMessge to a hastable be better method? I am new to PowerShell code I am able to get both methods to work. But was wondering A. which method is preferred. B. is there a better/more efficient way?
#####Define Variables########
$fromaddress = "donotreply#fromemail.com"
$toaddress = "blah#toemail.com"
$bccaddress = "blah#bcc.com"
$CCaddress = "blah#cc.com"
$Subject = "ACtion Required"
$body = get-content .\content.htm
$attachment = "C:\sendemail\test.txt"
$smtpserver = "smtp.labtest.com"
##############################
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.CC.Add($CCaddress)
$message.Bcc.Add($bccaddress)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
$smtp.Send($message)
(Example of the hashtable method
$emailHashSplat = #{ To = $toAddress
From = $fromAddress
Subject = $emailSubject
Body = $emailBody SMTPServer = $smtpServer BodyAsHtml =
$true Attachments = "C:\sendemail\test.txt" # Attachments =)
Stick with Powershell commands whenever possible, .NET might be faster in some cases but it is a best practice to use only Powershell commands when possible.
Also make sure your code is easy to read and understand by others, using hastables with splatting wil help with this, see the following example: Github Gist Link (Click Me!)
Copy of the code, in case of link failure.
### Script Global Settings
#Declare SMTP Connection Settings
$SMTPConnection = #{
#Use Office365, Gmail, Other or OnPremise SMTP Relay FQDN
SmtpServer = 'outlook.office365.com'
#OnPrem SMTP Relay usually uses port 25 without SSL
#Other Public SMTP Relays usually use SSL with a specific port such as 587 or 443
Port = 587
UseSsl = $true
#Option A: Query for Credential at run time.
Credential = Get-Credential -Message 'Enter SMTP Login' -UserName "emailaddress#domain.tld"
<#
#Option B: Hardcoded Credential based on a SecureString
Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList #(
#The SMTP User Emailaddress
"emailaddress#domain.tld"
#The Password as SecureString encoded by the user that wil run this script!
#To create a SecureString Use the folowing Command: Read-Host "Enter Password" -AsSecureString | ConvertFrom-SecureString
"Enter the SecureString here as a single line" | ConvertTo-SecureString
)
#>
}
### Script Variables
#Declare Mailmessages.
$MailMessageA = #{
From = "emailaddress#domain.tld"
To = #(
"emailaddress#domain.tld"
)
#Cc = #(
# "emailaddress#domain.tld"
#)
#Bcc = #(
# "emailaddress#domain.tld"
#)
Subject = 'Mailmessage from script'
#Priority = 'Normal' #Normal by default, options: High, Low, Normal
#Attachments = #(
#'FilePath'
#)
#InlineAttachments = #{
#'CIDA'='FilePath'
#} #For more information about inline attachments in mailmessages see: https://gallery.technet.microsoft.com/scriptcenter/Send-MailMessage-3a920a6d
BodyAsHtml = $true
Body = "Something Unexpected Occured as no Content has been Provided for this Mail Message!" #Default Message
}
### Script Start
#Retrieve Powershell Version Information and store it as HTML with Special CSS Class
$PSVersionTable_HTLM = ($PSVersionTable.Values | ConvertTo-Html -Fragment) -replace '<table>', '<table class="table">'
#Retrieve CSS Stylesheet
$CSS = Invoke-WebRequest "https://raw.githubusercontent.com/advancedrei/BootstrapForEmail/master/Stylesheet/bootstrap-email.min.css" | Select-Object -ExpandProperty Content
#Build HTML Mail Message and Apply it to the MailMessage HashTable
$MailMessageA.Body = ConvertTo-Html -Title $MailMessageA.Subject -Head "<style>$($CSS)</style>" -Body "
<p>
Hello World,
</p>
<p>
If your recieved this message then this script works.</br>
</br>
<div class='alert alert-info' role='alert'>
Powershell version
</div>
$($PSVersionTable_HTLM)
</P>
" | Out-String
#Send MailMessage
#This example uses the HashTable's with a technique called Splatting to match/bind the Key's in the HashTable with the Parameters of the command.
#Use the # Symbol instead of $ to invoke Splatting, Splatting improves readability and allows for better management and reuse of variables
Send-MailMessage #SMTPConnection #MailMessageA
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.
I am trying to create a PowerShell script that will send an email if a service goes into a stopped state. I would like to be able to read the email configuration from another file.
Email configuration file:
.\emailconfig.conf
$emailSmtpServer = "smtp.company.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "usera"
$emailSmtpPass = "passwordb"
$emailFrom = "userA#company.com"
$emailTo = "userB#company.com"
$emailcc= "userC#company.com"
And this is what I have so far in the PowerShell script:
.\emailservicecheck.ps1
$A = Get-Service "Service B"
if ($A.Status -eq "Stopped") {
Get-Content emailconfig.conf | Out-String
$emailMessage = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
$emailMessage.Cc.Add($emailcc)
$emailMessage.Subject = "subject"
#$emailMessage.IsBodyHtml = $true # true or false depends
$emailMessage.Body = Get-Service "Service B" | Out-String
$SMTPClient = New-Object System.Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $False
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
}
The script works if I enter the text from the email config file into the script but I cannot seem to be able to read in the data from the file on the fly and get the script to work. It errors out and says that my variables are empty.
What you are searching for, (I think) are .psd1 files. I personally prefer them (along with JSON) over the other configuration formats. The link I'm referring to also describes other well-known formats and how to use them in PowerShell.
In short, module manifests work as follows:
configuration.psd1
#{
SmtpServer = "";
MailFrom = "";
Auth = #{
User = "";
Pass = "";
};
}
Script.ps1
$mailConfig = Import-LocalizedData -BaseDirectory C:\ -FileName configuration.psd1
$emailMessage = New-Object System.Net.Mail.MailMessage( $$mailConfig.mailFrom , $mailConfig.mailTo )
As Mark already pointed out, Get-Content emailconfig.conf | Out-String will just output the content of the file, it won't define the variables in your code. For that you'd need to dot-source the file, which requires a file with the extension ".ps1".
If you want to stick with a simple config file format I'd recommend changing the file to something like this:
emailSmtpServer = smtp.company.com
emailSmtpServerPort = 587
emailSmtpUser = usera
emailSmtpPass = passwordb
emailFrom = userA#company.com
emailTo = userB#company.com
emailcc = userC#company.com
And importing it into a hashtable via ConvertFrom-StringData:
$cfg = Get-Content emailconfig.conf | Out-String | ConvertFrom-StringData
The data in the hashtable can be accessed via dot-notation ($cfg.emailFrom) as well as via the index operator ($cfg['emailFrom']), so your code would have to look somewhat like this:
$msg = New-Object Net.Mail.MailMessage($cfg.emailFrom, $cfg.emailTo)
$msg.Cc.Add($cfg.emailcc)
$msg.Subject = 'subject'
$msg.Body = Get-Service 'Service B' | Out-String
$smtp = New-Object Net.Mail.SmtpClient($cfg.emailSmtpServer, $cfg.emailSmtpServerPort)
$smtp.EnableSsl = $false
$smtp.Credentials = New-Object Net.NetworkCredential($cfg.emailSmtpUser, $cfg.emailSmtpPass)
$smtp.Send($msg)
It looks like what you're trying to do is include some script from another file. This can be done by dot sourcing, however the file needs to be saved as a .ps1 file, you can't use .conf.
You'd do it as follows (in place of your existing Get-Content) line:
. .\emailconfig.ps1
Assuming the file is kept in the current working directory of the script.
Your script wasn't working because
get-content emailconfig.conf | Out-String
Was returning the contents of that file to the output pipeline, rather than including it in the script and executing it.
I'm not sure i understood correctly what you want.
If you want to use variables from external file, you need to dot source your external script, for example, create a file named variables.ps1 and put in the same folder
In the beginning of the main script use
. .\variables.ps1
If you are after expanding variables that are in external file to ues as an email template please do as following:
$HTMLBody = get-content "yourfilepath" | Foreach-Object {$ExecutionContext.InvokeCommand.ExpandString($_)}
This will expand all variables and put it in the $HTMLBody variable
Then use:
$emailMessage.Body = (ConvertTo-Html -body $HTMLBody)
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.