Tag Archives: Test-Connection

Fake the Data when Missing the CSV

Earlier today, I had an opportunity to help a coworker run ICMP requests against a series of IP addresses. They were being pulled in from a CSV file, which contained hostnames with matching IP addresses. If for some reason the import failed, such as the file wasn’t where it was supposed to be, he wanted to supply a single IP address he knew would fail in place of the successful CSV import. I suspect this was for testing purposes. Whatever the reason, I obliged him in his quest and request. I leaned heavily on a recently written and published article of mine: Hash Table to CSV, Revisited. It lined up perfectly with what I was working toward in this instance.

The goal here was to fake out an upcoming Foreach loop in such a way that no coding changes needed to be made, whether or not the CSV file existed, etc. That was the end goal for this work: leave the later code doing what it had been doing previously.

We’re, mostly, going to start with the code. In this first example, you’ll have to believe me that the file IPs.csv does in fact exist, and it contains two columns. One is the hostname, which we don’t actually even use in this example, and the other is the IP address. As expected, this column contains a properly formatted IP address. As everything is in place, the CSV file is imported and saved in $IPAddresses. Following that, the code loops through the contents of our new variable. For every entry, it returns True or False depending on whether it’s able to return a successful ping request or not, against that IP address.

try {
    $IPAddresses = Import-Csv -Path 'C:\IPs.csv'
} catch {
    $IPAddresses = [PSCustomObject]@{Host = 'Test';IP = '8.8.8.9'}
} # End try-catch

Foreach ($IP in $IPAddresses) {
    If (Test-Connection $IP.IP -Count 1 -Quiet) {
        $true
    } Else {
        $false
    } # End If-Else.
} # End Foreach.

Pinging 8.8.8.8 [8.8.8.8] with 32 bytes of data:
Reply from 8.8.8.8: bytes=32 time=13ms TTL=50
Ping complete.
True
Pinging 216.58.193.206 [216.58.193.206] with 32 bytes of data:
Reply from 216.58.193.206: bytes=32 time=12ms TTL=53
Ping complete.
True

In this next example, we’ve altered the CSV file extension. This attempted CSV import will fail, and will therefore execute our code in the catch block. This will make it appear as though we’ve imported the CSV; however, because we recognize this IP address and its guaranteed to fail, we’ll know there was a problem with the CSV import.

try {
    $IPAddresses = Import-Csv -Path 'C:\IPs.csvvvvvv'
} catch {
    $IPAddresses = [PSCustomObject]@{Host = 'Test';IP = '8.8.8.9'}
} # End try-catch

Foreach ($IP in $IPAddresses) {
    If (Test-Connection $IP.IP -Count 1 -Quiet) {
        $true
    } Else {
        $false
    } # End If-Else.
} # End Foreach.

Pinging 8.8.8.9 [8.8.8.9] with 32 bytes of data:
Request timed out.
Ping complete.
False

There are other ways to handle this, yes. But remember, the co-worker needed data to be produced just as it would be if it worked (the import part), even though it didn’t. It had something to do with JSON, schemas, and Azure Log Analytics. It was good enough for him, so it was good enough for me, too.

Give a Parameter a Default Value (Part III)

Part II: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-ii-2015

Didn’t know I’d be back for a third installment of this topic, but the $PSDefaultParameterValues variable is still such a huge convenience. While punching out commands today, I had a thought: Can $PSDefaultParameterValues be a bit more dynamic?

I’m not going to fully introduce the $PSDefaultParameterValues again, but I’ll leave a quick explanation. This hash table allows us to instruct cmdlets and functions to use a default value for one of their parameters. Here’s a couple examples borrowed from the first two $PSDefaultParameterValues posts. To read the first two posts, follow the links at the bottom of this post. Both of these examples do the same thing, outside of the fact they’re written for different cmdlets.

$PSDefaultParameterValues.Add('Test-Connection:Count','1')
$PSDefaultParameterValues = @{'Get-Help:ShowWindow' = $true}

After our variable is set, anytime I use the Test-Connection cmdlet, it will include the -Count parameter with the parameter value of 1. Additionally, when I use Get-Help, it will always include the -ShowWindow parameter, without the need to enter it myself. The examples show how to set the variable both by using the Add method, and as a hash table — which is really all the variable is anyway.

I had a recent need to ensure the -Server parameter value on the Get-ADPrincipalGroupMembership cmdlet used the PDC Emulator. If you see the error: “The operation being requested was not performed because the user has not been authenticated, then you may need to ensure you’re using the PDCE when using this cmdlet, too. Anyway, I didn’t want to hard code the PDCE value in $PSDefaultParameterValues, so I tried something new, and you’re reading this today, because it worked. Here’s how I updated the $PSDefaultParameterValues variable to dynamically obtain the value it should use.

$PSDefaultParameterValues.Add('Get-ADPrincipalGroupMembership:Server',"$((Get-ADDomain).PDCEmulator)")
$PSDefaultParameterValues | Format-Table -AutoSize

Name                                  Value
----                                  -----
Get-ADPrincipalGroupMembership:Server DC01.mydomain.com

Now, whenever I run the Get-ADPrincipalGroupMembership cmdlet (inside the PowerShell session where the $PSDefaultParameterValues has been set), it’ll include the -Server parameter and the value of the PDCE — which ever server that is, and without the need to hard code its name. So yeah, we can use dynamic content in $PSDefaultParameterValues.

Part I: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-2015
Part II: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-ii-2015

Update: A buddy at work alerted me to the fact that pointing to the PDCE is no longer necessary for the Get-ADPrincipalGroupMembership cmdlet. Thanks, Logan!

Give a Parameter a Default Value

Part II: http://tommymaynard.com/quick-learn-give-a-parameter-a-default-value-part-ii-2015/

If you’re into Windows PowerShell, then you probably think a little like me. You want to do things fast, and efficiently. There’s a reason we pound out commands and write scripts: we want to make the most of our time, and PowerShell let’s us do that. With that in mind, I decided I should modify a cmdlet I often use, so that using it takes less time, by default.

The Test-Connection cmdlet does for us what ping always has. It sends ICMP request packets to a remote computer or device. If the remote computer or device sends us a reply, then we know the device is up. It should be stated, that ICMP replies can be restricted so that a computer or device that is up, may not reply. Although I’ve opted to use Test-Connection for my example, this concept can be used with any number of other cmdlets and their parameters.

Let’s start by taking a look at the default output of the Test-Connection cmdlet. By default, Test-Connection will ping a given computer four times. If I were checking for latency, four replies would probably be better than one, but because I just want to know if the computer is up or down, a single reply is suitable.

PS C:\> Test-Connection -ComputerName bing.com

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms)
------        -----------     -----------      -----------                              -----    --------
TOMMYMAYNA... bing.com        204.79.197.200                                            32       14
TOMMYMAYNA... bing.com        204.79.197.200                                            32       13
TOMMYMAYNA... bing.com        204.79.197.200                                            32       14
TOMMYMAYNA... bing.com        204.79.197.200                                            32       13

This cmdlet includes a -Count parameter, and so I could have used that, as -Count 1, so that my computer only sent a single packet. But remember, I want be fast and efficient — I don’t want type more to see less. I want to type the same and make things faster. To accomplish this, what I’ll do is change the default behavior of this command to only send one packet. These means I’ll need to make use of the $PSDefaultParameterValues variable.

The $PSDefaultParameterValues variable stores any modifications to the default values of any of our cmdlet’s parameters. Since I don’t have any modified default values, the variable has nothing to return.

PS C:\> $PSDefaultParameterValues
PS C:\>

In the example below, we’ll use the Add() method to add a key/value pair to the variable. The keys are anything in the Name property, and the values are anything in the Value property. There is a one-to-one ratio between keys and values. These are also referred to as hash tables, associative arrays, or a dictionary object.

After we’ve added our key/value pair, we’ll try Test-Connection again, as we did in the first example.

PS C:\> $PSDefaultParameterValues.Add('Test-Connection:Count','1')
PS C:\> $PSDefaultParameterValues

Name                           Value
----                           -----
Test-Connection:Count          1

PS C:\> Test-Connection -ComputerName bing.com

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms)
------        -----------     -----------      -----------                              -----    --------
TOMMYMAYNA... bing.com        204.79.197.200                                            32       13

PS C:\>

As we can see, Test-Connection will now only ping a computer one time by default. Now, what happens when I do want to test for latency? I can use the -Count parameter and supply whatever number I want, so long as it’s 1 through 2147483647. Using zero or less, or 2147483648 will not work. I know, I tried.

You can read more information on the about_Parameters_Default_Values page here: https://technet.microsoft.com/en-us/library/hh847819.aspx.

Make Ping Accept Multiple Computers

Windows PowerShell, and an old-school command line tool favorite, collided today when I brainlessly typed the following:

PS C:\> ping computer1,computer2
Bad parameter computer2

This didn’t work, and as quickly as I realized my mistake, I thought, I’m going to have to fix it so ping can accept multiple computers.

There’s a couple ways I can do this, but my idea was to create an advanced function to do the work. I did that, but before I share the function, we should discuss what we could have done. We could have simply made an alias named ping that would run the Test-Connection cmdlet, a cmdlet that can handle a comma-separated list of computer names. Here’s an example of creating the alias and then using it to ping multiple computers.

PS C:\> New-Alias -Name ping -Value Test-Connection
PS C:\> ping computer1,computer2

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms)
------        -----------     -----------      -----------                              -----    --------
TOMMYMS PC... computer1       10.10.10.30                                              32       1
TOMMYMS PC... computer2       10.10.10.31                                              32       1
TOMMYMS PC... computer1       10.10.10.30                                              32       1
TOMMYMS PC... computer2       10.10.10.31                                              32       1
TOMMYMS PC... computer1       10.10.10.30                                              32       1
TOMMYMS PC... computer2       10.10.10.31                                              32       1
TOMMYMS PC... computer1       10.10.10.30                                              32       2
TOMMYMS PC... computer2       10.10.10.31                                              32       2

The reason this works is because of command precedence. If an alias and a command line tool share the same name, the alias will always run when the name is entered.

Well, this wasn’t quite want I wanted. I thought instead, I would make a wrapper around Test-Connection, called Test-TMConnection that would include a switch parameter (-Ping) that would return the standard ping results. I’ll dump in the function below and then we can walk though what it does.

Function Test-TMConnection {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [string[]]$ComputerName,

        [switch]$Ping
    )

    Begin {
    } # End Begin

    Process {
        If ($Ping) {
            ForEach ($Computer in $ComputerName) {
                ping $Computer
            }
        } Else {
            Test-Connection -ComputerName $ComputerName
        }
    } # End Process
} # End Function

Here’s how this works: The function is called by entering Test-TMConnection, the -ComputerName parameter, and then either one computer name, or a comma-separated list of computers. If the -Ping parameter is not used, it will run the standard Test-Connection cmdlet against the computer(s), like we saw in the alias example above. If -Ping is included, it will loop though the computers, using each one with ping. Here’s an example that includes the -Ping parameter.

PS C:\> Test-TMConnection -ComputerName computer1,computer2 -Ping

Pinging computer1.mydomain.com [10.10.10.30] with 32 bytes of data:
Reply from 10.10.10.30: bytes=32 time=2ms TTL=121
Reply from 10.10.10.30: bytes=32 time=1ms TTL=121
Reply from 10.10.10.30: bytes=32 time=1ms TTL=121
Reply from 10.10.10.30: bytes=32 time=1ms TTL=121

Ping statistics for 10.10.10.30:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 2ms, Average = 1ms

Pinging computer2.mydomain.com [10.10.10.31] with 32 bytes of data:
Reply from 10.10.10.31: bytes=32 time=1ms TTL=121
Reply from 10.10.10.31: bytes=32 time=2ms TTL=121
Reply from 10.10.10.31: bytes=32 time=1ms TTL=121
Reply from 10.10.10.31: bytes=32 time=1ms TTL=121

Ping statistics for 10.10.10.31:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 2ms, Average = 1ms

Important: Keep in mind, that if you were to use something like this, that you’ll be giving up other parameters that are included with ping and Test-Connection. This includes -t with ping, and -Count, and -Source with Test-Connection. In the end, the ping alias might be the better option.