I am trying to run a Powershell script and display the error code and error message if it fails. It is supposed to output me a result in this format:
"FAILED;ErrorCode;ErrorMessage;"
Here is my script:
param([String]$Cab_Type)
$output
if(!(Test-Connection -Cn 165.100.10.10 -BufferSize 16 -Count 1 -quiet))
{
$output = "FAILED; " + $LASTEXITCODE + ";" + $error[0] + ";"
}
else
{
$output = "PASSED"
}
Write-Host $Cab_Type
Write-Host "<ScriptResult_Start>"
Write-Host $output
Write-Host "<ScriptResult_End>"
I am trying to intentionally ping an address that I know will fail.
When running the script, it returns me the error message but not the error code.
Does $LASTEXITCODE not return the error code of the script? Even if my script worked, does it only return 0 or 1? Is there any way of getting the actual error code of the script?
Perhaps this is what you are after?
# set up a hash with possible Ping status codes
$status = #{
11001 = 'Buffer Too Small'
11002 = 'Destination Net Unreachable'
11003 = 'Destination Host Unreachable'
11004 = 'Destination Protocol Unreachable'
11005 = 'Destination Port Unreachable'
11006 = 'No Resources'
11007 = 'Bad Option'
11008 = 'Hardware Error'
11009 = 'Packet Too Big'
11010 = 'Request Timed Out'
11011 = 'Bad Request'
11012 = 'Bad Route'
11013 = 'TimeToLive Expired Transit'
11014 = 'TimeToLive Expired Reassembly'
11015 = 'Parameter Problem'
11016 = 'Source Quench'
11017 = 'Option Too Big'
11018 = 'Bad Destination'
11032 = 'Negotiating IPSEC'
11050 = 'General Failure'
}
$server = '165.100.10.10'
$ping = (Get-WmiObject -Class Win32_PingStatus -Filter "Address='$server'").StatusCode
if (!$ping -or [int]$ping -ne 0) {
$err = if ( $status[[int]$ping]) { $status[[int]$ping] } else { "Unknown Failure" }
$output = "FAILED; $ping; $err"
}
else { $output = "PASSED" }
Write-Host $output
The above example outputs:
FAILED; 11010; Request Timed Out
There are multiple ways to handle errors and verifying the. First and foremost, I would like you to keep the main code under a try/catch block and capture the error message so that you get to know what exactly the error is. Below is the modification of your code.
param([String]$Cab_Type)
$output
try
{
if(!(Test-Connection -Cn 165.100.10.10 -BufferSize 16 -Count 1 -quiet))
{
$output = "FAILED; " + $LASTEXITCODE + ";" + $error[0] + ";"
}
else
{
$output = "PASSED"
}
Write-Host $Cab_Type
Write-Host "<ScriptResult_Start>"
Write-Host $output
Write-Host "<ScriptResult_End>"
}
catch
{
$_.Exception.Message
}
Also, go through about 1. TRY CATCH FINALLY in PS
Apart from that you can use $Error variable to get to know about all the errors.
Go through 2. About $Error variable and how to use it effectively
Also you need to understand 3. Powershell Exceptions and Everything you ever wanted to know
Hope it helps and gives you a direction.
Related
Im trying to get remote registry key vaules using this script, it works fine but throws a few null errors on certain units, I'd like it to just output the error text if it can't find powershell 5.1 key.
*clients are win 7 HP, Workgroup
*I know my logic is off (red if it's there, green if not) just trying to see what I can come up with myself before asking you guys for assistance
$ComputerName = get-content "C:\Users\David\Desktop\IPS.txt"
$Hive = 'LocalMachine'
$KeyPath = 'SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine'
$Value = 'PowerShellVersion'
$KeyPath1 = 'SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine'
$Value1 = 'PowerShellVersion'
Foreach ($Computer in $ComputerName) {
if (Test-Connection $computer -Count 2 -Quiet) {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $Computer)
$key = $reg.OpenSubKey("$KeyPath")
$Data = $key.GetValue($Value)
$reg1 = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $Computer)
$key1 = $reg1.OpenSubKey("$KeyPath1")
$Data1 = $key1.GetValue($Value1)
If (($data = "2.0") -and ($Data1 = "5.1.14409.1005")){
Write-Host "$Computer needs updated " -ForegroundColor Red
##Added to try and do some error checking [string]::IsNullOrEmpty($Data1)
##Added to try and do some error checking Write-Host $Data1
}else {
Write-Host ""
Write-Host "$Computer $Data" -ForegroundColor Green
Write-Host "$Computer $Data1" -ForegroundColor Green
Write-Host ""
}
}
}
Error I'm getting:
it's weird cause I can output the variable and it looks fine.
The error is occurring on line 18, which is:
$Data1 = $key1.GetValue($Value1)
Since the error indicates you are calling a method on a null-valued expression, that shows that the error has occurred because $key1 is null.
$key1, in turn, is expected to have a value after the previous line (17) is executed:
$key1 = $reg1.OpenSubKey("$KeyPath1")
OpenSubKey returns null when the operation failed. This would happen if the subkey did not exist. So your program needs to check whether $key1 is null after line 17. Then, instead of attempting to call a method on a null object, your program can put out an error message saying that the subkey could not be opened.
If (($data = "2.0") -and ($Data1 = "5.1.14409.1005")){
The above is always true as you are doing assignments (=) vs testing (-eq).
Code should read:
If (($data -eq "2.0") -and ($Data1 -eq "5.1.14409.1005")){
I need to test for three possible states of a remote computer: Online, No RPC Server, or No Response. I use a try-catch block to catch a non-terminating condition when the remote is online but the RPC server is unavailable. I understand why the following code doesn't return the status for No RPC Server but I do not know how I should proceed. Any assistance is greatly appreciated
$Status = ""
$hostname = Read-host("Enter Computer Name")
if (test-connection $hostname -Count 1 -ErrorAction SilentlyContinue){
$Status = "Online"
Try {
$x = gwmi -Class win32_ComputerSystem -ComputerName $hostname -ErrorAction Stop
}
Catch{
$Status = "No RPC"
Continue
}
}
Else{
$Status = "No Response"
}
$Status
If you don't need to do any other processing in the function there's no real reason to use the $Status variable at all. Just do a Return "No RPC" inside your Catch block(I'd also do the same inside your Else for consistency but that's entirely up to your preference).
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.
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
}
...
}
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];
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.
I am battling with a PowerShell script that captures all SQL Failed jobs for the past day and exports it to .CSV
Please view code below.
param (
#[string]$serverInstance = '03RNB-VSQLPRD4\SQLPRD04
)
begin {
[void][reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
}
process {
try {
Write-Verbose "List failed SQL Server jobs using SMO..."
$serverInstance = Get-Content "C:\MountSpaceCollector\SQLJobFailures\servers2.txt";
$server = new-object Microsoft.SqlServer.Management.Smo.server $serverInstance
$results = #()
$reasons = #()
$jobs = $server.jobserver.jobs | where-object {$_.isenabled}
# Process all SQL Agent Jobs looking for failed jobs based on the last run outcome
foreach ($job in $jobs) {
[int]$outcome = 0
[string]$reason = ""
# Did the job fail completely?
if ($job.LastRunOutcome -eq "Failed") {
$outcome++
$reasons += "Job failed: " + $job.name + " Result: " + $job.LastRunOutcome
# Did any of the steps fail?
foreach ($jobStep in $job.jobsteps) {
if ($jobStep.LastRunOutcome -ne "Succeeded") {
$outcome++
$reasons += "Step failed: " + $jobStep.name + " Result: " + $jobStep.LastRunOutcome
}
}
}
if ($outcome -gt 0) {
$jobFailure = New-Object -TypeName PSObject -Property #{
Name = $job.name
LastRunDate = $job.lastrundate
LastRunOutcome = $reasons
}
$results += $jobFailure
}
}
Write-Output $results | Export-CSV -Path 'C:\MountSpaceCollector\SQLJobFailures\SQLJobFailures.csv' -Delimiter '|'
}
catch [Exception] {
Write-Error $Error[0]
$err = $_.Exception
while ( $err.InnerException ) {
$err = $err.InnerException
Write-Output $err.Message
Write-Output $results
}
}
}
But It Exports all except the last field (LastRunOutcome). It only displays "System.Object[]"?
Can anyone please assist with this as I do not know what I am doing wrong?
It's because $Reasons is an object, more specifically an array. You need to format the reasons differently, as a string for instance, to be able to have it appear in the CSV normally.
Perhaps you meant to use the string $Reason that you declare, but don't use?
I have script:
$servers = "server01", "s02", "s03"
foreach ($server in $servers) {
$server = (New-Object System.Net.NetworkInformation.Ping).send($servers)
if ($server.Status -eq "Success") {
Write-Host "$server is OK"
}
}
Error message:
An exception occured during a Ping request.
I need to ping each server in $servers array and display status. I think, that Foreach statement is not properly used, but I'm unable to find out where is the problem. Thank you for your advice
You should not be modifying the value of $server within the foreach loop. Declare a new variable (e.g. $result). Also, Ping.Send takes the individual server name, not an array of server names as an argument. The following code should work.
Finally, you will need to trap the PingException that will be thrown if the host is unreachable, or your script will print out a big red error along with the expected results.
$servers = "server1", "server2"
foreach ($server in $servers) {
& {
trap [System.Net.NetworkInformation.PingException] { continue; }
$result = (New-Object System.Net.NetworkInformation.Ping).send($server)
if ($result.Status -eq "Success") {
Write-Host "$server is OK"
}
else {
Write-Host "$server is NOT OK"
}
}
}