PowerShell

Colorful Information in PowerShell 5.0

color-aspect-hero

In a previous article I introduced you to the new information stream in PowerShell 5.0. I think this is something that you can incorporate into your own scripts and functions. As I mentioned last time, one of the drawbacks to using PowerShell’s Write-Information cmdlet is that if you want to see messages, then it’s difficult to distinguish between output and messages. Fortunately, Microsoft’s PowerShell team planned ahead and tweaked Write-Host in PowerShell 5.0.

In the past the PowerShell community has frowned upon the use of Write-Host, especially as the means to display results. But it has always been fine for informational messages, especially if you take advantage of the ForegroundColor or BackgroundColor parameters. If you take advantage of those parameters, then you can easily tell the difference between actual output and messages. In PowerShell 5.0, Write-Host is a wrapper for Write-Information. This means you can use Write-Host and get all the benefits of the new information stream.

To demonstrate, here’s a variation on the test function that I used previously.

Function Test-Me2 {
[cmdletbinding()]
Param()

Write-Verbose "InformationPreference = $InformationPreference"

Write-Host "Starting $($MyInvocation.MyCommand) " -ForegroundColor Green
Write-Host "PSVersion = $($PSVersionTable.PSVersion)" -ForegroundColor Green
Write-Host "OS = $((Get-CimInstance Win32_operatingsystem).Caption)" -ForegroundColor Green

Write-Verbose "Getting top 5 processes"
Get-process | sort WS -Descending | select -first 5 -outvariable s

Write-Host ($s[0] | out-string) -ForegroundColor Green

Write-Host "Ending $($MyInvocation.MyCommand) " -ForegroundColor Green

}

Let’s run the test function.

Sponsored Content

Maximize Value from Microsoft Defender

In this ebook, you’ll learn why Red Canary’s platform and expertise bring you the highest possible value from your Microsoft Defender for Endpoint investment, deployment, or migration.

Using Write-Host as an information wrapper (Image Credit: Jeff Hicks)
Using Write-Host as an information wrapper (Image Credit: Jeff Hicks)

You’ll notice that I specified an information variable. The information preference doesn’t have an effect on Write-Host. But I have information.

Information data (Image Credit: Jeff Hicks)
Information data (Image Credit: Jeff Hicks)

Everything worked as planned, but I probably need to make additional decisions as to what information to display. By the way, you can also use Write-Information in the same command as Write-Host. Messages for the former will only be displayed if you use an InformationPreference of continue while messages for the latter will always be displayed.

Or so I thought until I came up with the approach.

Function Test-Me3 {
[cmdletbinding()]
Param()

write-Verbose "InformationPreference = $InformationPreference"

#create a variable that will be easier to work with
if ($InformationPreference -eq "Continue") { $Info = $True }

if ($Info) {
  Write-Host "Starting $($MyInvocation.MyCommand) " -ForegroundColor Green
  Write-Host "PSVersion = $($PSVersionTable.PSVersion)" -ForegroundColor Green
  Write-Host "OS = $((Get-CimInstance Win32_operatingsystem).Caption)" -ForegroundColor Green
}

Write-Verbose "Getting top 5 processes"
Get-process | sort WS -Descending | select -first 5 -outvariable s

If ($Info) {
  Write-Host ($s[0] | out-string) -ForegroundColor Green
  Write-Host "Ending $($MyInvocation.MyCommand) " -ForegroundColor Green
}

}

Sometimes I might want to occasionally see messages from Write-Host, so I’ll check the value of $InformationPreference. If it’s set to continue, then I know to display the messages with Write-Host. To simplify my code, I’ll create a Boolean variable, $Info, at the beginning. Throughout the function, if this value is True, then the Write-Host lines will execute.

Testing with no information (Image Credit: Jeff Hicks)
Testing with no information (Image Credit: Jeff Hicks)

As expected, no information is generated.

Writing information in color (Image Credit: Jeff Hicks)
Writing information in color (Image Credit: Jeff Hicks)

Writing information in color (Image Credit: Jeff Hicks)

Now my Write-Host messages are displayed in color, and I have data in the information variable.

Write-Host information (Image Credit: Jeff Hicks)
Write-Host information (Image Credit: Jeff Hicks)

The PSHost tag is automatically added, and you can see that the source is Write-Host. You can manually add additional tags if you desire.

Adding additional tags (Image Credit: Jeff Hicks)
Adding additional tags (Image Credit: Jeff Hicks)

But I’m probably not going to do that and neither are you. Instead, here’s a proof of concept that lets you add additional tags to each piece of information.

Function Test-Me4 {
[cmdletbinding()]
Param()

Write-Verbose "InformationPreference = $InformationPreference"
write-verbose "Information variable = $($PSBoundParameters["InformationVariable"] | out-string)"
#create a variable that will be easier to work with
if ($InformationPreference -eq "Continue") { $Info = $True }

if ($Info) {
  Write-Host "Starting $($MyInvocation.MyCommand) " -ForegroundColor Green
(get-variable $PSBoundParameters["InformationVariable"]).value[-1].Tags.Add("Process")
Write-Host "PSVersion = $($PSVersionTable.PSVersion)" -ForegroundColor Green
#insert a new tag to the last information entry
(get-variable $PSBoundParameters["InformationVariable"]).value[-1].Tags.Add("Meta")

Write-Host "OS = $((Get-CimInstance Win32_operatingsystem).Caption)" -ForegroundColor Green
(get-variable $PSBoundParameters["InformationVariable"]).value[-1].Tags.Add("Meta")
}

Write-Verbose "Getting top 5 processes"
Get-process | sort WS -Descending | select -first 5 -outvariable s

If ($Info) {
  Write-Host ($s[0] | out-string) -ForegroundColor Green
  (get-variable $PSBoundParameters["InformationVariable"]).value[-1].Tags.Add("Data")
  Write-Host "Ending $($MyInvocation.MyCommand) " -ForegroundColor Green
  (get-variable $PSBoundParameters["InformationVariable"]).value[-1].Tags.Add("Process")
}

}

I can look at the bound parameters and discover the name of the information variable being used. Then, every time I want to insert a new tag, I can update the variable value. In this last iteration of the function, I’m adding the same tags I used in the very first version of the function. Now I have the best of everything. I can display information messages in color and get property tagged data.

Testing additional tags (Image Credit: Jeff Hicks)
Testing additional tags (Image Credit: Jeff Hicks)

As you can see, the information object has my additional tags.

Displaying the additional tags (Image Credit: Jeff Hicks)
Displaying the additional tags (Image Credit: Jeff Hicks)

I would probably create a small helper function to streamline the process of adding additional tags. Please note that this is only necessary when using Write-Host. If you use Write-Information, you can specify all the tags you need.

I’ve probably given you a lot to think about to take some time to experiment and test. When you get ready to take advantage of this feature, I hope you’ll let me know how you are using this new stream and your experiences.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply

External Sharing and Guest User Access in Microsoft 365 and Teams

This eBook will dive into policy considerations you need to make when creating and managing guest user access to your Teams network, as well as the different layers of guest access and the common challenges that accompany a more complicated Microsoft 365 infrastructure.

You will learn:

  • Who should be allowed to be invited as a guest?
  • What type of guests should be able to access files in SharePoint and OneDrive?
  • How should guests be offboarded?
  • How should you determine who has access to sensitive information in your environment?

Sponsored by: