Error handling

Hi,

I wrote the script below to check if a server is physical or virtual. If the server is virtual I output the name to a log where it’s picked up by another script that automates snapshots. If the server is physical, it gets moved to another log.

As you’ll notice my error is that I’ve not accounted for errors. The consequence is VMs that error are treated the same as a physical server. What I need to do is have servers that error go to a separate log.

I’ve done some reading on error handling, but it’s new for me and I was hoping for some assistance.

===============================
$servers = Get-Content C:\servers.txt

WMI query run against each server to check for model type.

VM’s are moved into C:\1_Patching_VM.txt. Servers in this log will be used in part 2 - Snapshot.

Physical servers will be moved to C:\2_Snapshot_Success_Log.txt as they don’t require a snapshot.

ForEach ($server in $servers)
{

$a = Get-WmiObject Win32_ComputerSystem -ComputerName $server| Select-Object -property name, model

if($a.model -eq “VMware Virtual Platform”) {Write-Output “$Server” | Out-File C:\1_Virtual_Server.txt -Append}
else {Write-Output “$Server” | Out-File C:\2_Snapshot_Success_Log.txt -Append}

}

You have C: and not C:\ also use test-connection to make sure the servers are up and running

Hi Wilfredo,

C:\ is a typo in my post and not causing an issue in my actual script. I’ll edit my post now. Thanks for pointing it out.

I don’t want to test-connections, I want to handle errors.

Regards,

John.

Note: for some reason when posting '' seems to be getting removed sometimes.

I’ve got this, any suggestions on improving the error handling would be appreciated.

$servers = Get-Content C:\servers.txt

ForEach ($server in $servers)
{
 try {
      $ErrorActionPreference = 'Stop'
      $currentcomputer = $server

$a = Get-WmiObject Win32_ComputerSystem -ComputerName $server| Select-Object -property name, model 

if($a.model -eq "VMware Virtual Platform") {Write-Output "$Server" | Out-File C:\1_Virtual_Server.txt -Append}
    else {Write-Output "$Server" | Out-File C:\2_Snapshot_Success_Log.txt -Append}

}
    catch {
      Write-output ('Failed to access "{0}" : {1} in "{2}"' -f $currentcomputer, `
      $_.Exception.Message, $_.InvocationInfo.ScriptName) | out-file C:\Script_fail.txt
    }
}

Errors should be handled where the error is occurring versus setting error handling to stop globally. Also, consider taking a different approach to your script to leverage Powershell. Testing the connection is just a logical step when connecting remotely to a server, if the server is off or name is mistyped in your servers.txt, WMI has to timeout trying to connect. It’s better practice to basically ping (Test-Connection) to the server before doing a WMI call.

$servers = Get-Content C:\Temp\servers.txt

$results = ForEach ($server in $servers){
    if (Test-Connection -ComputerName $server -Count 2 -Quiet) {
        #Server is reacheable\pingable
        try {
            #Rather than setting error preferences globally, try setting
            #them on the command that will produce the error and using try\catch
            #to catch and handle the error
            $model = Get-WmiObject Win32_ComputerSystem -ComputerName $server -ErrorAction Stop| Select-Object -ExpandProperty model 
        }
        catch {
            $model = "ERROR: {0} " -f $_.Exception.Message
        }
    }
    else {
        #Server is NOT reacheable\pingable
        $model = "OFFLINE"
    }
    New-Object -TypeName PSObject -Property @{Server=$Server;Model=$model;}
}

$results | Format-Table -AutoSize

When I run the above I’ll get output like below and know what error occurred on what server, the model (if the WMI call was successful) and if the computer was OFFLINE

Server        Model                                                                  
------        -----                                                                  
VMWARECOMPUTER   VMWare Virtual Platform                                                                  
BADCOMPUTER   OFFLINE                                                                
DC001     ERROR: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

At this point, you can do tasks like getting the server names into a file:

$results | Where{$_.Model -eq "VMware Virtual Platform"} | Select Server | Out-File C:\2_Snapshot_Success_Log.txt

or actually run a Powershell command to send emails, perform additional actions, etc. against a subset of the machines based on Where-Object query.

Hi Rob,

thanks for your reply and help.

This was my first attempt at including error handling. I think I can see the difference in the scripts with regard to how the errors are handled, but I don’t understand why. What’s wrong with how I done it (globally)?

I didn’t see the point in testing the connections because, if the server is offline it will be included in the error log and picked up during manual investigation. However, if it’s good practice to do so I’ll remember for the future.

In your script you advised using the code below. I can’t see where I would add code to output to a separate error log? I’ll spend more time looking at and no doubt work it out.

$results | Where{$_.Model -eq "VMware Virtual Platform"} | Select Server | Out-File C:\2_Snapshot_Success_Log.txt
Regards,

John.

If you ran the WMI command with no error handling, it would continue to process all of the servers and write errors and output to the console. This is because the $ErrorActionPreference is set to Continue. You are not only setting that variable in the script, you are changing that preference for the session for any other commands or scripts that are run. Rather than risk breaking other things, personally I handle the error where they occur and not set something to affect a large scope. In this script we are handling a single error instance, but if you had a 1000 line script I would handle the errors through my debugging and not do any global changes.

With that said, to get errors only, you would do something like this:

$results | Where{$_.Model -like "ERROR:*"} | Select Server | Out-File C:\Error.txt