Solved

Powershell error

Posted on 2014-01-08
9
844 Views
Last Modified: 2014-01-15
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 68

Expert Comment

by:Qlemo
Comment Utility
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
Comment Utility
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 68

Expert Comment

by:Qlemo
Comment Utility
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
 

Author Comment

by:LarryDAH
Comment Utility
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
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 68

Expert Comment

by:Qlemo
Comment Utility
The opcode of the task doesn't need to be the same as the error code of RoboCopy. The task get's it 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
Comment Utility
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 68

Accepted Solution

by:
Qlemo earned 500 total points
Comment Utility
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
Comment Utility
Great help, thanks for working with me on this.
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Hi all.   The other day I had to change the passwords for a bunch of users on the fly. Because they were so many, I decided to do it in an automated way and I would like to share it with you all.   If you are not doing it directly in a Domain Co…
How to sign a powershell script so you can prevent tampering, and only allow users to run authorised Powershell scripts
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

772 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now