Powershell error trapping - powershell

I've got a script that does some server maintenance. Part of the job is to stop and restart services on the host server. The easiest way to tell when the service is fully back online again is attempt to connect to the admin port. Once you're able to connect to the admin port, than the service is up and ready for business. Simple enough. This bit of code will loop until it's back online:
function test-port {
$PortProbe = New-Object Net.Sockets.TcpClient
$ErrorActionPreference = "SilentlyContinue"
while ($PortProbe.Connected -eq 0){
$PortProbe.Connect("localhost",3041)
write-host "Server is off line... waiting for it to come online."
sleep 1
}
write-host "Server is back online!"
$ErrorActionPreference = "Continue"
$PortProbe.Close()
}
test-port
So in my script I do my work, restart services and then call "test-port" to have the script to wait for the service to come back online before proceeding.
The problem I'm running into is if I have a generic trap in the script, it's trapping the connection error when the port isn't ready yet.
Exception calling "Connect" with "2" argument(s): "No connection could be made because the target machine actively refused it 127.0.0.1:3041"
At line:6 char:27
+ $PortProbe.Connect <<<< ("localhost",3041)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I've got a trap statement at the end in case some really bizarre error happens, it'll kick off an e-mail alert. But it breaks the functionality of the test-port function.
In theory, I could trap this specific TCP/IP connection error and tell it to just continue on like nothing had happened, but I can't figure out how to tell what the class of the error message is. I thought it would be something like this:
trap [Net.Sockets.TcpClient] { #Don't Panic
}
But that's not right.
Any suggestions? Do I need to handle this completely differently?

Rather than dropping to sockets, why not use the Get-Service cmdlet in a loop to check the service's status e.g.:
do {
$svc = Get-Service -ComputerName Localhost w3svc
$connected = $false
if ($svc -and $svc.Status -eq 'Running') {
$connected = $true
}
Start-Sleep -Milliseconds 500
} while (!$connected)

Related

How to test connectivity to a remote computer in powershell

There are two ways suggested in the docs both of which are completely useless to determine if the remote computer is available for powershell remoting.
Test-Connection is useless because it sends only ping, however, the destination may be running Intel AMT and thus respond to ping or may not be running winRM service, so this provides no useful information.
Test-WSMan should test the availability of Windows RM service, but it only works, if the WinRM works, otherwise (the computer is off or WinRM is not running) it gives an error:
Test-WSMan : The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. Consult the logs and documentation for the WS-Management service running on the destination, most commonly IIS or WinRM. If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: "winrm quickconfig".
At line:2 char:1
Test-WSMan -ComputerName destinationcomputer
+ CategoryInfo : InvalidOperation: (destinationcomputer:String) [Test-WSMan], InvalidOperationException
+ FullyQualifiedErrorId : WsManError,Microsoft.WSMan.Management.TestWSManCommand
So I tried enveloping Test-WSMan in try-catch commands, but it still gives the error out, it is not really caught:
Try {
Test-WSMan -ComputerName destinationcomputer
} catch {
write-output "not working"}
Any idea how to do this? (i would be happy with Test-WSMan if I could get rid of the error and enforce it to return true or false)
Test $? after running test-wsman.
Test-WSMan destinationcomputer
if (! $?) {
'not working'
}
You could also do it this way since a failure returns no standard output:
if (!(Test-WSMan destinationcomputer)){
'not working'
}
See also Check if a command has run successfully
Also in powershell 7:
Test-WSMan destinationcomputer || 'not working'

Determining when machine is in good state for Powershell Remoting?

Update - the original question claimed that I was able to successfully perform an Invoke-Command and then shortly after was unable to; I thought it was due to processes going on during login after a windows upgrade.
It turns out the PC was actually starting, running a quick batch/cmd file, and then restarting. This is what was leading to being able to do PS Remoting and then suddenly not. The restart was quick enough after first boot that I didn't realize it was happening. Sorry for the bad question.
For the curious, the machine was restarting because of a remnant of the Microsoft Deployment Toolkit in-place upgrade process. The way MDT completes its task-sequence post-upgrade is problematic for many reasons, and now I've got another to count.
Old details (no longer relevant, with incorrect assumption that machine was not restarting after first successful Invoke-Command):
I'm automating various things with VMs in Hyper-V using powershell and powershell remoting. I'll start up a VM and then want to run some commands on it via powershell.
I'm struggling with determining when I can safely start running the remote commands via things like Invoke-Command. I can't start immediately as I need to let the machine start up.
Right now I poll the VM with a one second sleep between calls until the following function returns $true:
function VMIsReady {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)][object]$VM
)
$heartbeat = $vm.Heartbeat
Write-Host "vm heartbeat is $heartbeat"
if (($heartbeat -eq 'OkApplicationsHealthy') -or ($heartbeat -eq 'OkApplicationsUnknown'))
{
try
{
Invoke-Command -VMName $vm.Name -Credential $(GetVMCredentials) {$env:computername} | out-null
}
catch [System.Management.Automation.RuntimeException]
{
Write-Host 'Caught expected automation runtime exception'
return $false
}
Write-Host 'remoting ready'
return $true
}
}
This usually works well; however, after a windows upgrade has happened, there are issues. I'll get Hyper-V remoting errors of various sorts even after VMIsReady returns $true.
These errors are happening while the VM is in the process of first user login after upgrade (Windows going through "Hi;We've got some updates for your PC;This might take several minutes-Don't turn off your PC). VMIsReady returns true right as this sequence starts - I imagine I probably should be waiting until the sequence is done, but I've no idea how to know when that is.
Is there a better way of determining when the machine is in a state where I can expect remoting to work without issue? Perhaps a way to tell when a user is fully logged on?
You can use Test-WSMan.
Of run a script on the invoke that will receive a response from the server.
[bool]$Response | Out-Null
try{
$Response = Invoke-Command -ComputerName Test-Computer -ScriptBlock {return $true}
}catch{
return $false
}
if ($Response -ne $true){
return $false
}else{
return $true
}

Powershell Try Catch on Get-Printer

I have a little function to get all printer objects from a print server, the cmdlet throws an error if the spooler service is not available (e.g. the hostname is wrong). I want to catch that error message with a try catch, but it wont catch? Can someone explain this behavior to me?
Code:
Function GetAllPrinters
{
param
(
[Parameter(Mandatory=$true)]
[string]$PrintServerHostName
)
try {
$Printers = Get-Printer -ComputerName $PrintServerHostName
}
catch {
Write-Host "Could not receive information from the print server $PrintServerHostName."
exit 1001
}
return $Printers
}
Error Message:
Get-Printer : Der Spoolerdienst ist nicht erreichbar. Stellen Sie sicher, dass der Spoolerdienst ausgeführt wird.
In C:\Users\f.zedler\Desktop\GetPrintServerStatus.ps1:34 Zeichen:21
+ $Printers = Get-Printer -ComputerName $PrintServerHostName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (MSFT_Printer:ROOT/StandardCimv2/MSFT_Printer) [Get-Printer], CimException
+ FullyQualifiedErrorId : HRESULT 0x800706ba,Get-Printer
You're running into the concept of a Terminating versus a non-terminating error.
Some functions can throw an error but still continue without any issues, they might just throw an error to the error stream and keep on trucking. We'd call these a non-terminating error.
Others could leave you in a bad situation or cause issues, and it'd be better for them to error and stop. We call these terminating errors.
Terminating error have the nice value-add in that they are capable of triggering a try/catch block, while most other errors will not.
This function seems to be throwing a non-terminating error, which ignores try/catch blocks. You can override this though, by specifying a choice for -ErrorAction, which is a common parameter, one that's available for all cmdlets.
Your choices are this:
Inquire - ask you to continue or not
Continue - keep on trucking but write out error messages
SilentlyContinue - keep on trucking but sweep the error under the rug (silently!)
STOP - this is the one you want
So, in conclusion, since it looks like your function is ignoring your try/catch block, try and add -ErrorAction STOP. That will probably solve this problem.
Get-Printer -ErrorAction Stop
Try it with Get-Printer -ComputerName $PrintServerHostName -ErrorAction Stop or set $erroractionpreference to stop.
FYI
on my script below, I had to move the "-erroraction stop" option before the first pipe symbol.
$e=Get-Printer -ComputerName $lbpclist.SelectedItem -ErrorAction stop | Where-Object {$_.Name -like $txtPrinterName.Text

Win32Shutdown Generic Failure

I am putting together a script which will log off VDI sessions which have been in a disconnected state for over 10 hours. I have managed to get everything together except for the last hurdle - actually forcing a logoff.
ForEach ($Desktop in $VDIlist)
{
$win32OS = Get-wmiobject win32_operatingsystem -ComputerName $desktop.'DNS Name' -EnableAllPrivileges
write-host "Shutting down host $Desktop."DNS Name""
$win32OS.Win32Shutdown(4)
}
This results in the error below.
Exception calling "Win32Shutdown" : "Generic failure "
At line:1 char:1
+ $win32OS.win32shutdown(4)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WMIMethodException
This does not appear to happen when no argument is used
($win32os.win32shutdown()), but this also does not force the log off like I require.
As far as I have read the -EnableAllPrivileges parameter should allow for the remote log off and it does work if I have a live PCoIP session to the VDI I am attempting to shutdown but not when in a disconnected state.
Could anyone point me in the right direction?
Still not entirely sure why the first script is giving an error but I have instead switched to using VMWare View's built in PowerCLI snapin to produce the same result - just faster and more efficiently.
get-remotesession -state "Disconnected" | Where-Object {($_.duration -match 'Day' -or $_.duration -match '\d\d hours')} | Send-SessionLogoff
This will query the Horizon view server for any sessions with the "Disconnected state", it will then filter out any objects that have had a lifetime of less than 10 hours and log off anything that is left.
This requires VMware View PowerCLI PSSnippets to be loaded and connected to your view connection broken.

Using try/catch blocks with Lync 2010 cmdlets in powershell

I've written a function to wrap the the new-cssipdomain cmdlet with a try/catch block incase the sip domain already exists.
Code is:
function LHP-AddSIPDomain
{
param ( [string] $SIPDomain)
try
{
New-cssipdomain -id $SIPDomain
}
catch
{
Write-host "Lync specific exception occured adding SIP domain"
Write-host "Exception String:"+$_.Exception.Message
exit
}
}
LHP-AddSIPDOmain -SipDomain "Test206.com"
The output when the domain already exists is:
New-CsSipDomain : "SipDomain" with identity "Test206.com" already exists. To modify
the existing item, use the Set- cmdlet. To create a new item, use a different
identity. Parameter name: Identity
At S:\Scripts\LHP-AddSIPDomain.ps1:33 char:26
+ New-cssipdomain <<<< -id $SIPDomain
+ CategoryInfo : InvalidArgument: (Test206.com:String) [New-CsSipDomain],
ArgumentException
+ FullyQualifiedErrorId :
InvalidIdentity,Microsoft.Rtc.Management.Xds.NewOcsSipDomainCmdlet
This should be caught by the try/catch block.
I've tried adding the [system.exception] to the catch statement. I'ev also tried setting $erroraction=”Stop”. Neither made any different, the try/catch statement seems to be being ignored. I've used this type of code structure to capture errors from the new-aduser cmdlet and this seemed to work ok.
I have also considerd and tried using hte get-cssipdomin cmdlet first to check if the sip domain already exists, but I have a similar problem in that if you call get-cscsipdomain with a domain that doesn't exist it throws an error which I don't seem to be able to catch.
Any suggestions would be greatly appreciated.
TRY:
try
{
New-cssipdomain -id $SIPDomain -ERRORACTION SilentlyContinue
}
Maybe the command it self got a try/catch for errors.
You can perhaps have a look to this answer. It explains why try/catch is sometime not working.
Can't you just write :
$Res = New-cssipdomain -id $SIPDomain -ERRORACTION SilentlyContinue
And test the value of $Res ?
I guess the error you get is not a terminating error and that's why you can't catch it. Try to set the ErrorAction value to 'stop', that will make the error a terminating error and you'll be able to catch it in the catch block.