New to error handling - powershell

The path that I have set is not valid, when the copy fails I want to send an email to someone. If there is no error then send an email stating the copy was successful.
Current it doesnt give me an error and it doesnt send an email. I know that the email part is correct and confirmed it does work.
My script block.
try
{
Copy-Item -path "\\main-
4\info\SmartPlant\app\CitrixRelease\domain\app\*" -Destination "\\domain.com\citrix\Installation Media\app\" -force -ErrorAction Stop
}
catch
{
$from = "alerts#domain.com"
$to = "me#domain.com"
$subject = "Copy Failed"
$body = "The Copy failed to complete, please make sure the servers rebooted"
$msg = "$file"
$Attachment = "$file"
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient("mail.domain.com")
$msg.From = $From
$msg.To.Add($To)
if($Attachment.Length -gt 1)
{
$msg.Attachments.Add($Attachment)
}
$msg.Subject = $Subject
$msg.IsBodyHtml = $true
$msg.Body = $Body
$smtp.Send($msg)
}

How about this as a solution for sending an email for both failure and success without duplicating the email send code:
$Status = 'Succeeded'
try{
Copy-Item -path "\\main-4\info\SmartPlant\app\CitrixRelease\domain\app\*" -Destination "\\domain.com\citrix\Installation Media\app\" -force -ErrorAction Stop
}catch{
$Status = 'Failed'
}finally{
$from = "alerts#domain.com"
$to = "me#domain.com"
$subject = "Copy $Status"
$body = "The Copy $Status"
If ($Status = 'Failed') {$body += ", please make sure the server is rebooted" }
$Attachment = "$file"
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient("mail.domain.com")
$msg.From = $From
$msg.To.Add($To)
if($Attachment.Length -gt 1){
$msg.Attachments.Add($Attachment)
}
$msg.Subject = $Subject
$msg.IsBodyHtml = $true
$msg.Body = $Body
$smtp.Send($msg)
}
You don't really need to use a Finally block, but it does create a nice code block to make explicit what the email functionality belongs to.

Related

Powershell FileSystemWatcher script firing twice on some new files

I'm using FileSystemWatcher to monitor a folder where documents are scanned to. When a new file is detected, it will send an email to notify someone. It's working as is, but sometimes (not every file) it will trigger 2 or 3 times on a new file and send the email 2-3 times for the same file. I'm guessing it has to do with the way the file is created by the scanner or something like that.
I'm trying to figure out a way to protect against this happening, to ensure it only sends one email per file. Any suggestions would be greatly appreciated.
$PathToMonitor = "\\path\to\folder"
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.Filter = "*.*"
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.EnableRaisingEvents = $true
$Action = {
if ($EventArgs.Name -notlike "*.pdf" -and $EventArgs.Name -notlike "*.tif") {
return
}
$details = $event.SourceEventArgs
$Name = $details.Name
$Timestamp = $event.TimeGenerated
$text = "{0} was submitted on {1}." -f $Name, $Timestamp
$FromAddress = "Email1 <email1#email.com>"
$ToAddress = "Email2 <Email2#email.com>"
$Subject = "New File"
$SMTPserver = "123.4.5.678"
Send-MailMessage -From $FromAddress -To $ToAddress -Subject $Subject -Body $text -SmtpServer $SMTPserver
}
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreateConsumer
}
try {
do {
Wait-Event -Timeout 5
} while ($true)
}
finally {
Unregister-Event -SourceIdentifier FSCreateConsumer
$handlers | Remove-Job
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
}
This may be because you listen too many notifications. The default is LastWrite, FileName, and DirectoryName
FileName is sufficient for your need and may prevent your issue.
$FileSystemWatcher.NotifyFilter = [System.IO.NotifyFilters]::FileName
As a remark, I don't know why you use Wait-Event -Timeout 5. Script is working fine without the try{} block.
EDIT: Add a ConcurrentDictionary to avoid duplicate events
Try this sample code. I've included only the beginning part of your script. End is untouched.
$PathToMonitor = "\\path\to\folder"
$KeepFiles = 5 #minutes
$MonitoredFiles = New-Object -TypeName 'System.Collections.Concurrent.ConcurrentDictionary[[System.String],[System.DateTime]]'
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.Filter = "*.*"
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.NotifyFilter = [System.IO.NotifyFilters]::FileName
$FileSystemWatcher.EnableRaisingEvents = $true
$Action = {
if ($EventArgs.Name -notlike "*.pdf" -and $EventArgs.Name -notlike "*.tif") {
return
}
#Cleaning events -gt 5mn
$Now = [System.DateTime]::Now
$OriginEventDate = [System.DateTime]::MinValue
foreach($MonitoredFile in [System.Linq.Enumerable]::ToList(($MonitoredFiles.Keys))) {
if ($MonitoredFiles.TryGetValue($MonitoredFile, [ref]$OriginEventDate)) {
if ($OriginEventDate.AddMinutes($KeepFiles) -gt $Now) {
try {
[void]$MonitoredFiles.Remove($MonitoredFile)
}
catch {}
}
}
}
$ProcessEvent = $false
# any same file creation event within 5mn are discarded
$OriginEventDate = [System.DateTime]::MinValue
if ($MonitoredFiles.TryGetValue($event.SourceEventArgs.Name, [ref]$OriginEventDate)) {
if ($OriginEventDate -ne [System.DateTime]::MinValue -and $OriginEventDate.AddMinutes($KeepFiles) -le $Now) {
return
}
else {
$ProcessEvent = $true
}
}
else {
#not successful means a concurrent event was successful, so discard this one.
if ($MonitoredFiles.TryAdd($event.SourceEventArgs.Name, $event.SourceEventArgs.TimeGenerated)) {
$ProcessEvent = $true
}
else {
return
}
}
if ($ProcessEvent) {
$details = $event.SourceEventArgs
$Name = $details.Name
$Timestamp = $event.TimeGenerated
$text = "{0} was submitted on {1}." -f $Name, $Timestamp
$FromAddress = "Email1 <email1#email.com>"
$ToAddress = "Email2 <Email2#email.com>"
$Subject = "New File"
$SMTPserver = "123.4.5.678"
Send-MailMessage -From $FromAddress -To $ToAddress -Subject $Subject -Body $text -SmtpServer $SMTPserver
}
}
I had to adjust these two things to make it work:
if ($MonitoredFiles.TryAdd($Event.SourceEventArgs.Name, $Event.TimeGenerated))
if ($OriginEventDate -ne [System.DateTime]::MinValue -and $OriginEventDate.AddMinutes($KeepFiles) -ge $Now)

Powershell RDS CAL email Report

I have a PowerShell command that will get an output of how many CAL's I have installed and how many are used. I would like to instead of write-host, change it to a variable so that I can add it to the body of an email and have it run on a schedule, to email me weekly reports on usage, I would like to have the variable something like $report as shown in the $body of the email, this is what I have so far..
$fileName = (Invoke-WmiMethod Win32_TSLicenseReport -Name GenerateReportEx).FileName
$summaryEntries = (Get-WmiObject Win32_TSLicenseReport|Where-Object FileName -eq $fileName).FetchReportSummaryEntries(0,0).ReportSummaryEntries
$summaryEntries|ForEach {Write-Host $_.ProductVersion $_.TSCALType "Installed:" $_.InstalledLicenses "Issued:" $_.IssuedLicenses}
$EmailTo = "itgroup#contonso.com"
$EmailFrom = "admin#contonso.com"
$user = 'admin#contonso.com'
$password = Unprotect-CmsMessage -Path C:\Scripts\Powershell\EncryptedSecret.txt
$Subject = "Alert: CAL USAGE "
$Body = "Alert; $Report"
$SMTPServer = "smtp#contonso.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25)
$SMTPClient.EnableSsl = $false
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($user, $password)
$SMTPClient.Send($SMTPMessage)
I have used both parts of this script separately, but I would like to join them together, to make more useful. Thanks in advance PowerShell newbie..
Here is the full code that I used to get this to work, simply changing 'Write-Host' to 'Write-Output'
$fileName = (Invoke-WmiMethod Win32_TSLicenseReport -Name GenerateReportEx).FileName
$summaryEntries = (Get-WmiObject Win32_TSLicenseReport|Where-Object FileName -eq $fileName).FetchReportSummaryEntries(0,0).ReportSummaryEntries
$Report = $summaryEntries|ForEach {Write-Output $_.ProductVersion $_.TSCALType "Installed:" $_.InstalledLicenses "Issued:" $_.IssuedLicenses}
$EmailTo = "itgroup#contonso.com"
$EmailFrom = "admin#contonso.com"
$user = 'admin#contonso.com'
$password = Unprotect-CmsMessage -Path C:\Scripts\Powershell\EncryptedSecret.txt
$Subject = "RDS CAL USAGE REPORT"
$Body = "Alert; $Report"
$SMTPServer = "smtp#contonso.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25)
$SMTPClient.EnableSsl = $false
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($user, $password)
$SMTPClient.Send($SMTPMessage)

Power Shell: If condition variable comparison not working

I have a piece of code comparing two values, and if the condition is satisfied it sends out an email. But it is not working, help is appreciated.
code:
$filesize = Get-ChildItem $filename | Select-Object Length | Format-Wide
$filesize
$num=1265
$num
if("$filesize" -gt "$num")
{
$SMTPServer = "10.20.19.94"
$SMTPPort = 25
$username = "vcenter#somosadc.com"
#Define the receiver of the report
$to = "jeevan.m2#hcl.com"
$subject = "VM Snapshot Report"
$body = "VM Snapshot Report"
$attachment = new-object Net.Mail.Attachment($filename)
$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 = $false
#$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
write-host "Mail Sent"
}
output:
1262
1265
Mail Sent
Why is it sending email if $filesize=1262 is less than $num=1265. It is killing me.
Because you're not comparing two numbers, you're comparing two strings.
Remove the Format-Wide command from the first pipeline, and remove the quotes around the arguments in your if condition:
$filesize = Get-ChildItem $filename | Select-Object Length
$num = 1265
if($filesize.Length -gt $num) {
<# ... #>
}

Sending mail Power Shell 4

Good Day! Help please, use this script here:
$file = "C:\Reports\Log_1.txt"
$FileExists = Test-Path $file
If ($FileExists -eq "False") {
Write-Host "sending mail"
$mail = New-Object System.Net.Mail.MailMessage
$mail.From = New-Object System.Net.Mail.MailAddress("test#gmail.com")
$mail.To.Add("test#gmail.com")
$mail.Subject = "report";
$mail.Body = get-content $file
$smtp = New-Object System.Net.Mail.SmtpClient
$smtp.host = "smtp.domen.com"
$smtp.port="25"
$Credentials = new-object System.Net.networkCredential
$Credentials.domain = "smtp.domen.com"
$Credentials.UserName = "test#gmail.com"
$Credentials.Password = "rgrthbdgfhytju"
$smtp.Credentials = $Credentials
$smtp.Send($mail)
}
# else {
#Write-Host "File $file not found"
#}
I get two errors trying to connect the two mail servers.
In the first case, the connection error and the second authentication error:
error 1 (gmail.com): Exception calling with this 1 argument This operation has timed out.
error 2 (corporate post): Exception calling with this 1 argument Failure sending mail.
Sorry,I can not insert a picture of the limitations.

Adding Get-WMI Data To E-mail Body

I've got this script that I found that simply sends the time the script was run in an e-mail to the recipient.
function send-email
{
$time = get-date
$EmailFrom = “from”
$EmailTo = “To”
$Subject = “ADX Has Been Deployed”
$Body = “Script has been used on: ” + $time
$SMTPServer = “smtp.gmail.com”
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(“e-mail address”, “password”);
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
}
send-email
This works a treat, however when I try and add some data into the body of the e-mail using the below code (hostname, IP Address, etc), the data is returned as a complete string.
$a = #()
$systeminfo = get-wmiobject win32_computersystem | select *
foreach ($item in $systeminfo)
{
$a = $item
}
Basically, what I'm after is for the data to be presented in the e-mail one line at a time.
Any ideas?
Thanks
$a = #()
$systeminfo = get-wmiobject win32_computersystem | select *
foreach ($item in $systeminfo)
{
$a += $item
}
$body = [string]::Join("`n", $a)