LarryDAH
asked on
Powershell error
I am testing a powershell script that should check for the exit code of a task after it runs but the email it sends does not show that the task was successful even tho it shows that way on the server. Usually the error is unknown, but it varies.
I am using a script that I found at
stackoverflow.com/question s/15672388 /robocopy- sending-em ail-attach ment-appen ding-file- name
It runs robocopy fine but the email part is what is failing.
When I do a debug starting at the switch command (see the snip below) I get this error message and the Powershell IDE is putting a red tilde after ($LASTEXITCODE)
PS C:\scripts> Switch ($LASTEXITCODE)
Missing '{' in switch statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordE xception
+ FullyQualifiedErrorId : MissingCurlyBraceInSwitchS tatement
PS C:\scripts>
Below is the first part of the code. The website has the entire thing.
<snip>
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
Switch ($LASTEXITCODE)
16
{
{
$exit_code = "16"
$exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
}
</snip>
I am using a script that I found at
stackoverflow.com/question
It runs robocopy fine but the email part is what is failing.
When I do a debug starting at the switch command (see the snip below) I get this error message and the Powershell IDE is putting a red tilde after ($LASTEXITCODE)
PS C:\scripts> Switch ($LASTEXITCODE)
Missing '{' in switch statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordE
+ FullyQualifiedErrorId : MissingCurlyBraceInSwitchS
PS C:\scripts>
Below is the first part of the code. The website has the entire thing.
<snip>
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
Switch ($LASTEXITCODE)
16
{
{
$exit_code = "16"
$exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
}
</snip>
ASKER
Qlemo, if you have something simpler I would be happy to try it. I all want to do is run robocopy with Task Scheduler then get an email with the exit code. .
No, nothing (much) simpler. You have to check for all the error codes one by one ... But to show what I mean, I've incorporated your robocopy line already.
#*=============================================
#* 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 /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
$exit_code = $LASTEXITCODE
# The following attempts to get the error code for Robocopy
# and use this as extra information and email determination.
Switch ($exit_code)
{
16 { $exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
8 { $exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
4 { $exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
2 { $exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
1 { $exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
0 { $exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
break
}
default {
$exit_code = "Unknown ($exit_code)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Defining a parameter hash table supplying all switches and values which are the same for all cases
$parms = @{
From = $Sender
To = $Recpients
Subject = $Subject
DeliveryNotificationOption = 'onFailure'
SmtpServer = $SMTPServer
}
# 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) { $parms += @{CC = $Admin} }
if ($AsAttachment) {
$parms += @{ Body = "Robocopy results are attached."; Attachment = $Logfile }
} Else {
$parms += @{ Body = (Get-Content $LogFile | Out-String) }
}
} Else {
# Create the email body from the beginning and end of the $Logfile
$parms += @{ 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
$parms += @{ Cc = $Admin }
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
Send-MailMessage @parms
#*=============================================
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*=============================================
ASKER
Thanks for cleaning up the code. I cut the part about mailing me the log file. I can look at that if the job fails but not otherwise. I just ran from PowerShell and got my email with the subject below:
Subject: Robo: CMF MTCC data to CA-6458-10 Z drive : Unknown Reason EC: Unknown (3)
Body: If Exit Code (EC) is not 1 or 0 there may have been a problem
When I went to the server and ran the script by starting the scheduled task it shows that it completed successfully with Event ID 102 and Op Code 2, but the email subject was the same as above.
So it looks like the ps1 file runs fine but is still not getting the exit code. If Event ID 02 means the job completed successfully is there some way to use that to trigger the email?
-------------------------- ---------- ---------- ---------- --
The code that I ran:
#*======================== ========== ========== =
#* SCRIPT BODY
#*======================== ========== ========== =
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
$exit_code = $LASTEXITCODE
# The following attempts to get the error code for Robocopy
# and use this as extra information and email determination.
Switch ($exit_code)
{
16 { $exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
8 { $exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
4 { $exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
2 { $exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
1 { $exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
0 { $exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
break
}
default {
$exit_code = "Unknown ($exit_code)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Create the email body from the beginning and end of the $Logfile
$Body = "If Exit Code (EC) is not 1 or 0 there may have been a problem" + (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 -Subject $Subject -Body $Body -DeliveryNotificationOptio n onFailure -SmtpServer $SMTPServer
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOptio n onFailure -SmtpServer $SMTPServer
#*======================== ========== ========== =
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*======================== ========== ========== =
Subject: Robo: CMF MTCC data to CA-6458-10 Z drive : Unknown Reason EC: Unknown (3)
Body: If Exit Code (EC) is not 1 or 0 there may have been a problem
When I went to the server and ran the script by starting the scheduled task it shows that it completed successfully with Event ID 102 and Op Code 2, but the email subject was the same as above.
So it looks like the ps1 file runs fine but is still not getting the exit code. If Event ID 02 means the job completed successfully is there some way to use that to trigger the email?
--------------------------
The code that I ran:
#*========================
#* SCRIPT BODY
#*========================
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
$exit_code = $LASTEXITCODE
# The following attempts to get the error code for Robocopy
# and use this as extra information and email determination.
Switch ($exit_code)
{
16 { $exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
8 { $exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
break
}
4 { $exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
2 { $exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
1 { $exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
break
}
0 { $exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
break
}
default {
$exit_code = "Unknown ($exit_code)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Create the email body from the beginning and end of the $Logfile
$Body = "If Exit Code (EC) is not 1 or 0 there may have been a problem" + (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 -Subject $Subject -Body $Body -DeliveryNotificationOptio
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOptio
#*========================
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*========================
The opcode of the task doesn't need to be the same as the error code of RoboCopy. The task gets its return code from the shell, which runs perfect (hence = 2 - don't ask me why 2 is "ok").
As I suspected, RoboCopy return codes are bit mapped. The evaluation of return codes in the original script is nonsense, e.g. 3 is 2 + 1, "Extra Files" and "Copy Successful". That is usually ok, just some files found in the destination which are not in the source tree.
So, in fact we would have to see whether serious errors are reported, that are those 8 and higher. Or we just decode the bitmap. I've removed all the additional flags (IncludeAdmin, SendEmail), as you are not using it at the moment. I've also removed some other stuff, plus simplified the sending of parts of the log file beginning and end.
As I suspected, RoboCopy return codes are bit mapped. The evaluation of return codes in the original script is nonsense, e.g. 3 is 2 + 1, "Extra Files" and "Copy Successful". That is usually ok, just some files found in the destination which are not in the source tree.
So, in fact we would have to see whether serious errors are reported, that are those 8 and higher. Or we just decode the bitmap. I've removed all the additional flags (IncludeAdmin, SendEmail), as you are not using it at the moment. I've also removed some other stuff, plus simplified the sending of parts of the log file beginning and end.
# For robocopy options see http://ss64.com/nt/robocopy.html
Robocopy $SourceFolder $DestinationFolder /E /DCOPY:T /CopyAll /R:2 /W:2 /TEE /XA:SHTO /XJ /MT:16 /XF thumbs.db ~*.* /V /$LogFile /NP
$exit_code = $LASTEXITCODE
# The following attempts to get the error code for Robocopy
# and use this as extra information and email determination.
# The code is a bitmap
16, 8, 4, 2, 1 | % {
Switch ($exit_code -band $_)
{
16 { $exit_reason += "***FATAL ERROR***, " ; break }
8 { $exit_reason += "**FAILED COPIES**, " ; break }
4 { $exit_reason += "*MISMATCHES*, " ; break }
2 { $exit_reason += "EXTRA FILES, " ; break }
1 { $exit_reason += "Copy Successful, " ; break }
}
}
$exit_code = $exit_code.Substring(0, $exit_code.Length-2)
if ($exit_code -eq 0) { $exit_reason = 'No Change' }
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Create the email body from the beginning and end of the $Logfile
$Body = "If Exit Code (EC) is greater than 7 there is a problem " + (Get-Content $LogFile | Select -First 15 -Last 13)
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
ASKER
Thanks, that script is much cleaner. When I ran it from the PS IDE it completes with this msg:
-------------------------- ----------
Method invocation failed because [System.Int32] doesn't contain a method named 'Substring'.
At C:\scripts\Robocopy_data_m tcc.ps1:38 char:1
+ $exit_code = $exit_code.Substring(0, $exit_code.Length-2)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
-------------------------- ---------- ---
My email has the subject "EXTRA FILES, EC: 2" and since that exit code is less than 7 I should be good to go, right?
If so, I have another question. I will be using this on 8-10 servers, not a lot, but when my mail server changes or the destination server is replaced I have to revisit each script and make the change. Is there a way to pull the $SMTPServer and $Destinationfolder from a centralized location?
--------------------------
Method invocation failed because [System.Int32] doesn't contain a method named 'Substring'.
At C:\scripts\Robocopy_data_m
+ $exit_code = $exit_code.Substring(0, $exit_code.Length-2)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
--------------------------
My email has the subject "EXTRA FILES, EC: 2" and since that exit code is less than 7 I should be good to go, right?
If so, I have another question. I will be using this on 8-10 servers, not a lot, but when my mail server changes or the destination server is replaced I have to revisit each script and make the change. Is there a way to pull the $SMTPServer and $Destinationfolder from a centralized location?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Great help, thanks for working with me on this.
Small rant: That PS code isn't very sophisticated - code repeated, unnecessary commands, missing indention and proper alignment ... . It works, however.