Neat trick: Organizing and formatting messy PowerShell output

One of the things that I have always loved about Windows PowerShell is that it puts a wealth of diagnostic and health data at your fingertips. You can use PowerShell to find out almost anything that you want to know about the Windows operating system. Even so, there is one aspect to PowerShell that seems to bother a lot of people. When using PowerShell to create a report, it can be difficult to arrange the output into well-organized columns.

At first, this might sound a little bit strange. After all, Get cmdlets display their output in columns by default. Suppose, for instance, that I wanted to see a list of the virtual machines running on my Windows 10 desktop, and for each virtual machine, I wanted to see the virtual machine’s name and the amount of memory that it is currently using. I could easily get that information by using this command:

Get-VM | Select-Object Name, MemoryAssigned

As you can see in the figure below, the command’s output is organized into nice, neat columns.

As you look at the figure, you will notice that the memory assigned to the various VMs is zero. That’s because all of the virtual machines are powered off. I don’t want to turn this article into a crash course in Hyper-V, but what if you wanted to add a column to find out the maximum amount of memory that the virtual machines could consume if they were all suddenly powered on? You could actually get that value by adding MemoryMaximum to the list of objects that you are selecting. But for the sake of argument, let’s pretend for a moment that we had to resort to using another cmdlet (Get-VMMemory) to get this information and that you wanted to display the virtual machine name, the memory that is currently assigned, and the total amount of memory that could be assigned in a single chart. Here is what the code might look like:

$VMs = Get-VM
ForEach ($VM in $VMs){
$Mem = Get-VMMemory $VM
$MaxMem = $Mem.Maximum
$Current = $VM.MemoryAssigned
$Name = $VM.Name
Write-Host $Name, ' ', $Current, ' ', $MaxMem

The first line of code retrieves a list of the virtual machines and adds them to a variable named $VMs. The second line then sets up a loop that progresses through the list of virtual machines.

The next four lines create a series of variables. $Mem gets the virtual machine’s memory consumption information from the Get-VMMemory cmdlet. $MaxMem holds the maximum amount of memory that the VM might use, $Current holds the amount of memory that the virtual machine is consuming right now, and $Name contains the virtual machine’s name.

The last line of code is the one that I really want to focus on. This line displays the requested values on screen. It does this by using the Write-Host cmdlet to display the variable’s contents, as well as some blank space between each variable.

The script does exactly what it is supposed to do. The problem is that the script’s output leaves a lot to be desired. If you look at the figure below, you can see that the output is not organized into columns. Instead, the number of characters in each field has an effect on the way that the output is displayed.

So how can you fix this problem? Well, PowerShell clearly knows how to arrange its output into columns, as illustrated by the first screen capture. The difference between the two screen captures is that the Get-VM cmdlet that I used as the basis for the first screen capture outputs an object – not raw text. I used the Select-Object cmdlet to tell PowerShell which of the object’s properties I wanted to display. In contrast, the script that acted as the basis for the second screen capture is outputting raw text. Because raw text is being used, PowerShell does not know how to format it and display it in columns.

The trick to getting the output to display in a series of neatly organized columns is to convert the raw text into an object. Thankfully, this is easier than it sounds.

PowerShell contains a cmdlet that is specifically designed to create new objects. It’s called New-Object. You only have to supply the New-Object cmdlet with two parameters. The first of these parameters is the type name. The type name tells PowerShell what type of object you are creating. For the purposes of this article, I will use PSCustomObject as the type name. This indicates that we are building a custom object.

The second parameter that you have to use with the New-Object cmdlet is a list of properties that should be included with the object. For that, we will use the variables from the previously demonstrated script as properties. However, in the interest of making the code a bit easier to follow, I am going to incorporate these variables into an array called $MyProperties. So let’s look at the modified code:

$A = New-Object -TypeName PSCustomObject
$VMs = Get-VM
$MyObject = ForEach ($VM in $VMs){
$Mem = Get-VMMemory $VM
$MaxMem = $Mem.Maximum
$Current = $VM.MemoryAssigned
$Name = $VM.Name
$ObjectProperties = @{
Name = $Name
Current = $Current
Max = $MaxMem
New-Object PSCustomObject -Property $ObjectProperties

As you can see, I have associated a variable called $MyObject with the ForEach loop. Within the loop, I have also defined the ObjectProperties variable and mapped it to the VM name, maximum, and current memory amounts. The loop ends with the New-Object cmdlet. The last line of the revised script outputs the object contents. You can see the output below.

Fixing PowerShell output formatting problems
Tidying up PowerShell output: More coding, but worth it

As you can see, it takes a bit more coding to format a PowerShell script’s output into something that’s nice and clean. The nice thing about this approach, however, is that it makes it easier to convert the output into a table that can serve as the basis for an HTML report.

Featured image: Shutterstock

Leave a Comment

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top