After a conversation with someone at IBM last week about PowerShell, I picked it up again and have been having a play. I’ve been trying to write my own cmdlets, which has been an interesting experience – so I thought I’d jot down a few quick notes about what it’s been like.
First… a quick recap. Windows PowerShell is a command-line shell for system administrators. It has a number of neat features, but perhaps the most obvious is it’s object oriented approach – letting you pass objects (rather than strings) between commands in a pipe. I’ve done some work on writing PowerShell scripts before, but this time I approached it more as a developer – looking at how to extend the shell with new commands.
The .NET nature of PowerShell means you can run any .NET API at the Shell. In fact, this is what I did when I first played with PowerShell using the WebSphere MQ .NET API. It’s a quick way to get started without having to learn much about PowerShell – and I played around writing some simple scripts that were a translation of C# programs into the PowerShell syntax.
But it wasn’t really using PowerShell in the PowerShell way. What I have tried doing over the last couple of evenings is to extend PowerShell to include new commands that support WebSphere MQ administration work. I’ve been writing Cmdlets.
(If you’re not too interested in the detail of the whys or the hows of creating cmdlets but you are interested in WebSphere MQ, you might want to skip to the end of this post to see examples of what I’ve done in action…)
What are ‘Cmdlets’?
The PowerShell term for a command is “Cmdlet”. There is a good discussion about the distinction between Cmdlets and APIs on the PowerShell blog – essentially looking at if you already have a .NET API (as we do with WebSphere MQ), why bother creating Cmdlets as well?
The question boils down to “Why not let sys-admins use the API?”. And the best answer is because “sys-admins aren’t programmers”. If you have a very nice, clear API, maybe you can get away with giving them the API. But the focus of Cmdlets seem to be more about providing this function in an abstract, task-oriented, user-friendly way.
Besides this difference in focus and “mindset”, some other neat characteristics of Cmdlets worth mentioning…
All commands follow a VERB-NOUN pattern – a verb to describe the action to perform, and a noun to identify what to perform the action on. For example, Get-Process
returns a list of processes running on the system. Putting the verb first is apparently supposed to reinforce the task-oriented mindset of cmdlets – it’s about what the user wants to do, rather than focusing on the product.
When writing a new Cmdlet, you choose a verb from the predefined list of Windows PowerShell verbs. I really like this constrained approach. It makes it much easier to start using an unfamiliar interface if you can make reasonable guesses at what the commands will be before you even start. (And with the tab-completion for all registered commands, guessing near to what a command is normally enough).
If that is not enough help, then there are standard ways to get more information about commands – from consistent approaches to get documentation for a command (you can use Get-Help
– similar to the UNIX ‘man’ command) to ways to find a command if you can’t guess it’s name (with Get-Command
).
The user gets to choose how to handle errors when running commands – when developing a Cmdlet, you distinguish between non-terminating and terminating errors – giving sys-admins the ability to choose the behaviour in the event of a non-terminating error (e.g. continue, continue silently, halt, prompt) in a consistent way for all Cmdlets.
Cmdlets (should!) support wildcards – even if the API they are wrapping does not. This is something that I think will make a big difference between a set of WebSphere MQ cmdlets, and the command-line tools we currently have. And the PowerShell engine does a lot to support the Cmdlet developer to make this possible.
The way you can pass objects through pipelines between commands is very powerful. For example, in PowerShell Stop-Process
lets you stop a running process – similar to UNIX’s kill -9
, and Get-Process
lets you get the current processes – similar to UNIX’s ps
. But putting them together is so much easier than in UNIX:
Get-Process notepad | Stop-Process
This stops all of your notepad processes. Doing this in a UNIX script means piping ps
output into sed
or something like that, to get the PIDs out for passing to kill
.
Cmdlets can write output to more than just stdout, with the ability to generate output in various forms such as webpages, spreadsheets or RSS feeds.
Writing my first Cmdlets
1 – Identify what cmdlets I need.
I made a list of tasks WebSphere MQ sys-admins will want to be able to do – then matched these tasks up with a verb from the list of common Cmdlet verbs that was the closest match for each. (I was expecting this to be difficult – wondering how I’d handle it when I needed to do something that wasn’t covered by one of the verbs there – but that hasn’t happened. Everything that I’ve wanted to do so far can be described with one from the PowerShell set.)
2 – Check what they need to be able to do
The Cmdlet Development Guidelines was a useful document – containing guidelines on what Cmdlets should do, and how they should work. For example, -Debug
should cause the cmdlet to write out debug information, and -Verbose
should cause it to produce more detailed output. It goes back to what I was saying above about consistency making life easier for sys-admins starting with an unfamiliar product. So I tried to design functions which conformed to the norms of a PowerShell command.
3 – Download the Visual Studio PowerShell templates
I’m quite familiar with Visual Studio 2005, as I use it for my Windows Mobile development work – so the Windows PowerShell Visual Studio 2005 Templates pack was a big help. With this installed, I can create PowerShell projects in Visual Studio which makes the writing and compiling of Cmdlets much easier.
4 – Creating my first Get-WMQ… Cmdlet
The “How to Create a Windows PowerShell Cmdlet” walkthrough on MSDN is a good start – it goes through the steps required to write the Get-Process cmdlet, and included enough samples to get me going with my first cmdlet, Get-WMQQueueManager.
5 – Write the Cmdlet
I’m writing them in C#, using the WebSphere MQ .NET API. You basically need to create subclasses of Cmdlet or PSCmdlet, overriding methods which PowerShell will invoke to execute the command (typically, ProcessRecord
).
6 – Build the Cmdlet
Visual Studio does most of the work for me, and the remaining steps are well described in a blog post on how to use the Visual Studio templates, but essentially I compile my C# cmdlet classes into a DLL. This DLL can then be registered with PowerShell, which adds all of my shiny new WebSphere MQ commands to the Shell.
My work is nowhere near finished. There are several verbs I want to support (nearly two dozen), and nine main types of WMQ object – so it will take me a while before I cover all the combinations.
But even after a couple of evenings of coding, (and this is all output from working code… nothing faked here – honest! 🙂) I can do the following:
get me a list of local queue managers, reverse sorted by name, and show me their name, qmid and description
Get-WMQQueueManager * | Select Name, QueueManagerIdentifier, QueueManagerDescription | Sort-Object Name -descending Name QueueManagerIdentifier QueueManagerDescription ---- ---------------------- ----------------------- Test Test_2007-09-03_09.33.44 to check name collision TeSt TeSt_2007-09-03_09.33.47 to check name collision TEST TEST_2007-09-03_09.34.02 Test queue manager - to be deleted test test_2007-09-02_22.14.03 post post_2007-09-03_09.34.19 dale dale_2007-09-03_09.01.37 personal qmgr - for client development work
show me the name and QMID of all local queue managers which have names ending in ‘st’
Get-WMQQueueManager *st | select Name, QueueManagerIdentifier Name QueueManagerIdentifier ---- ---------------------- post post_2007-09-03_09.34.19 test test_2007-09-02_22.14.03 Test Test_2007-09-03_09.33.44
find which queue managers have a queue called “FINAL.Q”
Get-WMQQueue FINAL.Q * | Select Name, @{e={$_.QueueManager.Name};n='Queue Manager'} Name Queue Manager ---- ------------- FINAL.Q post ...
get a list of all queues which contain the word “CLUSTER” in their name, from queue managers with names ending in “st”
Get-WMQQueue *CLUSTER* *st | Select Name, @{e={$_.QueueManager.Name};n='Qmgr'} Name Qmgr ---- ---- SYSTEM.CLUSTER.COMMAND.QUEUE post SYSTEM.CLUSTER.REPOSITORY.QUEUE post SYSTEM.CLUSTER.TRANSMIT.QUEUE post SYSTEM.CLUSTER.COMMAND.QUEUE test SYSTEM.CLUSTER.REPOSITORY.QUEUE test SYSTEM.CLUSTER.TRANSMIT.QUEUE test SYSTEM.CLUSTER.COMMAND.QUEUE Test SYSTEM.CLUSTER.REPOSITORY.QUEUE Test SYSTEM.CLUSTER.TRANSMIT.QUEUE Test
show me all local queues on all local queue managers where the current queue depth is less than 10 messages away from it’s max-depth setting
Get-WMQQueue * * | Where {$_.QueueType -eq [IBM.WMQ.MQC]::MQQT_LOCAL -and $_.CurrentDepth -gt ($_.MaximumDepth - 10)} | Select Name, CurrentDepth, MaximumDepth Name CurrentDepth MaximumDepth ---- ------------ ------------ MYQ ... 7 15
get all transmission queues (except the cluster transmit queue) from all queue managers and show their depths and open counts
Get-WMQQueue * * | Where {$_.Usage -eq [IBM.WMQ.MQC]::MQUS_TRANSMISSION -and $_.Name -ne "SYSTEM.CLUSTER.TRANSMIT.QUEUE"} | Select Name, CurrentDepth, OpenInputCount, OpenOutputCount, @{e={$_.QueueManager.Name};n='Queue Manager'} Name CurrentDepth OpenInputCount OpenOutputCount Queue Manager ---- ------------ -------------- --------------- ------------- TRANS1 0 0 0 dale ...
get me all channels from all local queue managers which have an SSL Cipher Spec applied, and show me their name, sslciph, conname and the name of the queue manager they are on – sorted by channel name
Get-WMQChannel * * | Where {$_.SSLCipherSpec -ne ''} | Select Name, @{e={$_.QueueManager.Name};n='Queue Manager'}, SSLCipherSpec, ConnectionName | Sort Name Name Queue Manager SSLCipherSpec ConnectionName ---- ------------- ------------- -------------- SECURE post ... NULL_MD5 dlane.hursley.ibm.com(9090) SECURE.R test ... TRIPLE_DES_SHA_US dlane.hursley.ibm.com(9091) SECURE.X dale ... TLS_RSA_WITH_AES_256_CBC_SHA dlane.hursley.ibm.com(9094)
get me all non-system (i.e. channels with names that don’t start with SYSTEM) sender channels from local queue managers with names ending in “st”, and show me their name, conname, transmit queue, sslciph, and the name of the queue manager they are on
Get-WMQChannel * *st | Where {$_.Name -match "^(?!SYSTEM).*" -and $_.ChannelType -eq [IBM.WMQ.MQC]::MQCHT_SENDER} | Select Name, ConnectionName, TransmissionQueueName, SSLCipherSpec, @{e={$_.QueueManager.Name};n='Hosting Queue Manager'} Name ConnectionName TransmissionQueueName SSLCipherSpec Hosting Queue Manager ---- -------------- --------------------- ------------- --------------------- SECURE dlane.hursley.ibm.com(9090) TRANS1 NULL_MD5 post ... SECURE.R dlane.hursley.ibm.com(9091) TRANSR TRIPLE_DES_SHA_US test ...
generate an HTML webpage with a table showing the name, description and depth information for all queues on the ‘test’ queue manager, and open this HTML file in a web-browser
Get-WMQQueue * test | ConvertTo-Html -property Name,Description,CurrentDepth,MaximumDepth -title "Queues on my test queue manager" > myqueues.htm Invoke-Item myqueues.htm
generate a CSV spreadsheet containing the name, description and depth information for all queues on the ‘test’ queue manager, and open this in Excel
Get-WMQQueue * test | Select Name, Description, CurrentDepth, MaximumDepth | Export-Csv -path myqueues.csv Invoke-Item myqueues.csv
Hello..I have been reading a ton of your stuff lately it seems and came across this webSphereMQ stuff. I, FOR THE LIFE OF ME, cannot figure out how show my remote queues sing powershell. I have used everything I have read and cannot get it to show anything when running the commands. Am I missing something?
PS C:\> $qmconns = @()
PS C:\> $qmconns += New-WMQQmgrConnDef -Name DALEQM -Hostname dlane.hursley.ibm.com -Channel SVRCN -Port 1414
PS C:\> $qmconns += New-WMQQmgrConnDef -Name CENTQM -Hostname sysserv.boulder.ibm.com -Channel SVRCN -Port 1418
PS C:\>
PS C:\> $qmgrs = Get-WMQQueueManager –Connections $qmconns
Is something supposed to show up after this last command? I thought not..so I typed the next entry in the guide and nothing. When I log on locally and run the local commands..I have no issues at all.
Any idea what may be going on here?
John
I wouldn’t expect you to have any luck getting a connection to dlane.hursley.ibm.com working – that’s my server! (And I don’t have a queue manager running at the moment, anyway)
Is sysserv.boulder.ibm.com a server of yours? It seems unlikely… if not, then you need to be trying to connect to your own servers, not IBM’s.
Kind regards
D