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.

sodawillow
  • 12,497
  • 4
  • 34
  • 44
  • Hi, why are there multiple `scriptBlock`s in you `catch`es ? – sodawillow Dec 14 '15 at 13:56
  • Im a noob at this so i know my limits right now. I got those scriptblocks from a script i found and it was using try catch and other stuffs which really got me confused but at least was working. – Jose Antonio Chan Dec 15 '15 at 18:41

3 Answers3

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
  }
  ...
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Thanks but after this i want to export the computers that either failed to ping or got access denied to a txt or csv file with the return error when failed. – Jose Antonio Chan Dec 15 '15 at 16:21
  • @JoseAntonioChan Doesn't make a difference. See updated answer. If you need an actual status code (e.g. for the connection test) use [BaconBits' suggestion](http://stackoverflow.com/a/34270373/1630171). – Ansgar Wiechers Dec 15 '15 at 16:28
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];
Bacon Bits
  • 30,782
  • 5
  • 59
  • 66
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.

sodawillow
  • 12,497
  • 4
  • 34
  • 44