powershell - write errors to .txt file and in one line - powershell

I need to log the process in a text file and there are two problems:
1. How to write errors in a text file? Now when the e-mails are not sent, nothing is logged.
2. I need to write time, date and event in a single line
Now I have this in log.txt:
17. feb. 2012 10:47:34
Chyba: .gpg neexistuje
17. feb. 2012 10:57:28
Test.gpg existuje.
This is my code:
function write-log{
param(
[string]$mytext,
[string]$fgc
)
if(!$fgc){$fgc="Black"}
write-host $mytext -foregroundcolor $fgc
$myfile = "c:\gnupg\subor\log.txt"
Get-Date | Out-File $myfile -append
$mytext | Out-File $myfile -append
}
if(test-path "d:\vm\shared pre ws08R2+SQL05\SFRB\*.chk") {
echo "Subor .chk existuje a jeho nazov je "
get-childitem "d:\vm\shared pre ws08R2+SQL05\SFRB\*.chk" -Name
$a = get-childitem "d:\vm\shared pre ws08R2+SQL05\SFRB\*" -include *.chk -name | Foreach-Object {$a -replace ".chk", ""}
if(test-path d:\vm\"shared pre ws08R2+SQL05"\SFRB\$a.gpg) {
Write-Log $a".gpg existuje." Green
Write-Log "Presuvam do banky subor $a.gpg.." Green
Move-Item "d:\vm\shared pre ws08R2+SQL05\SFRB\$a.gpg" c:\gnupg\subor\$a.gpg
Write-Log "Presun ukonceny." Green
Write-Log "Presuvam do banky subor $a.chk.." Green
Move-Item "d:\vm\shared pre ws08R2+SQL05\SFRB\$a.chk" c:\gnupg\subor\$a.chk
Write-Log "Presun ukonceny. Subor je pripraveny na spracovanie." Green
Write-Log "Posielam notifikacne maily.." Green
$emailFrom = "sfrbControlMsg#primabanka.sk"
$emailTo = "msic#primabanka.sk"
$subject = "subject"
$body = "subor presunuty"
$smtpServer = "87.244.217.54"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
# Write-Log "Maily odoslane." Green
}
else {
Write-Log " Chyba: $a.gpg neexistuje" Magenta
}
} else {
Write-log "V cielovom adresari neexistuje ziadny subor .chk."
}
Thank you.

Make use of the inbuilt Send-MailMessage cmdlet with parameter -ErrorAction Stop.
Use a try-catch around this (see about_try_catch_finally to pick up and handle any errors.
In the catch block $_ will be the error message (as would be shown on the console), and has an Exception property which is the (.NET) Exception (or subclass) instance (I use $_.Exception.GetType() below to specifically report this type as it is an important part of any diagnostics):
Eg. in a script I have:
try {
$trans = (MakeSummaryMessage) + "`r`n`r`n" + $trans
Send-MailMessage -From $emailFrom `
-To $emailTo `
-SmtpServer $emailServer `
-Subject $subject" `
-Body $trans `
-Encoding ([System.Text.Encoding]::UTF8) `
-ErrorAction Stop
$emailSent = $true
} catch {
Write-EventLog -LogName $eventLog -Source $eventSource -EntryType "Error" `
-EventId 100 -Message "Failed to send email: $($_.Exception.GetType()): $_"
}

Related

The program works wrong. How do we catch error missage and direct it in a file error.txt? code that do not work

The program works wrong. How do we catch error missage and direct it in a file error.txt?
code that do not work
Get-content c:\attach\list.txt | Get-childitem -File | select -property fullname, length | Export-csv -notypeinformation -path C:\attach\error.txt
try {
$credentials = new-object Management.Automation.PSCredential “aaa#dsf.ru”, (“sadf3e33” | ConvertTo-SecureString -AsPlainText -Force)
Send-mailmessage -To "xx#company.com" -From "aaa#dsf.ru" -Encoding ([System.Text.Encoding]::UTF8) -Subject "subject." -Body "Сообщение отправлено автоматически" -Smtpserver "mail.dsf.ru" -Port 25 -Attachments "C:\attach\test.txt" -Credential $credentials
} catch {
Write-Output "error sending email" $credentials
Add-Content c:\attach\error.txt "error sending email"
}
wrong output in file error.txt we cannot find record of error.
Just a couple of things about your code.
I completely do not understand what you are trying to achieve with the first line..
You are using curly 'smart-quotes' in code. Change these to straight ones
Try to use splatting for cmdlets that use many parameters like Send-MailMessage
In order to make the try{..}catch{..} work, you need to use the -ErrorAction parameter set to Stop.
This will have PowerShell treat all errors from the cmdlet as 'terminating errors' so they can be dealt with inside the catch{..} block.
Below ought to do what you want:
# Get an array of email addresses to use in the Bcc from a textfile
# Remove empty elements. Each email address should be on a separate line
$bcc = Get-Content -Path 'F:\MY\MyFiles\files\1DAY.txt' | Where-Object { $_ -match '\S' }
try {
$secPassword = "sadf3e33" | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object Management.Automation.PSCredential "aaa#dsf.ru", $secPassword
$mailParams = #{
'To' = "xx#company.com"
'Bcc' = $bcc
'From' = "aaa#dsf.ru"
'Encoding' = 'UTF8'
'Subject' = "subject."
'Body' = "Сообщение отправлено автоматически"
'Smtpserver' = "mail.dsf.ru"
'Port' = 25
'Attachments' = "C:\attach\test.txt"
'Credential' = $credentials
}
# set ErrorAction to 'Stop' so all errors are regarded as terminating errors
# and are dealed with in the catch block
Send-MailMessage #mailParams -ErrorAction Stop
}
catch {
$errorMessage = "Error sending email: $($_.Exception.Message)"
Write-Warning $errorMessage
Add-Content -Path 'c:\attach\error.txt' -Value $errorMessage
}
the best approach is to have a function created Write-Line that creates one line within a txt file with all the errors you catch.
Then in a try and catch block of code you could write the body of code, and then create different catch functions for each error you have
catch{
Write-Line(Exception e.Message)
}

powershell winscp get local filename after transfer so i can attach to email

Hello I am trying to get the filename of the file just downloaded with the localpath ie C:\users\user\desktop\test.txt so i can attach it to an email.
I am using powershell with the winscp .net library
here is the code
param (
$localPath = "C:\upload\*",
$remotePath = "/home/user/",
$backupPath = "C:\backup\"
)
try
{
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$sessionOptions.HostName = "example.com"
$sessionOptions.UserName = "user"
$sessionOptions.Password = "mypassword"
$sessionOptions.SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files, collect results
$transferResult = $session.PutFiles($localPath, $remotePath)
# Iterate over every transfer
foreach ($transfer in $transferResult.Transfers)
{
# Success or error?
if ($transfer.Error -eq $Null)
{
Write-Host ("Upload of {0} succeeded, moving to backup" -f
$transfer.FileName)
Send-MailMessage -From $From -to $To -Cc $Cc -Bc $Bc -Attach $session.GetFileInfo(Filename) -Subject $Subject -Body $Body -SmtpServer $SMTPServer
# Upload succeeded, move source file to backup
Move-Item $transfer.FileName $backupPath
}
else
{
Write-Host ("Upload of {0} failed: {1}" -f
$transfer.FileName, $transfer.Error.Message)
}
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host $_.Exception.Message
exit 1
}
thank you in advance
If you're wanting to use $session.getFileInfo() from the WinSCP library, you're looking for $transfer.destination. I.e. $session.getFileInfo($transfer.Destination).
Otherwise if you're reading the properties of the file from the local path, you could just use Powershell's built in cmdlets as such: get-itemproperty $transfer.filename

IF statement + send-mailmessage confused

Quick question;
I want to make a log of bad stuff happening to the servers, but that's not the question.
The log should be deleted upon send, and if there is no log at the time the script is being run it should send a different message, without an attachment.
Here's the script so far:
<#
Set-ExecutionPolicy unrestricted -Force
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
cinst BetterCredentials
#>
Import-Module BetterCredentials
$Mailcred = Get-Credential -user user#gmail.com -pass 12345password
Try{
$File = C:/path/errorfile.log
}
Catch [System.Management.Automation.PSArgumentException]
{
"invalid object"
}
Catch [system.exception]
{
"caught a system exception"
}
Finally
{
If ($File -eq ){
then (send-mailmessage -ErrorAction SilentlyContinue -from "Server <Server#company.com>" -to "IT Dept. <Itdept#company.com>" -subject "Log of the day." -body "Good Morning,
Fortuneatly, nothing bad to report today!" -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
else (send-mailmessage -ErrorAction SilentlyContinue -from "Servers <Server#company.com>" -to "IT Dept. <ITDept#company.com>" -subject "Log of the day." -body "Good Morning,
Here is your daily report." -Attachments $File -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
}
}
What AM I doing wrong here?
Some parts of your code do not make any sense. Let's review those.
$File = C:/path/errorfile.log
...
If ($File -eq ){
The first part is about assignment. In order to find out wether errorfile.log exists, use the Test-Path cmdlet like so,
$File = test-path 'C:/path/errorfile.log'
Always use quotes around file name. If you got spaces within path, unquoted path doesn't work.
The second part is the actual test.
If ($File){
... # file was on disk, send alert and delete it
} else {
... # no file found, send ok
}

Powershell applied to Outlook Com Object - Multiple Filters

ORIGINAL POST:
Goal: an email is sent to a service account every day from a source.
Take the csv file it gives me and put it in a folder for the DBA guy.
Without further adieu, here is my current script:
#define variables
$datestamp = (get-date).AddDays(-1).ToString("MMddyyyy")
$datestamp = $datestamp.ToString()
$path = "C:\MyPath"
$dest = "C:\MyPath\Archive"
$file = "MyFile.csv"
#create outlook session
$objOutlook = New-Object -Com Outlook.Application
$inbox = $objOutlook.Session.GetDefaultFolder(6)
$inbox.Items.Restrict("[UnRead] = True" -and "[SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).SaveAsFile("C:\MyPath\$($_.Item($i).FileName)")
$_.Parent.Unread = $false
}
}
if (Test-Path "C:\MyPath\*.csv")
{
if(((Get-ChildItem C:\MyPath | Measure-Object ).Count) -gt '1' )
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject " FAIL" -Body "FAILED. Too many valid items from mailbox.
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "Too many items to get."
}
else
{
Get-ChildItem $path\*.csv | foreach {Copy-Item $_ "C:\MyPath\$"}
Copy-Item C:\MyPath\*.csv "$path\$file"
Copy-Item C:\MyPath\*.csv "$dest\${datestamp}_$file"
if(Test-Path "$dest\$file")
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject "some message”
#cleanup - remove all files from base directory, clean mailbox, close out com object.
Remove-Item "$path\*.csv"
$inbox.Items | select | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).Delete
}
}
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
}
else
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject " failure" -Body "File manipulation failure."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "File manipulation failure."
}
}
}
else
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To PoorITGuy#domain.com -Subject "FAIL" -Body "No item mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "No item to get in inbox."
}
What doesn't work:
$inbox.Items.Restrict("[UnRead] = True" -and "[SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | %
It seems that you cannot restrict the comObject Outlook.Application with more than one filter.
I've done a ton of searching on this and cannot find an answer on how to best perform this task in lieu of this. But I want my script to be spam-proof, so it needs to know that it is sent from the expected sender AND it needs to be unread only (see error catching right below)
In addition, I'm not certain if this would work:
$inbox.Items | select | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).Delete
}
}
Also would like input on the script itself. Any input to make it more efficient would be appreciated.
The correct code (lots of syntax errors on the above prevented it from doing what I wanted to)
#define variables
$datestamp = (get-date).AddDays(-1).ToString("MMddyyyy")
$datestamp = $datestamp.ToString()
$path = "C:\MyPath"
$dest = "C:\MyPath\Archive"
$importpath = "C:\MyPath\Import"
$file = "MyFile.csv"
$folderExclude = "C:\MyPath\Archive"
#create outlook session
$objOutlook = New-Object -Com Outlook.Application
$inbox = $objOutlook.Session.GetDefaultFolder(6)
$inbox.Items.Restrict("[Unread] = True AND [SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | % {
for ($i = $_.Count; $i; $i--) {
$_.Item($i).SaveAsFile("\\server\MyPath\$($_.Item($i).FileName)")
$_.Parent.Unread = $false
}
}
if (Test-Path "C:\MyPath\*.csv") {
if(((Get-ChildItem C:\MyPath -Include "*.csv" | Measure-Object ).Count) -gt '1' ) {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject "FAIL" -Body "FAILED. Too many items from PSAutomationSrv mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "Too many items to get."
}
else {
Copy-Item C:\MyPath\*.csv "$importpath\$file"
Copy-Item C:\MyPath\*.csv "$dest\${datestamp}_$file"
if(Test-Path "$dest\${datestamp}_$file") {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject " successful" -Body "Date is: ${datestamp} File name is: ${dest}\${file}"
#cleanup - remove all files from base directory, clean mailbox, close out com object.
Remove-Item $path\*.csv
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
}
else {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject " import - failure" -Body "File manipulation failure."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "File manipulation failure."
}
}
}
else {
Send-MailMessage -To -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject "FAIL" -Body "FAILED. No item in mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "No item to get in inbox."
}
Notes - I had some code in excess.
Get-ChildItem $path\*.csv | foreach {Copy-Item $_ "C:\MyPath\$"}
is incorrect syntax, and unnecessary since the script already knows it is just manipulating one file anyway.
For deleting the inbox I would need to use Export-Mailbox it seems, which I do not want to do; as the service account performing this function is NOT an exchange admin nor do I want it to be. I'm just going to manually delete the inbox periodically or have the exchange guy do it on his end.

Robocopy sending email, attachment, appending file name

I am using robocopy to backup one hard drive to another below is the code that I am using:
robocopy "\\server_location\XXXX" "\\local_location\xxxx" /copyall /TEE /S /r:1 /ndl /xc /xo /xn /log+:"C:\desktop\log.TXT"
START mailto:myemail.job.com?subject=Ovl%%2Reportˆ&body=Test,%%0D%%0A%%0D%%0AHere%%20is%%20is%%the%%20ovl%%20report.%%0D%%0A%%0D%%0ABye
I need to be able to do a couple of things:
email the text file (powershell?) automatically through outlook.
When comparing files if the time stamp is different, append the destination file name, and copy over the file from the source.
So here is a script I've used for a lot of my robocopy jobs. I'm not sure if it fits your "send email through outlook" as it's just doing an SMTP, it's not actually hooking in to Outlook to do it. The script is a Powershell 2.0 script, and remember only Server 2008R2 and Windows Vista+ have the /MT option, which is bad to use with the /TEE option. Script is at the bottom of the post.
For the second point you have, I think you might want two different scripts, one that specifically ignores timestamps, and then another that would include the timestamp difference files, which if ran directly after the first should be about right. This would be with the /XO switch which is "/XO : eXclude Older - if destination file exists and is the same date or newer than the source - don’t bother to overwrite it."
Other options that might help you in scripting that out are:
/XC | /XN : eXclude Changed | Newer files
/XL : eXclude "Lonely" files and dirs (present in source but not destination) This will prevent any new files being added to the destination.
/XX : eXclude "eXtra" files and dirs (present in destination but not source) This will prevent any deletions from the destination. (this is the default)
#*=============================================
#* Base variables
#*=============================================
$SourceFolder = "C:\SourceFolder"
$DestinationFolder = "C:\DestinationFolder"
$Logfile = "C:\Robocopy.log"
$Subject = "Robocopy Results: Copy Purpose - Location to Location"
$SMTPServer = "smtp.server.com"
$Sender = "Server <email#address.com>"
$Recipients = "User1 <email#address.com>"
$Admin = "Admin <email#address.com>"
$SendEmail = $True
$IncludeAdmin = $True
$AsAttachment = $False
#*=============================================
#* GMAIL variables
#*=============================================
#$SMTPServer = "smtp.gmail.com"
#$cred = New-Object System.Net.NetworkCredential("username", "password");
# Add "-UseSsl -Credential $cred" to the Send-MailMessage
#*=============================================
#* SCRIPT BODY
#*=============================================
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /MIR /R:2 /W:5 /LOG:$Logfile /NP /NDL
# The following attempts to get the error code for Robocopy
# and use this as extra infromation and email determination.
# NO OTHER CODE BETWEEN THE SWITCH AND THE ROBOCOPY COMMAND
Switch ($LASTEXITCODE)
{
16
{
$exit_code = "16"
$exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
}
8
{
$exit_code = "8"
$exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
}
4
{
$exit_code = "4"
$exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
}
2
{
$exit_code = "2"
$exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
}
1
{
$exit_code = "1"
$exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
}
0
{
$exit_code = "0"
$exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
}
default
{
$exit_code = "Unknown ($LASTEXITCODE)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Test log file size to determine if it should be emailed
# or just a status email
If ((Get-ChildItem $Logfile).Length -lt 25mb)
{
If ($IncludeAdmin)
{
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
} Else {
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
}
} Else {
# Creat the email body from the beginning and end of the $Logfile
$Body = "Logfile was too large to send." + (Get-Content $LogFile -TotalCount 15 | Out-String) + (Get-Content $LogFile | Select-Object -Last 13 | Out-String)
# Include Admin if log file was too large to email
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
#*=============================================
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*=============================================