Powershell: error handling with try and catch - powershell

I'm writing a script and want to control the errors. However im having trouble finding information on error handling using the try, catch. I want to catch the specific error (shown below) and then perform some actions and resume the code. What code is needed for this?
This is the code i am running and im entering in a invalid username when prompted.
Get-WMIObject Win32_Service -ComputerName localhost -Credential (Get-Credential)
Get-WmiObject : User credentials cannot be used for local connections
At C:\Users\alex.kelly\AppData\Local\Temp\a3f819b4-4321-4743-acb5-0183dff88462.ps1:2 char:16
+ Get-WMIObject <<<< Win32_Service -ComputerName localhost -Credential (Get-Credential)
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

Can anyone figure out why I can't trap this exception when trying to trap exceptions of type [System.Management.ManagementException]?
PowerShell should be able to trap exceptions that match certain exception classes, but even though the exception class for below is [System.Management.ManagementException] it won't catch it in that catch block!
i.e:
Try
{
Get-WMIObject Win32_Service -ComputerName localhost -Credential (Get-Credential) -ErrorAction "Stop"
}
Catch [System.Management.ManagementException]
{
Write-Host "System.Management.ManagementException"
Write-Host $_
$_ | Select *
}
Catch [Exception]
{
Write-Host "Generic Exception"
Write-Host $_
$_ | Select *
}
Works the same as:
Try
{
Get-WMIObject Win32_Service -ComputerName localhost -Credential (Get-Credential) -ErrorAction "Stop"
}
Catch [Exception]
{
Write-Host "Generic Exception"
Write-Host $_
$_ | Select *
}
Doesn't make sense to me.
You could also catch the error in the Generic Exception catch block and then check the text to see if it matches the words you are after, but it is a bit dirty.

You must use -erroraction stop to enter into the try/catch or trap scriptblock. You can test this :
Clear-Host
$blGoOn = $true
while ($blGoOn)
{
trap
{
Write-Host $_.exception.message
continue
}
Get-WMIObject Win32_Service -ComputerName $computer -Credential (Get-Credential) -ErrorAction Stop
if ($?)
{
$blGoOn=$false
}
}

Related

Powershell Script - Re-Prompt Password Screen If PW is İncorrect

I have a script that will change all local administrator passwords with the below script. Script will prompt password for each server. (all servers have different passwords)
I want to re-prompt the credential screen if user enters wrong password but I couldn't handle it.
$servers = Get-Content "C:\Users\Administrator\Desktop\Scripts\TestServers.txt"
foreach($server in $servers)
{
$pingtest = Test-Connection -ComputerName $server -Quiet -Count 1 -ErrorAction SilentlyContinue
if($pingtest)
{
Write-Host($server + " is online")
try
{
$ServerSessions = New-PSSession -ComputerName $server -Credential 'Administrator'
}catch [System.UnauthorizedAccessException]
{
Write-Host("Credential is incorrect! Try again")
$ServerSessions = New-PSSession -ComputerName $server -Credential 'Administrator'
}
Invoke-Command -Session $ServerSessions -ScriptBlock {
# Windows Server Versiyonunu check edip parolayı ona göre set etmek için
Get-ComputerInfo | select WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer
$Password = Read-Host -AsSecureString
$UserAccount = Get-LocalUser -Name "Administrator"
$UserAccount | Set-LocalUser -Password $Password
}
else
{
Write-Host($server + " is offline, nothing to do")
}
}
}
I got this error when running the script:
<IP_Address> is online
New-PSSession : [<IP_Address>] Connecting to remote server <IP_Address> failed with the following error message : Access is denied. For more information, see the
about_Remote_Troubleshooting Help topic.
At line:12 char:31
+ ... rSessions = New-PSSession -ComputerName $server -Credential 'Administ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : AccessDenied,PSSessionOpenFailed
Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:22 char:37
+ Invoke-Command -Session $ServerSessions -ScriptBlock {
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
else : The term 'else' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path
is correct and try again.
At line:30 char:9
+ else
+ ~~~~
+ CategoryInfo : ObjectNotFound: (else:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Write-Host($server + " is offline, nothing to do")
If I use correct password, the script works fine.
Update
Solution
Below method works but I couldn't be able to handle catch statement with catch [System.UnauthorizedAccessException]. Instead I used catch [Exception]. It is not a good solution but it works fine for me now.
$servers = Get-Content "C:\Users\Administrator\Desktop\Scripts\TestServers.txt"
foreach($server in $servers)
{
$pingtest = Test-Connection -ComputerName $server -Quiet -Count 1 -ErrorAction SilentlyContinue
if($pingtest)
{
Write-Host($server + " is online")
$Creds = Get-Credential 'Administrator'
do{
try
{
$ServerSessions = New-PSSession -ComputerName $server -Credential $Creds -ErrorAction Stop
Write-Host ("$ServerSessions")
}catch [Exception]
{
Write-Host("Credential is incorrect! Try again")
$Creds = Get-Credential 'Administrator'
}
}while(!$ServerSessions)
Invoke-Command -Session $ServerSessions -ScriptBlock {
# Windows Server Versiyonunu check edip parolayı ona göre set etmek için
Get-ComputerInfo | select WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer
$Password = Read-Host -AsSecureString
$UserAccount = Get-LocalUser -Name "Administrator"
$UserAccount | Set-LocalUser -Password $Password
}
}
else {
Write-Host($server + " is offline, nothing to do")
}
}
Try/catch doesn't work if it is not a stopping error, so add the -ErrorAction Stop parameter to your session, and then wrap that try/catch inside a Do/While loop based on if you have a session or not and get new creds in the Catch part.
$Creds = Get-Credentials 'Administrator'
Do{
try
{
$ServerSessions = New-PSSession -ComputerName $server -Credential $Creds -ErrorAction Stop
}catch [System.UnauthorizedAccessException]
{
Write-Host("Credential is incorrect! Try again")
$Creds = Get-Credentials 'Administrator'
}
}While(!$ServerSessions)
Your curly braces for the if statement were incorrect. This is why properly indenting your code is of importance.
To retry the authentication, use a while loop that will keep looping until a new session is established. For example:
$servers = Get-Content "C:\Users\Administrator\Desktop\Scripts\TestServers.txt"
foreach($server in $servers)
{
$pingtest = Test-Connection -ComputerName $server -Quiet -Count 1 -ErrorAction SilentlyContinue
if($pingtest)
{
Write-Host "$server is online"
while ($True) {
try
{
$Cred = Get-Credential -UserName 'Administrator'
$ServerSession = New-PSSession -ComputerName $server -Credential $Cred
break
}catch [System.UnauthorizedAccessException]
{
Write-Host "Credential is incorrect! Try again"
}
}
try {
Invoke-Command -Session $ServerSession -ScriptBlock {
# Windows Server Versiyonunu check edip parolayı ona göre set etmek için
Get-ComputerInfo | select WindowsProductName, WindowsVersion, OsHardwareAbstractionLayer
$Password = Read-Host -AsSecureString
$UserAccount = Get-LocalUser -Name "Administrator"
$UserAccount | Set-LocalUser -Password $Password
}
finally {
Remove-PSSession $ServerSession
}
}
else {
Write-Host "$server is offline, nothing to do"
}
}

How to create Powershell custom error output?

I want to make a small PS script that checks the status of a service logon account against a server list.
What i need to do is, if a server is down, it shows a custom error message that tells me which server from the list is offline, instead of the default bulky red error message.
Here is what i came up with so far.
This is the RPC error powershell shows if something wrong with a server.
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At V:\HRG01\MPE_HRG01_Information-Technology\Share\ITSS-Core\Icinga Monitor Software\Service Check\Get-Service Log On.ps1:1 char:1
+ Get-WmiObject win32_service -ComputerName (Get-Content -path ".\serverlist.txt") ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
PS Exception
PS c:\> $Error[0].Exception.GetType().FullName
System.Runtime.InteropServices.COMException
I searched on the internet for a solution, and this is what i came up with at last, but of course not working.
$servers= Get-Content -path ".\serverlist.txt"
foreach ($server in $servers)
{
try {
Get-WmiObject win32_service -ComputerName $server |
Where-Object {$_.Name -eq "icinga2"} -ErrorAction Continue |
format-list -Property PSComputerName,Name,StartName
}
catch [System.Runtime.InteropServices.COMException]
{
Write-Host "ERROR: $Server connection error"
}
}
Tee-Object .\Results.txt -Append
Read-Host -Prompt "Press Enter to exit"
I'd really appreciate your help
The error is on Get-WmiObject not Where-Object. And you have to set error action to stop to catch terminating error.
$servers= Get-Content -path ".\serverlist.txt"
foreach ($server in $servers)
{
try {
Get-WmiObject win32_service -ComputerName $server -ErrorAction Stop |
Where-Object {$_.Name -eq "icinga2"} |
format-list -Property PSComputerName,Name,StartName
}
catch [System.Runtime.InteropServices.COMException]
{
Write-Host "ERROR: $Server connection error"
}
}
Tee-Object .\Results.txt -Append
Read-Host -Prompt "Press Enter to exit"

Try-Catch: Why I'm still having uncaught errors? [duplicate]

This question already has an answer here:
try, catch doesent seem to work
(1 answer)
Closed 5 years ago.
I'm running the following script to identify whether there is drive Z: on a server. It contains try/catch, but I'm still getting "RPC-server is not available" errors like this:
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At C:\Users\vlitovch\Documents\Get-DriveZRemotely.ps1:19 char:26
+ ... $a = Get-WmiObject Win32_LogicalDisk -ComputerName $($comp.DNS ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
for the hosts that are listed in AD, but don't exist. Why? And on the top of this for these hosts I have "Cannot get disks" error message as well, so they don't fall into the catch block.
function Get-Moment {
Get-Date -Format 'MM/dd/yyyy HH:mm:ss'
}
#if (Test-Path -Path $logFile1) {Remove-Item -Path $logFile1 }
ipmo ActiveDirectory
$Servers = Get-ADComputer -Filter 'Name -like "*"' -SearchBase 'OU=Production,OU=Windows,OU=Servers,DC=contoso,DC=com'
foreach ($comp in $Servers) {
"INFO $(Get-Moment) Host:$($comp.DNSHostName)" | Write-Output
try {
$a = Get-WmiObject Win32_LogicalDisk -ComputerName $($comp.DNSHostName)
} catch {
"EROR $(Get-Moment) Host:$($comp.DNSHostName) Couldn't reach the host!" | Write-Output
continue
}
if ($a) {
$diskz = $false
foreach ($disk in $a) {
if ($disk.DeviceID -eq 'Z:') {$diskz = $true}
}
if (!$diskz) {
"EROR $(Get-Moment) Host:$($comp.DNSHostName) Disk Z: is absent." | Write-Output
}
} else {
"EROR $(Get-Moment) Host:$($comp.DNSHostName) Cannot get disks" | Write-Output
}
}
Some things in powershell throw non-terminating errors, which do not trigger on-error events.
adding -ErrorAction Stop to the end of the Get-WmiObject command will force it to become terminating, and thus trigger the try{}catch{} block.

Can't catch GetWMICOMException

Code like this,
try { $NIC = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -filter "IPEnabled = $TRUE" }
catch [GetWMICOMException]
{
"Error 1"
}
catch [System.UnauthorizedAccessException]
{
"Error 2"
}
get an error like:
Can't find type [GetWMICOMException]:
catch [COMException] Same
catch [System.Runtime.InteropServices.COMException] just ignored
How can I catch it?
Get-WmiObject : RPC server was unavailable. (Exception HRESULT: 0x800706BA)
F:\PowerShell Scripts\Project1.NetReconfigurer\proj1.ps1:36 :33
+ $NIC = Get-WmiObject <<<< Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -filter "IPEnabled = $TRUE"
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
First. The error is a non-terminating error, so it just notifies. If you want to catch a non-terminating error, use -ErrorAction Stop. Also, I don't think COMException is an exception you can catch. Try this instead:
try { $NIC = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -filter "IPEnabled = $TRUE -ErrorAction Stop}
catch [System.UnauthorizedAccessException]
{
"Error 2"
}
catch [Exception]
{
if ($_.Exception.GetType().Name -eq "COMException") {
"Error 1"
}
}
I was running into the same issue, and ran across this page. Frode F has the right idea, but I was not satisfied with that answer, so I messed around until I got the behavior that I wanted. I thought I would add a bit in case anyone else runs across this page in the future :
As Frode said, you must use -ErrorAction Stop to catch this exception because it is non-terminating
You can catch a COMException if you use its fully-qualified name [System.Runtime.InteropServices.COMException]
If you want to only catch a COMException when the error record shows GetWMICOMException, as it does when thrown by Get-WMIObject, you can check the error record using an if statement and re-throw the exception if it is not the one you are looking for. Doing so is probably a little excessive in this case because your try block only contains one command. It is doubtful that Get-WMIObject would throw any other kind of COM Exception. However, this might be useful in a case where you had a long pipeline, or multiple commands in the try block.
Simpler version that will catch any COMException:
try { $NIC = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -Filter "IPEnabled = $TRUE" -ErrorAction Stop}
catch [System.Runtime.InteropServices.COMException]
{
"Error 1"
}
catch [System.UnauthorizedAccessException]
{
"Error 2"
}
More complex version that will only catch a COMException if it is the "GetWMICOMException" thrown by Get-WMIObject:
try { $NIC = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -Filter "IPEnabled = $TRUE" -ErrorAction Stop}
catch [System.Runtime.InteropServices.COMException]
{
if ($_.FullyQualifiedErrorId.StartsWith("GetWMICOMException")
{
"Error 1"
}
else { throw }
}
catch [System.UnauthorizedAccessException]
{
"Error 2"
}
You can only catch terminating errors. Add '-ErrorAction Stop' to Get-WmiObject to convert the error to terminating error and try again. On a side note, I would also suggest that you test connectivity to the target system before you run wmi queries against it using a ping request, it can speed up execution especially if you query lots of computers (wmi timeout can slow your script).
try {
$NIC = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computername -Credential $Credential -filter "IPEnabled = $TRUE"
}
catch
{
$_
}
I managed to handle any type of errors in ErrorAction mode "SilentlyContinue" using Try{}Catch and -ErrorVariable:
foreach ($Server in $Server_List)
{
$ErrorVar = $null
$Server_Model = $null
try
{
$Server_Info= Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Server -Credential $Credential -ErrorAction SilentlyContinue -ErrorVariable ErrorVar | Select Model
}
catch [System.UnauthorizedAccessException] # here $ErrorVar.Exception doesn't exist but $ErrorVar.ErrorRecord.Exception
{
$Msg = "Powershell cmdlet on $Server : DCOM unauthorized access"
$Msg | Write-Warning
}
if ($ErrorVar.Exception)
{
switch ($ErrorVar)
{
{ $_.Exception.GetType().Name -eq "COMException" }
{
$Msg = "Powershell cmdlet on $Server : RPC server is unavailable"
$Msg | Write-Warning
break
}
{ $_.Exception.GetType().Name -eq "ManagementException" }
{
$Msg = "Powershell cmdlet on $Server : user credentials cannot be used for local connections.`nRetrying without credential."
$Msg | Write-Warning
$Server_Info = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Server | Select Model # when the script is hosted on a computer within the Server_List
break
}
default
{
$Msg = "Powershell cmdlet on $Server : unexpected error"
$Msg | Write-Warning
}
}
}
if ($Server_Info)
{
$Server_Info_Pso = New-Object PSObject -Property #{
HostName = $Server
Model = $Server_Info.Model
}
}
else
{
$Server_Info_Pso = New-Object PSObject -Property #{
HostName = $Server
Model = 'Unavailable'
}
}
$Server_Info_PsoCol = $Server_Info_PsoCol + #($Server_Info_Pso)
}

Error handling - PowerShell script

I have the following powershell script that cycles through a list of hostnames and changes the DNS settings for the active interfaces:
$servers = Get-Content C:\users\kevin.todd\desktop\serverlist.txt
foreach($server in $servers)
{
Write-Host "Connect to $server..."
$nics = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $server -ErrorAction Inquire | Where{$_.IPEnabled -eq "TRUE"}
$newDNS = "10.100.10.81","10.100.10.82"
foreach($nic in $nics)
{
Write-Host "`tExisting DNS Servers " $nic.DNSServerSearchOrder
$x = $nic.SetDNSServerSearchOrder($newDNS)
if($x.ReturnValue -eq 0)
{
Write-Host "`tSuccessfully Changed DNS Servers on " $server
}
else
{
Write-Host "`tFailed to Change DNS Servers on " $server
}
}
}
The problem is on some hosts I get the following error:
Get-WmiObject : The RPC server is unavailable. (Exception from
HRESULT: 0x800706BA) At C:\Documents and
Settings\user1\desktop\changednsserver.ps1:20 char:26
+ $nics = Get-WmiObject <<<< Win32_NetworkAdapterConfiguration
-ComputerName $server -ErrorAction Inquire | Where{ $_.IPEnabled -eq
"TRUE"}
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject],
COMException
+ FullyQualifiedErrorId :
GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Existing DNS Servers You cannot call a method on a null-valued
expression. At C:\Documents and
Settings\user1\desktop\changednsserver.ps1:30 char:42
+ $x = $nic.SetDNSServerSearchOrder <<<< ($newDNS)
+ CategoryInfo : InvalidOperation:
(SetDNSServerSearchOrder:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
And I'm asked the following question by Powershell:
The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help
(default is "Y"):
I would like the script to just answer A - Yes to all and continue running the script. The problem is it just halts the script until I manually enter "A". How can I have it automatically answer and continue?
Well the short answer is to not tell it to stop in the first place:
$nics = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $server -ErrorAction Inquire | Where{$_.IPEnabled -eq "TRUE"}
Try:
$nics = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $server -ErrorAction SilentlyContinue | Where{$_.IPEnabled -eq "TRUE"}
If you need to see where Errors occur, you have also a possibility to define an error variable using the switch
-ErrorVariable $<variable>
If errors occur in a foreach loop, the Error variable will be an array of error messages you can later analyze by querying the defined error variable with an array index echo $<EVariable>[<index>]
it would be something like echo $MyErrorArray[0] to get the first occurred error in that array.
An elegant way would be to have all that added to a textbox/combobox for log purposes but the coice of error handling ananalyzing is yours.