If statement in PowerShell dependant on if RPC Server is unavailable - powershell

I'm writing a script in PowerShell that ideally would gather information from another server. If it's not able to reach that server, I want to prompt it to have the user manually enter the information. I know how to do all of these, but I'm getting hung up when the RPC Server is unavailable. I will also say that I know how to fix the error when it occurs, but I do not want to rely on my end users to have to go in and fix this.
As an example, if I run:
Get-WmiObject Win32_ComputerSystem -Computer 10.5.21.94
the result I get back is:
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At line:1 char:1
+ Get-WmiObject Win32_ComputerSystem -Computer 10.5.21.94
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject],
COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
I'm trying to find a way to write an if statement that will check to see if the RPC server is available or not, but I'm not sure what to check in order to create a true/false variable. And again, I'm not really looking for someone to tell me how to write the if statement, I'm just trying to figure out any query I can run to determine if I can properly connect to this server and get a result back that can tell me to continue on or not.

An easy way to get around it in an if-statement is just to ignore potential errormessages with Erroraction and use a -not statement to check whether it can reach the destination or not and then append a $false value to a variable if it can't.
See the below example.
$status = ""
if (!(Get-WmiObject Win32_ComputerSystem -ComputerName 10.5.21.94 -ErrorAction SilentlyContinue)) {
Write-Host "Server is unavailable!"
$status += $false
}
else {
Get-WmiObject Win32_ComputerSystem -ComputerName 10.5.21.94
}
if ($status -eq $false) {
$Server = Read-Host "Please enter the destionation"
Get-WmiObject Win32_ComputerSystem -ComputerName $Server
}

There was a suggestion for a Try/Catch block, but since this is not a terminating error, it didn't work initially. Then I found this:
Try/catch does not seem to have an effect
There is an answer in there about making all errors terminating:
try {
$ErrorActionPreference = "Stop"; #Make all errors terminating
get-item filethatdoesntexist; # normally non-terminating
write-host "You won't hit me";
} catch{
Write-Host "Caught the exception";
Write-Host $Error[0].Exception;
}finally{
$ErrorActionPreference = "Continue"; #Reset the error action pref to default
}
This gave me exactly what I was looking for!

Related

How can I handle the remote server message by PowerShell?

I created a short PowerShell scrtipt in order to import a .reg file (an ODBC) to another server session.
I faced to this warning/issue.
The message is this (below):
The operation completed successfully.
+ CategoryInfo : NotSpecified: (The operation completed successfully.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : MYSERVERNAME
NotSpecified: (:) [], RemoteException
The script, install without problem the .reg file, but constantly I get the message.
This is my code:
#PARAMETERS - Variables
$Serverlist = Get-Content C:\ServerList.txt
try
{
Foreach ($ServerName in $Serverlist)
{
$session = New-PSSession -ComputerName $servername
Write-Host -Foregroundcolor Green "Copying ODBC Driver for $servername"
$copy_cmd = "C:\MYFILE.reg"
Copy-Item $copy_cmd \\$servername\C$\ -recurse -force;
Write-Host -Foregroundcolor Green "ODBC Successfully copied on $servername"
#$session = New-PSSession -ComputerName $servername
Invoke-Command -Session $session -ScriptBlock {
#Start-Process
reg import C:\CopiedFile.reg #This line generate the message
Write-Host -Foregroundcolor Green "ODBC was installed
}
catch
{
Write-Host "ERROR" -Foregroundcolour Red
exit
}
I tried to incapsulate the Invoke-Command or reg import in to try - catch statement, but the message still appear. I used another command, instead reg import, but the nothing change.
I can use this command line, but I would like to catch the error.
Write-Host -Foregroundcolor Green "ODBC is installed " } ##-ErrorAction SilentlyContinue
There is any way to get the eventually error or handle the message.
Thanks in advance.
If the try block does not generate a terminating error, it will not move into the Catch block. This is controlled by -ErrorAction parameter. So you can set
Invoke-Command Session $session -ScriptBlock {} -ErrorAction Stop
This will cause the Invoke-Command Cmdlet to generate terminating errors(if any error occurs) allowing catch block to execute.

Try, catch in powershell invoke-command

It is not going to "catch" block as part of invoke-command for the incorrect host using powershell
$server= #("correcthost","Incorrecthost")
foreach($server in $server)
{
Try{
Invoke-Command -ComputerName $server -ArgumentList $server -ScriptBlock {
$serverk=$args[0]
write-host $serverk
}
}
Catch
{
write-host "error connecting to $serverk"
}
}
I expect the catchblock getting executed as i am trying to incorrect host
but the actual output is not printing catch block
There are two issues. First, the variable $serverk is out of scope in the catch block. It's being used only on the remote computer, so it doesn't exist - or have value - on the local system.
Debugging any Powershell script should always start with turning on the strict mode, so warnings about uninitialized variables are generated. Like so,
Set-StrictMode -Version 'latest'
...<code>
The variable '$serverk' cannot be retrieved because it has not been set.
At line:12 char:41
+ write-host "error connecting to $serverk"
+ ~~~~~~~~
+ CategoryInfo : InvalidOperation: (serverk:String) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined
The fix is easy, just refer to $server that's the variable used in iterating $servers.
The second issue is caused by ErrorAction, or to be specific, not declaring one. Add -ErrorAction Stop to Invoke-Command and process the exception in catch block like so,
catch{
write-host "error connecting to $server`: $_"
}
error connecting to doesnotexist: [doesnotexist] Connecting to remote server doesnotexist failed...

Handling Domain Join Errors

I have created a basic script to add a PC to the domain. Although this works there is room for error, and I want to put in some error handling.
do {
Add-Computer -DomainName $Domain -Credential(get-credential)
} while (!$?)
Using !$? runs the while loop while the last command is not successful.
However, there are various errors that return. Whether the PC is off the network, incorrect user ID or password, or domain specification, I want to be able to handle those errors and display something meaningful.
One of the errors returned
Add-Computer : This command cannot be executed on target computer('PCName') due to
following error: Logon failure: unknown user name or bad password.
At line:1 char:13
+ Add-Computer <<<< -DomainName $Domain -Credential(get-credential);
+ CategoryInfo : InvalidOperation: (PCNAME:String) [Add-Computer], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.AddComputerCommand
Working with error handlers in lets say VBA, an error ID is given, and using if statements you can do something with it.
The FullyQualifiedErrorID in the error above is the same across all errors received for different reasons, so I do not believe I can use that.
How can I capture the specific error "Logon failure: unknown user name or bad password." or other errors and display a meaningful message so the admin can take appropriate action?
If nothing else you should be able to use the error message for distinguishing between errors:
do {
$joined = $true
$cred = Get-Credential
try {
Add-Computer -DomainName $Domain -Credential $cred -ErrorAction Stop
} catch {
$joined = $false
switch -regex ($_.Exception.Message) {
'.*unknown user name.*' { ... }
'.*domain does not exist.*' { ... }
...
default { 'Unexpected error' }
}
}
} until ($joined)
Note that you'll need to set the error action to Stop (-ErrorAction Stop), because otherwise the errors would be non-terminating and thus not catchable.
Use it with the -ErrorAction parameter:
Add-Computer ... -ErrorAction SilentlyContinue -ErrorVariable computerError
The ErrorVariable is an array, so the resulting error will be stored in:
$computerError[0]
To use the same variable over and over again, use a + in front of the var name:
Add-Computer -ErrorVariable +manyErrors
And the last error will always be:
$manyErrors[$manyerrors.count - 1]
To get the last error code if it has a corresponding Win32 error code then run the following
$manyErrors[$manyerrors.count - 1].Exception.InnerException.NativeErrorCode
And then if you gather the potential error codes you could do the following
if ($manyErrors[$manyerrors.count - 1].Exception.InnerException.NativeErrorCode -eq 1)
{
Write-Host error happened
}
elseif ($manyErrors[$manyerrors.count - 1].Exception.InnerException.NativeErrorCode -eq 2)
Write-Host other error happened
}

How can I tell if a specific ProcessName is running without crashing

Lets say there is an application with the name exampleService that should be running on Server1. This code works if it is running. However when it's not running, it crashes.
$application = Get-Process -ComputerName Server1 -Name "exampleService"
I get this crash if the application is not running. Is there a more graceful way of finding out if it's not running (without crashing)
Get-Process : Cannot find a process with the name "exampleService". Verify the process name and call the cmdlet again.
At line:1 char:16
+ $application = Get-Process -ComputerName Server1 -Name "exampleService"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Sampler:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
Also is it possible to launch the application on the Server if it's not running?
The Server is running Windows Server 2012. The PowerShell commands are being run from a Windows 7 64-bit PC.
Look at using -ErrorAction SilentlyContinue to keep that error from displaying. You can use that in an If statement to launch the application if it isn't running.
--Updated to include launching the remote process
If (-NOT (Get-Process -Computername Server1 -name "cmd" -ErrorAction SilentlyContinue)) {
Write-Host "Launch application"
$application = "c:\windows\system32\cmd.exe"
$start = ([wmiclass]"\\Server1\Root\CIMV2:win32_process").Create($application)
}
You could set the ErrorActionto SilentlyContinue (alias for that is -ea 0):
$application = Get-Process -ComputerName Server1 -Name "exampleService" -ea 0
Now you can check $application and start the application if its null.
I only wanted my script to continue on one particular Get-Process error, i.e. process not found. (and I preferred to use a Try/Catch). But I haven't done much powershell and had trouble locating the specific error.
Once I found I could look at FullyQualifiedErrorId and added the following to a general Catch block I located what I was after.
Write-Host ('FullyQualifiedErrorId: ' + $_.FullyQualifiedErrorId);
So as a full example which works for my situation:
Try {
$application = Get-Process -Name "exampleService" -ea Stop #note the -ea Stop is so try catch will fire whatever ErrorAction is configured
} Catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
If ($_.FullyQualifiedErrorId) {
If ($_.FullyQualifiedErrorId -eq "NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand"){
Write-Host "Presume not running. That is OK."; # or you could perform start action here
}
}
else {
throw #rethrow other processcommandexceptions
}
} Catch {
# Log details so we can refine catch block above if needed
Write-Host ('Exception Name:' + $_.Exception.GetType().fullname); # what to put in the catch's square brackets
If ($_.FullyQualifiedErrorId) {
Write-Host ('FullyQualifiedErrorId: ' + $_.FullyQualifiedErrorId); #what specific ID to check for
}
throw #rethrow so script stops
}

Write custom error to log on RPC server unavailable

I want to output a custom error message to a log, when a GWMI query fails. I can catch the exception, but apparently only the last one, because the output in my error file only has the name of the last computer in the list. I am using a list of computers I know do not have WMI enabled. There should be an entry for each one.
I have a list of domain computers in a text file, each on a single line, no trailing characters. I loop through the file to get network information, using GWMI. Some of the computers do not have WMI enabled, and I want to know which ones. My current script just throws a:
gwmi : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At line:12 char:17
+ $base = gwmi win32_networkadapterconfiguration -computername $comp | whe ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
whenever it loops through a machine that does not have wmi enabled.
This does not identify the computer that threw the exception. What I would like is for every time the RPC server is unavailable... error is thrown, for a custom error message to be written to a log with the name of the computer that threw the exception.
My script:
$computers = Get-Content -path f:\scripts\docs\computer_list_test.txt
if (F:\scripts\wmi_mac_output.txt){
rm F:\scripts\wmi_mac_output.txt
}
foreach ($comp in $computers) {
try
{
$base = gwmi win32_networkadapterconfiguration -computername $comp -ErrorAction Stop | where {$_.dnsdomain -eq "mydomain.com"}
$machine = $base.DNSHostName
$mac = $base.MACAddress
$ip = $base.IPAddress
"<COMPUTER>`n`tname: $machine`n`tMAC: $mac`n`tIP: $ip`n</COMPUTER>" | Out-File F:\scripts\wmi_mac_output.txt
}
catch [Exception]
{
if ($_.Exception.GetType().Name -eq "COMException")
{
"$comp has no winrm" > f:\scripts\docs\error.txt
}
}
}
Thank you.
I can catch the exception, but apparently only the last one, because the output in my error file only has the name of the last computer in the list.
The issue is that the error.txt is being overwritten instead of appended.
"$comp has no winrm" > f:\scripts\docs\error.txt
Change to:
"$comp has no winrm" >> f:\scripts\docs\error.txt