Try/Catch or If/Else?

3

Hello Guys im having trouble trying to figure out how to make this script to work, im very new on scripting but i do understand most of it but still figuring out some things.

try {
    Test-Connection -Computername $_ -count 1 -ErrorAction Stop
} catch {
    $_.Exception.ErrorCode -eq 0x800706ba
} `
{
    $err = 'Unavailable (Host Offline or Firewall)'
}

try {
    Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString)
} catch {
    $_.CategoryInfo.Reason -eq 'UnauthorizedAccessException'
} `
{
    $err = 'Access denied (Check User Permissions)'
}

Write-Warning "$computer- $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append

What im looking for is for this script to test if the system responds or not. If True then next step would be to test credentials, and last would be to perform a get-wmiobject query. But if the system does not respond to ping then i want to catch the hostname that failed to respond ping, capture it and export it to a txt and do the same if the credential fails.

powershell
if-statement
try-catch
credentials
asked on Stack Overflow Dec 14, 2015 by Jose Antonio Chan • edited Dec 14, 2015 by sodawillow

3 Answers

7

try..catch is for handling terminating errors. Don't abuse it for status checks by forcing a check to fail hard when it doesn't need to. If you just want to test the availability of a system run Test-Connection with the parameter -Quiet as an if condition:

if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
  ...
}

If you need to cascade multiple checks you could do so in a more readable manner by inverting the checks and returning with an appropriate message:

function Test-Multiple {
  ...
  if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
    return "Host $_ unavailable."
  }

  $pw = Read-Host -AsSecureString
  if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
    return 'Login failed for user testuser.'
  }
  ...
}

If you want the information about ping or login failures in log files you can just append it to the respective files:

function Test-Multiple {
  ...
  if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
    $_ | Add-Content 'C:\path\to\unavailable.log'
    return
  }

  $pw = Read-Host -AsSecureString
  if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
    $_ | Add-Content 'C:\path\to\login_failure.log'
    return
  }
  ...
}
answered on Stack Overflow Dec 14, 2015 by Ansgar Wiechers • edited Dec 17, 2015 by Ansgar Wiechers
2

Personally, I can't stand the behavior of Test-Connection. Throwing an exception when it doesn't successfully ping isn't the behavior I want. Like, ever. I understand why they did it that way, but it's not how I ever want a ping to work. Test-Path doesn't throw an exception when the path is invalid. It just returns false. Why is Test-Connection so unfriendly?

WMI allows you to capture the actual status code, and it also allows you to easily control the timeout so it will function much more quickly.

I tend to use this:

$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000";
if ($Ping.StatusCode -eq 0) {
    # Success
}
else {
    # Failure
}

If I actually want to decode the ping status code:

$StatusCodes = @{
    [uint32]0     = 'Success';
    [uint32]11001 = 'Buffer Too Small';
    [uint32]11002 = 'Destination Net Unreachable';
    [uint32]11003 = 'Destination Host Unreachable';
    [uint32]11004 = 'Destination Protocol Unreachable';
    [uint32]11005 = 'Destination Port Unreachable';
    [uint32]11006 = 'No Resources';
    [uint32]11007 = 'Bad Option';
    [uint32]11008 = 'Hardware Error';
    [uint32]11009 = 'Packet Too Big';
    [uint32]11010 = 'Request Timed Out';
    [uint32]11011 = 'Bad Request';
    [uint32]11012 = 'Bad Route';
    [uint32]11013 = 'TimeToLive Expired Transit';
    [uint32]11014 = 'TimeToLive Expired Reassembly';
    [uint32]11015 = 'Parameter Problem';
    [uint32]11016 = 'Source Quench';
    [uint32]11017 = 'Option Too Big';
    [uint32]11018 = 'Bad Destination';
    [uint32]11032 = 'Negotiating IPSEC';
    [uint32]11050 = 'General Failure'
    };

$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000"

$StatusCodes[$Ping.StatusCode];
answered on Stack Overflow Dec 14, 2015 by Bacon Bits • edited Dec 14, 2015 by Bacon Bits
1

You could do it like this:

if(Test-Connection -Computername $_ -Count 2 -ErrorAction 0 -Quiet) {
    if(-not (Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString))) {
        $err = "Access denied (Check User Permissions)"
    }
} else {
    $err = "Unavailable (Host Offline or Firewall)"
}

if($err) {
    Write-Warning "$computer - $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append
}

I believe Test-Connection and Test-Credentials are meant to return $true or $false rather than an exception (if properly used), so you don't really need try/catch here.

answered on Stack Overflow Dec 14, 2015 by sodawillow • edited Dec 14, 2015 by sodawillow

User contributions licensed under CC BY-SA 3.0