Solved

Powershell error

Posted on 2014-01-08
9
914 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
9 Comments
 
LVL 70

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 70

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
Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

 

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 70

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 70

Accepted Solution

by:
Qlemo earned 500 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

What Is Transaction Monitoring and who needs it?

Synthetic Transaction Monitoring that you need for the day to day, which ensures your business website keeps running optimally, and that there is no downtime to impact your customer experience.

Question has a verified solution.

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

This article will help you understand what HashTables are and how to use them in PowerShell.
My attempt to use PowerShell and other great resources found online to simplify the deployment of Office 365 ProPlus client components to any workstation that needs it, regardless of existing Office components that may be needing attention.
The viewer will learn how to dynamically set the form action using jQuery.
In a recent question (https://www.experts-exchange.com/questions/29004105/Run-AutoHotkey-script-directly-from-Notepad.html) here at Experts Exchange, a member asked how to run an AutoHotkey script (.AHK) directly from Notepad++ (aka NPP). This video…

728 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