How can I check internet connection with PowerShell - powershell

I have a PowerShell script that need to check if there is internet connectivity before running the rest of the script. For the example using Test-Connection
if (Test-Connection example.com) {
Write-Host "Connection available"
# do-other things
} else {
Write-Host "No internet connection"
Exit
}
The problem with this approach is if there is no connection it gives this error message:
Testing connection to computer 'example.com' failed: Cannot resolve the | target name. and then the actual message follows No internet connection but the idea was to have it move to the else clause not giving that error message when there no connection.
For example when there is no internet connection the error message No internet connection is printed, that's it, nothing else. How can I achieve this.

Test-Connection doesn't seem to have something that we can catch with try-catch, but there is a -Quiet parameter that may do what you want. It translates the output to a simple boolean.
Testing without and with an internet connection:

Per #mukunda's answer, Test-Connection has a -Quiet switch that ignores errors and returns a boolean result - see the documentation for Test-Connection for the details.
Note there's some perhaps unexpected behaviour if you're testing multiple sites in a single call:
If any ping to a given target succeeds, $True is returned
In other words, even if some sites fail you'd still get $true returned.
Note that in general you can also suppress errors in cmdlets with -ErrorAction "SilentlyContinue", and check the return value after:
$result = Test-Connection -Ping "www.example.org" -ErrorAction "SilentlyContinue";
if ($null -eq $result)
{
Write-Host "No internet connection"
Exit
}
else
{
Write-Host "Connection available"
# do-other things
}

Related

PowerShell Test-Connection is returning that a device is down when it is not

I am using the following code in PowerShell to ping multiple devices and return whether they are up or down.
$Output= #()
$names = Get-content "C:\HHM\hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 4 -Delay 2 -ErrorAction SilentlyContinue){
$Output+= "$name,up"
Write-Host "$Name,up"
}
else{
$Output+= "$name,down"
Write-Host "$Name,down"
}
}
$Output | Out-file "C:\HHM\result.csv"
When I check some of the devices that are listed as down, they are actually up. Given the nature of these devices, I do not believe that the connection is intermittent and the connection was actually down when the code was run. Would any of the other Test-Connection Parameters potentially help this? I've already used Count 4 and Delay 2, but I'm not sure if any of the other parameters will solve the issue.
EDIT - Just to clarify, I am getting the correct up/down status most of the time. Every once and a while, the code will return that a device is down, but when I go to check the device, I have a connection.
This may not be your problem, but you didn't specify if the devices have a static IP or dynamic IP. If dynamic, I would suggest always running ipconfig /flushdns prior to running your script. I can not begin to tell you how many times I thought a device was down, or thought I was connecting to one device only to discover I was connecting to another device.

Suppress and handle stderr error output in PowerShell script

I wanted to capture SMB shares using PowerShell, but this doesn't work
# Cannot use CIM as it throws up WinRM errors.
# Could maybe use if WinRM is configured on all clients, but this is not a given.
$cim = New-CimSession -ComputerName $hostname
$sharescim = Get-SmbShare -CimSession $cim
So this led me to another method using net view and this is fairly ok if the host is Windows
# This method uses net view to collect the share names (including hidden shares like C$) into an array
# https://www.itprotoday.com/powershell/view-all-shares-remote-machine-powershell
try { $netview = $(net view \\$hostname /all) | select -Skip 7 | ?{$_ -match 'disk*'} | %{$_ -match '^(.+?)\s+Disk*'|out-null ; $matches[1]} }
catch { $netview = "No shares found" }
So, if the host is Linux, I get an error, and as you can see, I am trying above to suppress that error with try / catch, but this fails.
Obviously, this is because 'net view' is CMD so is not controllable by try / catch. So my question is: how can I a) suppress the system error below?, and b) handle this error when it happens (i.e. throw a "This host is not responding to 'net view'" or something instead of the error)?
System error 53 has occurred.
The network path was not found.
Stderr (standard error) output from external programs is not integrated with PowerShell's error handling, primarily because this stream is not only used to communicate errors, but also status information.
(You should therefore only infer success vs. failure of an external-program call from its exit code, as reflected in $LASTEXTICODE[1]).
However, you can redirect stderr output, and redirecting it to $null (2>$null) silences it[2]:
$netview = net view \\$hostname /all 2>$null | ...
if (-not $netview) { $netview = 'No shares found' }
[1] Acting on a nonzero exit code, which by convention signals failure, is also not integrated into PowerShell's error handling as of v7.1, but fixing that is being proposed in this RFC.
[2] Up to PowerShell 7.1.x, any 2> redirection unexpectedly also records the stderr lines in the automatic $Error collection. This problem has been corrected in v7.1
As an unfortunate side effect, in versions up to v7.0, a 2> redirection can also throw a script-terminating error if $ErrorActionPreference = 'Stop' happens to be in effect, if at least one stderr line is emitted.

Using PowerShell to identify a machine as a server or PC

I'm trying to write a PowerShell script that will give me a list if of roles and features if run on a server but if run on a client machine will say "Only able to execute command on a server."
I've played around with this script a lot and can get it to run on either a client machine or server (depending on what I've tweaked) but not both. Here's the latest iteration:
$MyOS="wmic os get Caption"
if("$MyOS -contains *Server*") {
Get-WindowsFeature | Where-Object {$_. installstate -eq "installed"
}}else{
echo "Only able to execute command on a server."}
What am I doing wrong?
The quotes around your wmic command will create the $MyOS variable with a String and not execute the command. Still, I would recommend you use native PowerShell commands such as Get-CimInstance. Like the $MyOS variable your if statement condition will always equal true as the quotes will make it a String.
$MyOS = Get-CimInstance Win32_OperatingSystem
if ($MyOS.Caption -like "*Server*") {
Get-WindowsFeature | Where-Object { $_. installstate -eq "installed" }
}
else {
Write-Output "Only able to execute command on a server."
}
You can also use the ProductType property. This is a (UInt32) number with the following values:
1 - Work Station
2 - Domain Controller
3 - Server
$MyOS = (Get-CimInstance Win32_OperatingSystem).ProductType
if ($MyOS -gt 1) {
Get-WindowsFeature | Where-Object { $_. InstallState -eq "installed" }
}
else {
Write-Output "Only able to execute command on a server."
}
Try to use '-like' instead of 'contains', it should work
Generally, I try to avoid pre-checks like this that make assumptions about functionality that may not be true forever. There's no guarantee that Get-WindowsFeature won't start working on client OSes in a future update.
I prefer to just trap errors and proceed accordingly. Unfortunately, this particular command produces a generic Exception rather than a more specifically typed exception. So you can't really do much other than string matching on the error message to verify specifically what happened. But there's very little that can go wrong with this command other than the client OS error. So it's pretty safe to just assume what went wrong if it throws the exception.
try {
Get-WindowsFeature | Where-Object { $_. InstallState -eq "installed" }
} catch {
Write-Warning "Only able to execute command on a server."
}
If you don't want to accidentally hide an error that's not the client OS one, change the warning message to just use the actual text from the error. This also gets you free localization if you happen to be running this code in a location with a different language than your own.
Write-Warning $_.Exception.Message

Using PowerShell to test an FTP connection

I have a PowerShell script that performs checks on some servers, for example Test-Connection for PING.
I wish to check one of the servers that has an FTP server by performing an "FTP Open" command. I don't need to log in or upload/download any files, just need to know if the FTP server responds or not.
Most of my research on the internet points to setting up credentials and importing proprietary modules to connect, perhaps uploading or downloading files, but I just need a simple method to open the connection and tell me either true or false if there is a responding server.
The server I am running this script from should have minimal software installed, but if it needs anything, preferably Microsoft and from their website.
Test-NetConnection is native Powershell and can be used to test simple connectivity on FTP Port 21:
Test-NetConnection -ComputerName ftp.contoso.com -Port 21
There's nothing like FTP command "open".
But maybe you mean to just test that the server listens on FTP port 21:
try
{
$client = New-Object System.Net.Sockets.TcpClient("ftp.example.com", 21)
$client.Close()
Write-Host "Connectivity OK."
}
catch
{
Write-Host "Connection failed: $($_.Exception.Message)"
}
If you want to test that the FTP server is behaving, without actually logging in, use FtpWebRequest with wrong credentials and check that you get back an appropriate error message.
try
{
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftp.example.com")
$ftprequest.Credentials = New-Object System.Net.NetworkCredential("wrong", "wrong")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::PrintWorkingDirectory
$ftprequest.GetResponse()
Write-Host "Unexpected success, but OK."
}
catch
{
if (($_.Exception.InnerException -ne $Null) -and
($_.Exception.InnerException.Response -ne $Null) -and
($_.Exception.InnerException.Response.StatusCode -eq
[System.Net.FtpStatusCode]::NotLoggedIn))
{
Write-Host "Connectivity OK."
}
else
{
Write-Host "Unexpected error: $($_.Exception.Message)"
}
}

Powershell Try Catch invoke-sqlcmd

I am having problems catching an error in PowerShell when a connection fails to a SQL Server using Invoke-Sqlcmd. This is some generic code to demonstrate the issue:
CLS
$server = "Localhost\fake"
try
{
Invoke-Sqlcmd -Query "SELECT DB_NAME() as [Database]" -Server $server
}
catch
{
Write-Host "Error connecting to server " $server
}
I get the following error:
Invoke-Sqlcmd : A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name
is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)
I was expecting to get the one line statement: "Error connecting to server Localhost\fake"
It would appear that error is considered non-terminating which is a bit odd. Try the Invoke-SqlCommand with an additional parameter: -ErrorAction Stop. If there error is non-terminating, this will convert it to a terminating error that you can catch.
Posting additional info to supplement the answer by #KeithHill as an answer since it is too long for a comment.
If the error record was created by the Write-Error commandlet it is non-terminating and subject to the behavior specified by the -ErrorAction argument or the $ErrorActionPreference system variable. Errors using throw are terminating. See the documentation for Write-Error (Note that PowerShell 4.0 and below do not have the -Exception parameter mentioned in the web page.) and about_Throw in the PowerShell help system.
If you want to add custom error information and make it a terminating error, throw from your catch block as follows:
catch
{
throw (New-Object System.Exception "Error connecting to server $($server).", $_.Exception)
}
You can use Write-Error if you want termination to behave as specified by the -ErrorAction argument. In PowerShell 4.0 and below the Write-Error commandlet does not allow a -Exception argument and therefore will not provide an InnerException. That means that the caller would have to examine the collection in the $Error system variable if it needs to determine the original exception. In later versions you can Use Write-Error -Message "Some additional error information." -Exception $_.Exception in your catch block.
Try
CLS
$server = "Localhost/fake"
try
{
Invoke-Sqlcmd -Query "SELECT DB_NAME() as [Database]" -Server $server
}
catch
{
$_ | Out-Null
Write-Host "Error connecting to server " $server
}
This will capture the error and redirect it to null and display your write-host