We just finished a very successful virtual edition of the PowerShell+DevOps Global Summit. We lost our 2020 event to the pandemic but fortunately, the people at The DevOps Collective were able to pull together a fantastic virtual event. There were as many virtual attendees as we normally have at the in person event. But of course, we are all looking forward to next year when we can gather again in person. Next year's event is April 25-28, 2022 in Bellevue, WA.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
There is also an Automation + DevOps Summit in Nashville this November. So there are things to look forward to. Given that, I thought it would be handy to have a PowerShell tool to help me count down the days. And maybe teach a few things along the way. Let's have some fun.
Timespans
Creating a timespan between two dates is not that difficult.
New-TimeSpan -Start (Get-Date) -End ([datetime]"1/1/2022")
The result is a timespan object. Run the code to see for yourself. By the way, you can also simply subtract dates.
([datetime]"1/1/2022") - (Get-Date)
Finding out how long until the next PowerShell Summit isn't that hard.
Creating a Tool
This is a good start, but it would be nice to make this easier to use. So I built a small PowerShell function I call Get-EventCountdown. Naturally, it needs a datetime value.
Param(
[Parameter(Position=0,Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,HelpMessage = "Enter the event date and time.")]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if ($_ -ge (Get-Date)) {
$True
}
else {
Write-Warning "The specified events date has already occurred."
Throw "You must specify a future datetime."
$false
}
})]
[alias("Date")]
[datetime]$EventDate,
I want to be able to take in pipeline input. My plan is to keep a CSV file of upcoming events that I can import and pipe to the function. Notice also the custom [ValidateScript()] attribute. This is something I covered in my presentation at this years's PowerShell Summit.
The scriptblock has to result in a True or False value. If the $EventDate value is greater or equal to now, in other words it has already occurred, then PowerShell will write a warning message and throw a custom exception message. I could also have done this validation in the function if I wanted something a bit less "noisy".
Creating the timespan is simple enough.
$ts = New-Timespan -Start (Get-Date) -End $EventDate
Now, I could have returned this value and call it good. But why miss an opportunity to write a rich object to the pipeline? My function also takes a parameter for the event name and builds a custom object.
[PSCustomObject]@{
PSTypename = "PSCountdown"
EventName = $EventName.Trim()
EventDate = $EventDate
Countdown = $ts
}
The function parameters have aliases to make the command easier to use at the console. They will also work in case the incoming object has a property called "Date" and not "EventDate". This gives me flexibility.
The CSV file headings are "Date" and "Name".
Leverage Objects
I could have stopped here, but if you know me, you know that just isn't' going to happen. Because I'm writing a rich object to the pipeline, I want to take advantage of it. In order to do that, you need to make sure your output is a typed object with a unique name and not a generic pscustomobject. That's why my hashtable has a TypeName entry. This becomes my type name.
This particular object doesn't have a lot of properties and I don't mind displaying all of them. In other situations, I may be writing a very rich object to the pipeline. That's where custom type and format extensions come into the picture. I'm going to do that here as well.
I used my New-PSFormatXML command from the PSScriptTools module to create a custom format file displaying all of the properties in a table by default. In the script file, I add this to load the file.
Update-Formatdata $PSScriptRoot\pscountdown.format.ps1xml
Custom Formatting
Now for the fun part. The Timespan objects include a millisecond component. PowerShell automatically converts Timeppan objects to strings to display them on the screen. But I don't need to see the milliseconds. In the custom format file, I can create a scriptblock to format the Countdown property.
$cd = $_.Countdown.ToString("dd\.hh\:mm\:ss")
The scriptblock can display this value. In fact, the screen shots above are using this format. But why stop there? Wouldn't something like this be useful?
If an event is imminent, display it in red. If it is getting close, I'll display it in yellow. My format file uses scriptblocks and ANSI escape sequences, assuming you are running in the console host. The coloring doesn't run in VSCode or the PowerShell ISE.
Initially, the format file had hard-coded logic. I was comparing the countdown.totaldays value to 7 and 14. But you might have different ideas of what is imminent and what is pending. Maybe you want to use values of 30 and 45. In the function file, I added 2 variables.
#events almost here
$PSCountdownCritical = 7
#events getting closer
$PSCountdownPending = 14
When you dot source the script file, these variables are set in your PowerShell session. You can modify the values in the file or after the fact. My format file has code like this to use these variables.
If ($host.name -match "console") {
if ($_.countdown.totaldays -le $PSCountdownCritical) {
"$([char]27)[91m$($_.EventDate)$([char]27)[0m"
}
elseif ($_.countdown.totaldays -lt $PSCountdownPending) {
"$([char]27)[93m$($_.EventDate)$([char]27)[0m"
}
else {
$_.EventDate
}
}
else {
$_.EventDate
}
I didn't want to force people to have to edit the XML file.
You could extend this idea to the ANSI sequences.
In any event, I now have rich PowerShell tool that provides useful information in a meaningful way.
Want to Try?
Want to try this for yourself? The necessary files can be found at https://gist.github.com/jdhitsolutions/17bc013f3ae43c88fd7b14c0f840ad4c. Put both files in the same folder and dot source the ps1 file. You can enter values via parameters or from the pipeline.
I'm counting down the days to next year's PowerShell Summit and now you can to. Have a great weekend.