PowerShell Module Test-TCPConnection

I have this love versus disappointment relationship with PowerShell. It can provide a lot of great stuff for us in automation, but sometimes the thing that looks likes the best ever since sliced bread can be a bit of a disappointment. Take the cmdlet “Test-NetConnection” for example. It’s absolutely wonderful in what it does. It assists you in doing a network diagnosis with just a single command. Much more than you could ever get out of just a simple icmp ping. However, the latter is just the problem with this PowerShell cmdlet. You cannot run it without it using a ping. The real downside to that is when the port is not available, blocked by a firewall or something else is blocking it, you have to wait for it to get a time out. And that takes time, a lot of time. If it was just for one host, I could live with it, but if you need to figure out if 200 servers are reachable, you are going to be in the office for a while. Hence the creation of my ever first cmdlet!

Based on my disappointment for the “Test-NetConnection” cmdlet and my desire to learn more on PowerShell I created my first cmdlet that does exactly what “Test-NetConnection” does with respect to a port query but without the icmp ping involved. I have dubbed it “Test-TCPConnection”, because, well that it what it does. Being the nerd that I am, I have included a full help in the module itself but I will list on my site as well. Use “help Test-TCPConnection –Online” to connect to the online help page.

To make the “Test-TCPConnection” module work, extract the folder and place it in your PowerShell Modules folder. Use the following command to see your individual module path’s.

$env:PSModulePath -Split ";"

After reloading your PowerShell environment, the module will be loaded automatically. You can check the commands the module exposes with:

Get-Command -Module TestTCPConnection

testtcpconnection001

Getting the syntax is easily accomplished with:

Get-Command Test-TCPConnection -Syntax

testtcpconnection002

Any feedback is always appreciated. Use the comment option down below or send me a message using the contact page.

Download version 1.0.0.0 from the PowerShell Gallery:
https://www.powershellgallery.com/packages/TestTCPConnection

Replacing DiskPart with PowerShell

In my previous life, I was a deployment person. MDT, WDS, WinPE & bare-metal installation were all part of my life. For managing disks, physical or virtual I always used “diskpart.exe” to create the disk layout, create bootable partitions and everything surrounding the magic of disks and partitions. Since I am trying to do as much as possible now with PowerShell I thought it would be fun to see if “diskpart.exe” could be replaced with pure PowerShell CMDLET’s? To give you a heads-up, it can for 99%, almost there! Just one feature I could not find is setting GPT attributes. According to this article, a Recovery Partition should have the attribute of “GPT_ATTRIBUTE_PLATFORM_REQUIRED” & “GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER” resulting in a value of “0x8000000000000001”. Using “diskpart.exe” to query for information from a default installation of Windows 10 results in the correct attributes.

PSDiskpart001

I did expect that setting a partition to the guid value for a recovery partition with PowerShell (“de94bba4-06d1-4d40-a16a-bfd50179d6ac”) ,would also take care of both the attributes. It partially does that, just the “GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER” is set, so the drive is hidden for the OS. The other one is not set and according to my research, it is simply not available in PowerShell. Therefore, you will still need “diskpart.exe”.

During my experimentations, I have concluded that PowerShell’s “Disk”, “Partition” and “Volume” cmdlets are tricky to work with. It takes time to understand how to handle them, but it eventually works. In my opinion “diskpart.exe” is still more powerful when it comes down to pure handling disks, however PowerShell has far better support for handling the dynamics surrounding scripting and error handling. Still it is not so difficult to combine them both, as you will see in my example.

Here is my code or download the script below. Please note that I have put a “return” at the top of the script so you do not destroy your disk the first time you run the script.

# Define the disk
$Disk = Get-Disk | where Number -EQ "0"
$DiskNumber = $Disk.DiskNumber

# Clean the disk and convert to GPT
if ($disk.PartitionStyle -eq "RAW")
{

Initialize-Disk -Number $Disk.DiskNumber -PartitionStyle GPT

} Else {

Clear-Disk -Number $DiskNumber -RemoveData -RemoveOEM -Confirm:$false
 Initialize-Disk -Number $DiskNumber -PartitionStyle GPT

}

#Create the System Partition Partition
$SystemPartition = New-Partition -DiskNumber $DiskNumber -Size 512MB
$SystemPartition | Format-Volume -FileSystem FAT32 -NewFileSystemLabel "system"
$systemPartition | Set-Partition -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
$SystemPartition | Set-Partition -NewDriveLetter "S"

#Create Microsoft Reserved Partition
New-Partition -DiskNumber $DiskNumber -Size 128MB -GptType "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"

#Create Primary Partition
$PrimaryPartition = New-Partition -DiskNumber $DiskNumber -UseMaximumSize
$PrimaryPartition | Format-Volume -FileSystem NTFS
$PrimaryPartition | Set-Partition -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
$PrimaryPartition | Set-Partition -NewDriveLetter "W"

#Shrink Primary Partition by 500MB for the Recovery Partition
$newSize = ($PrimaryPartition.Size - 524288000)
$PrimaryPartition | Resize-Partition -Size $newSize

#Create the Recovery Partition
$RecoveryPartition = New-Partition -DiskNumber $DiskNumber -UseMaximumSize
$RecoveryPartition | Format-Volume -FileSystem NTFS
$RecoveryPartition | Set-Partition -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"
$RecoveryPartition | Set-Partition -NewDriveLetter "R"

# Add "Required" attribute to the GPT Recovery partition. (No .Net Function available)
$partitionNumber = $RecoveryPartition.PartitionNumber
$DiskPartCMD=@(
"select disk $DiskNumber"
"select partition $partitionNumber"
'gpt attributes=0x8000000000000001'
'exit'
)

$DiskpartCMD | diskpart.exe

If anyone reading has a PowerShell solution to setting the attributes, please let me know.

Downloads


Create-EFIPartitions

Reference


PARTITION_INFORMATION_GPT structure
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365449(v=vs.85).aspx

CREATE_PARTITION_PARAMETERS structure
https://technet.microsoft.com/en-us/library/aa381635.aspx

New-Partition
https://docs.microsoft.com/en-us/powershell/module/storage/new-partition?view=win10-ps

Customizing the recovery partition after upgrading the OS from Windows 8.1 to Windows 10
https://blogs.technet.microsoft.com/askcore/2016/04/26/customizing-the-recovery-partition-after-upgrading-the-os-from-windows-8-1-to-windows-10/

 

 

Using Credential Manager in PowerShell

Using PowerShell remoting can be a fantastic experience, but the number of  times I had to enter credentials to make a new pssession is getting out of hand, or to put it better a painful hand. Wouldn’t it be great if you could store the credentials somewhere safe and retrieve it when necessary? Fortunately, you can! Since a long time Windows has had the option to store credentials in a local secured database and use it when needed. This is known as the “Credential Manager” and can be located in the control panel.

credmgr001

Within this tool you can store credentials for both Web Sites and network resources such as remote servers. Wouldn’t it be really cool if you could store credentials there once and retrieve them using PowerShell? Luckily, you can! Someone actually created a PowerShell Module called “CredentialManger” that does just what this post is about.

First, we need to install the module. It’s located in the PowerShell Gallery so you need to trust that Gallery (Use Set-PSRepository) and have NuGet installed. If you didn’t do this just yet, you’ll get additional messages, accept them to continue.

Install-Module -Name "CredentialManager"

To see what the module exposes use:

Get-Command -Module "CredentialManager"

credmgr002

Let’s start with creating new credentials. The CMDLET “New-StoredCredential” seems to do just that. I’ve constructed the following code to create a new set of credentials in the credential manager. The first part prompts for input and stores it in the $Secure variable. Next I create the new object and store it as persistant accross sessions by specifying the “-Persist” parameter. As it’s also a generic type of credentials I explicitly specify that also.

$Target = "YourServerName"
$UserName = "Administrator"
$Secure = Read-host -AsSecureString
New-StoredCredential -Target $Target -UserName $UserName -SecurePassword $Secure -Persist LocalMachine -Type Generic

The output will result in this:

credmgr005

Using the help system I figured out that “Get-StoredCredential” retrieves the objects stored in credential manager. To select the credentials we just created the “-Target” property needs to be specified. In this case we will be referring to the target server we just created, “servername”. Use this command to get all properties into a credentialized object:

Get-StoredCredential -Target "servername" –AsCredentialObject

Retreiving a single pair of credentials is easy, right!? Now what I am after is credentials that are stored for the use in PowerShell remoting. In my specific case to connect to a remote server I’m managing.

Eventually, I created this command-line:

Enter-PSSession -ComputerName "servername" -Authentication Credssp -Credential (Get-StoredCredential -Target "servername")

As you can see the “Get-StoredCredential” needs a target parameter to retrieve the credentials. That outputs a username and password (as System.Security.SecureString) that is then passed to the “Enter-PSSession” cmdlet.

credmgr004

My servername in this case is “WHV” and the credentials are stored as “HyperVManager/WHV”.

Removing credentials using the same module is very straightforward. The “Remove-StoredCredentials” does the trick. Again pointing it to the credentials using the “-Target” parameter.

Remove-StoredCredential -Target "servername"

I hope that this post helps you to use credentials in a safe and easy manner.

ByValue & ByPropertyName

Recently I had the pleasure of attending a PowerShell course. Although it was not exactly what I expected I still picked up a few things here and there. It was good to see that people attending the course got a bit more enthusiastic on PowerShell during the course and they acquired a solid base to start using PowerShell.

On of the items that was discussed was how to use the pipeline. Casting the output of a cmdlet to another. Something obvious as “get-service -name “bits”| start-service” was used during a demo. That’s great for starters, however it gets a little more confusing when cmdlets with a different noun are used. As an example, lets try these two cmdlets to play nice with each other.

Resolve-DnsName <something> | Test-NetConnection

Let first try these two cmdlets individually to see how they actually work. Remember you can always use the help system for more information.

help Resolve-DnsName -Full

Without knowing anything about the cmdlet itself, I’m guessing that it will need a hostname, fqdn or ip address. Just trying a hostname with the name “server” resolves into this:

ByValueProprtyName 01

So apparently I was correct. But how does this actually work? What does this cmdlet actually expect as input? In PowerShell this is really easy to figure out. The next command gets the parameter that’s required for this cmdlet.

get-help Resolve-DnsName -Parameter * | where {($_.Position -eq "0") -and ($_.Required -like "true")}

ByValueProprtyName 02

Here we see that a single input object is required, which is called “Name”. We didn’t specify the Name parameter but it still worked because according to the help system anything on position 0 (The first parameter) is automatically assigned to the first parameter. Let’s try this on “Test-NetConnection”.

ByValueProprtyName 03

Okay… weird that produces absolutely nothing. Not to worry, the cmdlet construction isn’t faulty, “Test-NetConnection” just doesn’t require any input. Running the cmdlet by itself checks connectivity to the Internet.

ByValueProprtyName 04

Since we want to try testing an internal server it would make sense that a certain parameter would accept input. Using “get-help” we would need to figure that out.

get-help Test-NetConnection -Parameter *

ByValueProprtyName 05

Because this cmdlet doesn’t require input, the parameter position “0” is not available. This is now transferred to position “1”. Looking at the help file a parameter with the name “Computername” would do the trick. Let’s try it first without specifying the actual parameter name itself.

ByValueProprtyName 06

Eureka! This works. So combining the two cmdlets would work. Let’s try that.

ByValueProprtyName 07

Hmm no go, bummer. “Test-NetConnection” apparently has no idea what we want to accomplish. Let’s figure out what the result of “Resolve-DnsName” actually is? We do this with

Resolve-DnsName server | gm

GM stands for Get-Members, or “give me all stuff from an object”.

ByValueProprtyName 08

What’s important here is the Typename, “Microsoft.DnsClient.Commands.DnsRecord_A”. This “Type” is the object type that is passed over the pipeline. This object type needs to be something that the receiving end understands. This is the PowerShell technique that is called “ByValue”. So the value type of the object that’s being passed needs to be of the same type.  PowerShell under the hood does it’s magic by trying to match the output of the first cmdlet to the input of the second one IF the object type is the same. So let’s see what “Test-NetConnection” expect.

ByValueProprtyName 09

Using the “Get-help” function again we take a closer look at the “Computername” parameter. The input is specified directly after the parameter name “[<string>]”. So it expects the “ByValue” object name property that is passed over the pipeline to be of the string type. Let’s try the simplistic version first. Just pass a string as input.

“server” | Test-NetConnection

ByValueProprtyName 10

So, now we know that this actually works. Let’s convert the output of the “Resolve-Dnsname” and see what happens.

(Resolve-DnsName server).name.ToString() | Test-NetConnection

ByValueProprtyName 11

That seems to work as well! Please note that you could technically skip the “.ToString” part because the property “name” is already a string, regardless of the original type of the entire object. This wouldn’t work for other properties that have a different type. Use “GM” or the “.GetType()” method again to see the actual type.

ByValueProprtyName 12

Please note that by using “ByValue” you can only pass a single property over the pipeline!

ByPropertyName

One of the other options that we could also use is the “ByPropertyName” option. As we can see from the help text this option is available for the “Computername” parameter of “Test-NetConnection”.

What this simply means is that the property name of the output of the first cmdlet must match the input parameter of the second cmdlet. In our example the property “Name” of the “Resolve-DnsName” output must match the input parameter “Computername” of the “Test-NetConnection” cmdlet. It doesn’t by now but we can make it so by creating an expression like this:

Resolve-DnsName server | select @{Name="Computername";Expression={$_.name}} | Test-NetConnection

The select statement is where the magic happens. This “Name=” part tells PowerShell that a new property with the name “Computername” should be created. The “Expression” fills that newly created property with the value of the “Name” property of the first cmdlet. The best part here is that the Object type here doesn’t matter, as long as the type of the property matches. In this case it still needs to be a string. However the object type can remain a different type. In this case a “Selected.Microsoft.DnsClient.Commands.DnsRecord_A” object type.

ByValueProprtyName 13

Putting this all together we end up with the following command line:

Resolve-DnsName server | select @{Name="Computername";Expression={$_.name}} | Test-NetConnection

ByValueProprtyName 14

And there we have what we wanted to accomplish!!

As you can see there are multiple ways to go about constructing your entire command line and casting properties over the pipeline. Specially with the “ByValue” and “ByPropertyName” options. I hope that this post added in understanding the differences.

References


Learn About Using PowerShell Value Binding by Property Name
https://blogs.technet.microsoft.com/heyscriptingguy/2013/03/25/learn-about-using-powershell-value-binding-by-property-name/

Two Ways To Accept Pipeline Input In PowerShell
https://blogs.technet.microsoft.com/askpfeplat/2016/11/21/two-ways-to-accept-pipeline-input-in-powershell/