Maximizing the PowerShell Console Title Bar

A few days ago Boe Prox posted some very nifty PowerShell modules for using the title bar as a ticker for RSS feeds like the weather. I thought this was an awesome idea and an easy way to take advantage of what would otherwise be unused screen space. I was especially intrigued with his use of timer objects and event subscriptions to manage the updating.

Naturally I decided to run with this. My main goal was to take Boe’s fundamental idea and turn it into something more re-usable or extensible. My result is a module called ConsoleTitle.


PS C:\> get-command -Module ConsoleTitle | Select Name

Name
----
Get-Inspiration
Get-SystemStat
Get-Timer
New-Timer
Remove-Timer
Set-ConsoleTitle
Set-TimerInterval
Start-TitleTimer

The overall premise is pretty simple, define a global variable $PSConsoleTitle and use a timer to periodically update the console title bar with this value. During the refresh interval you can run whatever code you like, however you like, to provide a new value to the variable. In the module I’ve included two sample commands, Get-SystemStat and Get-Inspiration. The former uses WMI to gather system information from the local computer.

The other command defines an array of slogans, sayings and suggestions and randomly selects one to use as the title bar text.

The module includes a few commands for working with timer objects. You can use New-Timer in your own scripts. Here’s the function.


Function New-Timer {

<# .Synopsis Create an event timer object .Description Create an event timer object, primarily to be used by the ConsoleTitle module. Each timer job will automatically be added to the global variable, $ConsoleTitleEvents unless you use the -NoAdd parameter. This variable is used by Remove-Timer to clear console title related timers. This function is called from within other module functions but you can use it to create non-module timers. .Parameter Identifier A source identifier for your timer .Parameter Refresh The timer interval in Seconds. The default is 300 (5 minutes). Minimum value is 5 seconds. .Parameter Action The scriptblock to execute when the timer runs down. .Parameter NoAdd Don't add the timer object to the $ConsoleTitleEvents global variable. #>

Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a source identifier for your timer")]
[ValidateNotNullorEmpty()]
[string]$Identifier,
[Parameter(Position=1)]
[validatescript({$_ -ge 5})]
[int]$Refresh=300,
[Parameter(Position=2,Mandatory=$True,HelpMessage="Enter an action scriptblock")]
[scriptblock]$Action,
[switch]$NoAdd
)

Write-Verbose ("Creating a timer called {0} to refresh every {1} seconds." -f $Identifier,$Refresh)

#create a timer object
$timer = new-object timers.timer
#timer interval is in milliseconds
$timer.Interval = $Refresh*1000
$timer.Enabled=$True

#create the event subscription and add to the global variable
$evt=Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier $Identifier -Action $Action

if (-Not $NoAdd) {
#add the event to a global variable to track all events
$global:ConsoleTitleEvents+=$evt
}
#start the timer
$timer.Start()

} #Function

And here’s how you might use it.


Function Get-Inspiration {

Param(
[Parameter(Position=0)]
[ValidateScript({$_ -ge 5})]
[int]$Refresh=600
)

#Define an array of pithy sayings, slogans and quotes

#we'll create as a globally scoped variable so you can add to it anytime you want from PowerShell
$global:slogans=@(
"PowerShell Rocks!",
"Energize!!",
"To Shell and Back",
"I am the Shell",
"PowerShell to the People",
"Powered by PS",
"PowerShell Rulez!",
"PowerShell Fanboy",
"I am the walrus",
"Those who forget to script are doomed to repeat their work.",
"Have you backed up files lately?",
"Is your resume up to date?",
"Is it Beer O'Clock yet?",
"With great power comes great responsibility",
"I came, I saw, I scripted.",
"$env:username, Open the pod bay doors."
)

$sb={ $global:PSConsoleTitle=$global:slogans | get-random }
#invoke the scriptblock
Invoke-Command $sb

New-Timer -identifier "SloganUpdate" -action $sb -refresh $refresh

#start the update timer if not already running
if (-Not (Get-EventSubscriber -SourceIdentifier "TitleTimer" -ea "SilentlyContinue")) {
Start-TitleTimer -refresh $refresh
}

} #function

Think of the module as a framework or SDK for building your own solutions. The module also includes an about topic. I hope you’ll download ConsoleTitleand let me know what you think.

Managing VirtualBox with PowerShell

In my line of work I simply can’t afford not to use virtualization, and I use just about all the major tools from time to time. But most of the time I rely on the free VirtualBox program from Oracle. One of the reasons I like it is it’s relatively low footprint. It is not as full featured as a product like VMware and I don’t want to get into the merits of different virtualization products. Instead I want to show you how I’m managing my virtual machines. VirtualBox does not have any PowerShell cmdlets, but there is a COM object so I wrote a few PowerShell functions around it and bundled everything into a module. Continue reading

Windows Update Module

I don’t have a large environment to manage, but I do have a number of test boxes I try to keep up date using Windows Software Update Server (WSUS). Occasionally I’ve needed to manage things client-side. Unfortunately, there aren’t a lot of good tools, and nothing PowerShell-related that I’ve found so I wrote my own. At the end of this post you’ll be able to download a zip file with a self-extracting exe file to install my Windows Update module. Once installed, open an elevated PowerShell session and import the JH-WindowsUpdate module.

The module consists of the following commands:

  • Get-WindowsUpdateLog
  • Backup-WindowsUpdateLog
  • Clear-WindowsUpdateLog
  • Get-WindowsUpdate
  • Install-WindowsUpdate

The first challenge I tackled was finding an easier way to work with the client side event log. This text file, WindowsUpdate.log can get quite unwieldy very quickly. What I wanted was an object-based solution so I could find information. Get-WindowsUpdateLog takes the current log and turns each line into an object. Any lines that are just comment characters are stripped out.  You end up with an object like this.

[cc lang=”PowerShell”]

Component    : Report
Message      : Reporter successfully uploaded 2 events.
PID          : 1100
Computername : SERENITY
TID          : 17c0
Category     : Normal
Datetime     : 8/3/2010 7:47:26 AM

[/cc]

The function can also query remote computers and has a parameter to run as a job, which I typically do because processing the log file can take some time. I typically use it like this:

[cc lang=”PowerShell”]

PS C:\> $wu=Get-WindowsUpdatelog
PS C:\> $wu | select DateTime,Component,Category,Message

[/cc]

When the file gets too big or whenever you feel like it, you can clear the log using Clear-WindowsUpdateLog. This will require restarting the WUAUSERV service. You can manage remote computers as well via WinRM. But you might want to first make a backup copy with Backup-WindowsUpdateLog. Be sure to look at command help and examples for everything.

The next part of the puzzle are the updates themselves. Fortunately there is a COM object that can be used to find and download Windows Updates.

[cc lang=”Powershell”]

#create session objects
$updateSession = New-Object -ComObject “Microsoft.Update.Session”
$updateSearcher = $updateSession.CreateUpdateSearcher()

[/cc]

The Get-WindowsUpdate command will find all recommended updates needed on the local computer. Or you can use -All.

get-windowsupdate screen shotThe command doesn’t directly support remote computers, but you should be able to run it in a remote session. Installing updates uses almost the same approach with Install-WindowsUpdate. The default is to install Critical updates only. But you can install by severity, title or all.

Install-WindowsUpdate screenshotIf the update requires a reboot, the computer will not automatically reboot unless you specify -Reboot.

Unfortunately, installing updates must be done locally in an interactive session. There is a well known bug with this COM object that nobody has yet to find a way to beat. The best thing you can do for remote computers is to use SCHTASKS.EXE and remotely create a scheduled task to run the function on the remote computer. Of course you need to copy the module to the remote computer or at least load the function.

Please find a way to test all of this in a non-production environment. You can download the zip file with the self-extracting exe file here. Please post any followups, bugs or comments to this post.

Finally, and I’m trying something new here, but if you feel this module (or anything I’ve ever published for that matter)  is of value to you or your organization, I hope you’ll consider adding something to my Paypal tip jar. Any contribution would be appreciated and allows me to continue developing solutions like this along with all my other PowerShell endeavors. Thank you.

UPDATE: I’ve already made an update. I’ve tweaked a few of the functions to fix a few bugs and hopefully improve performance. The original zip file with the self-extracting archive has been update. However, for people with mapped home drives the default install path will be a problem. You can always manually specify where to install per user, but anyone just clicking through will run into problems.  Therefore, I’ve also created a simple zip file which you can download here. Manually extract the contents to a folder in your PSModulePath which you can check by looking at $env:PSModulePath.