Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Powershell error

Posted on 2014-01-08
9
Medium Priority
?
972 Views
Last Modified: 2017-04-02
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/questions/15672388/robocopy-sending-email-attachment-appending-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: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingCurlyBraceInSwitchStatement
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>
0
Comment
Question by:LarryDAH
  • 4
  • 4
9 Comments
 
LVL 71

Expert Comment

by:Qlemo
ID: 39766697
And PS is correct about that. The 16 has to come between the two braces - review the code as posted originally.

Small rant: That PS code isn't very sophisticated - code repeated, unnecessary commands, missing indention and proper alignment ... . It works, however.
0
 

Author Comment

by:LarryDAH
ID: 39768549
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. .
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39768924
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
#*=============================================

Open in new window

0
Veeam Task Manager for Hyper-V

Task Manager for Hyper-V provides critical information that allows you to monitor Hyper-V performance by displaying real-time views of CPU and memory at the individual VM-level, so you can quickly identify which VMs are using host resources.

 

Author Comment

by:LarryDAH
ID: 39769639
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 -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
#*=============================================
0
 
LVL 71

Expert Comment

by:Qlemo
ID: 39769877
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.
# 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

Open in new window

0
 

Author Comment

by:LarryDAH
ID: 39771053
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_mtcc.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?
0
 
LVL 71

Accepted Solution

by:
Qlemo earned 2000 total points
ID: 39771366
Line 18 used the wrong var, and we need an additional check if "no change" is the result:
if ($exit_reason) { $exit_reason = $exit_reason.Substring(0, $exit_reason.Length-2) }

Open in new window

I recommend to keep this whole file on a network share, if you do not need to customize it depending on the machine. Just set source and target path vars, and then call the script to perform the work. Something like:
$SourceFolder = 'C:\source'
$DestinationFolder = 'D:\Target'
\\server\share\DoTheCopy.ps1

Open in new window

You could also use the script like a function, but you would need to replace instead $SourceFolder by  $args[0] and $DestinationFolder by $args[1]  in the script, and then call it:
\\server\share\DoTheCopy.ps1 'C:\Source' 'D:\Target'

Open in new window

0
 

Author Closing Comment

by:LarryDAH
ID: 39783351
Great help, thanks for working with me on this.
0

Featured Post

Microsoft Certification Exam 74-409

Veeam® is happy to provide the Microsoft community with a study guide prepared by MVP and MCT, Orin Thomas. This guide will take you through each of the exam objectives, helping you to prepare for and pass the examination.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A project that enables an administrator to perform actions within a user session context not just at the time of login but any time later on day(s) or week(s) later.
A recent project that involved parsing Tableau Desktop and Server log files to extract reusable user queries for use in other systems. I chose to use PowerShell to gather the data, and SharePoint to present it...
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.
Suggested Courses

916 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question