an introduction to windows powershell

Download An Introduction to Windows PowerShell

If you can't read please download the document

Upload: dale-lane

Post on 16-Apr-2017

40.796 views

Category:

Technology


4 download

TRANSCRIPT

Windows
PowerShell

An introduction to...

Dale Lane

[email protected] Hursley Park

Two quick disclaimers before we get started.

One - it's worth pointing out that I don't represent Microsoft, or PowerShell. I'm in no way an authority on it and am speaking from the perspective of a user and a developer. It's entirely possible that some or all of this presentation may completely misrepresent PowerShell. I don't think it does, but it might. :-)Or in other words, any mistakes are entirely my own. Any brilliant flashes of insight have been stolen from others (such as some good demo's of PowerShell by the architect, Jeffrey Snover, which I found on channel9.msdn.com).

Two a lot of what I'm gonna say that I like about PowerShell has been done before. Without spoiling any surprises... bash does command tab-completion, AS400 does consistent command syntax and parameter parsing, and so on. It's interesting to note historical background, but as I've yet to present on PowerShell without someone pointing this out like it's a bad thing, let me preempt that by saying that I don't care. Why not borrow/steal from the best you can find and improve on it?

What is Windows PowerShell?

How does PowerShell work?

How can I 'hack' PowerShell so that it can be used with my product?

Agenda

In this presentation, I will give an introduction to what PowerShell is, and how it works.

I will then look at how you can extend PowerShell to provide support for administering third-party applications.

(The slide says 'hack' because this presentation was originally written for HackDay, so I figured 'hack' was more in-keeping with the spirit of the day than 'extend').

What is Windows PowerShell?

So let's start with the basics. What is PowerShell?

What is Windows PowerShell for?

Perhaps the best question for anything new is to ask what it is for? What problem has it been created to solve?

What is Windows PowerShell for?

ADMIN

Admin

It's essentially a tool for system administrators.

What is Windows PowerShell for?

ADMIN

Building GUIs on top of PowerShell

It's a platform that you can build rich graphical user interfaces on top of.

For example, the new management console for Exchange 2007 is a PowerShell GUI. (And there it is. Pretty, eh?)

What is Windows PowerShell for?

ADMIN

Interactive command shell

It's an interactive command shell.

What is Windows PowerShell for?

ADMIN

Scripting

It's a scripting language

What is Windows PowerShell for?

ADMIN

COM Scripting for WMI

It's also a COM scripting environment, for accessing WMI data.

What is Windows PowerShell for?

ADMIN

PowerShell is a single tool / language / environment to do what you might have needed to learn a few different tools or languages to do before.

This slide shows the progression if you consider PowerShell as an evolution from existing Windows technologies. Instead of writing custom MMC snap-ins, you can now create an MMC interface on top of a PowerShell admin layer. Instead of the old Windows 'Command Prompt', PowerShell is a new more powerful interactive shell. PowerShell scripts are a way to do more than you could in batch files. And PowerShell scripts can do the WMI work we used to do with VBScript or JScript.

How doesPowerShell work?

That's enough scene-setting for now... let's take a look at PowerShell itself.

And here we go. This is PowerShell.

And here is my PowerShell window after I've run my first command to get a list of processes running on my system.

The command is (imaginatively titled):

Get-Process

Get-Process

Get-ProcessIt's worth highlighting that all of the PowerShell commands (called cmdlets but we'll come back to that in a minute) look like this.

Get-Process

a verb

a noun

Verb, followed by noun

What do you want to do?

followed by

What do you want to do that to?

http://msdn2.microsoft.com/en-us/library/ms714428.aspx

The verbs

There is a standardized set of verbs (and this is just a part of the full list).

For example, to get a list of running processes, I used GET. Not read, list, display, find, etc. It's GET. It's always GET. Whatever you're doing, whatever system or application you're administering, in PowerShell, the verb to get elements in a resource is always GET.

The verbs

Consistent

Learnable

Readable

This is nice it makes for a more consistent experience. If you move from product to product, you don't need to learn different commands.

Once you learn the core command set which I found I picked up surprisingly quickly the learning curve for new commands is significantly reduced. It means you can normally guess what the command will be called.

The command names are quite verbose which makes for very readable scripts. As someone who used to work with other people's perl scripts, I know what a nightmare it can be deciphering the idea behind a script!

For example... a simple command to get a subset of running processes:

Get-Process | Where { $_.Handles -gt 500 } | Sort Handles | Format-Table

Ignoring some of the syntax quirks for the moment (which we will come back to), this is a very readable command.

You can work out what this is doing at a glance.

HELP!

Not convinced?

Okay, so perhaps that command was a little scary for a first example.

Let's take a step back and look at how we can find our way around the PowerShell commands.

HELP!

Get-CommandGet-HelpGet-PSDriveGet-Members

There are four basic 'help' commands in PowerShell.

The first one

Get-Command

What command do I need?

Get-Command helps you find which command you need.

So I know that I want to get a list of processes... I figure that the command will probably have process somewhere in the name, so 'Get-Command' for something-process gets me a couple of options.

The fact that I know what the verbs all mean meant that I could have probably guessed that it would have started with Get-.

(To be honest, the fact that commands all support tab-completion means that I could have worked out this command without Get-Command, but you get the idea)

Next you know what command you need. But how do you find out how to use it?

Get-Help

How does it work?

Get-Help followed by the name of any command gives you man-page style help for it.

You can see here that I get a nice synopsis, syntax guide and description for the command.

And notice that it's a not an epic tome that scrolls page after page. It's about getting you going quickly, or jogging your memory.

Need more?

How does it work?

Here is some more help for the same command and this one does scroll for a bit.. this is just the first page.

How does it work?

You get this by adding -detailed

to get more detailed documentation

How does it work?

This includes a more detailed description about each of the parameters what they are for, what type they are, and so on.

How does it work?

It also gives you a bunch of examples.

If you're new to any command in PowerShell, just type 'Get-Help', the name of the command, and '-detailed' and you can get a bunch of examples of how to use it.

Still not enough?

There is more if you need it.

This is the 'full' documentation.

And it goes on for page after page.

You don't often need this much as a user, but it's great if you're a developer using the commands as an API, because it gives you the API documentation.

For each parameter, it'll tell you if it is required, whether it's positional, the default values, etc.

As well as command documentation, 'Get-Help' can also give more general help.

This is called 'topic help', and there is topic help about most of the concepts in PowerShell.

Again, this help includes samples and examples.

For example, if you need to write a regular expression and can't remember your wildcard syntax, about_wildcard is a quick way to get help on what you need.

While I'm mentioning wildcards, as a quick aside, it's worth pointing out that the wildcard support in PowerShell is fantastic.

You can put wildcards anywhere. In commands, in parameters, anywhere.

In this command, I'm getting a listing of the contents of the 'desktop' directories for all users.

Get-Process | Where { $_.Handles -gt 500 } | Sort Handles | Format-Table

Consistent

The consistency of the command set is more than just the fact that the names of the commands are consistent. Their behaviour is similar, too.

If you want to sort the output from a command, you pipe it to 'Sort'. Whereas the UNIX equivalent to Get-Process, 'ps' has sort options, Get-Process doesn't. Other UNIX commands have their own implementations of sort, with their own approach to parameters.

But in PowerShell, sort is always sort. It always works the same way, and always needs the same arguments (that is, which property to sort by, and which direction to sort in).

This consistency is a common theme in PowerShell.

The shell does the command argument parsing giving the arguments to the individual commands as tokens. So you don't get some commands starting arguments with a hypen, others starting arguments with a slash, others with nothing and so on. PowerShell has it's standard way of handling arguments.

I mentioned before that PowerShell commands are called cmdlets. They are small commands. They don't do much. That's the idea Get-Process doesn't need to know how to do sorting it just gets a list of a processes.

This consistency is better for the user. (And makes life easier when you're developing new commands, as the Shell does most of your work for you!)

Okay, so I've veered off-topic a bit. Where were we?

Help we've got 'Get-Command' to figure out which command we want, and 'Get-Help' to figure out how it works.

Next 'Get-PSDrive'

What data stores are available?

This gets you a list of data sources that PowerShell knows about.

The point is, PowerShell is not just for files and filesystems. You can use it for tons of stuff. And again, consistency is the key it works the same way whatever data store you are in.

Let's take a look at the registry on my system.

Windows Registry

I love that I can interact with the registry at the command-line now, without needing to resort to regedit.

'cd' to change keys, and 'dir' to get the contents. It feels like being in a filesystem.

(and by the way, tab-completion is particularly helpful here!)

And you can do all of the sort of recursive searching and listing that you'd expect. Navigating is all very easy.

What about certificates?

Certificates

Same sort of thing.

You use 'cd' and 'dir' to navigate and list the contents of the certificate store on your system.

Here are the environment variables for my system

Environment variables

Same sort of behaviour

You 'cd' to the env store, and can view, search and edit the variables.

PowerShell also has it's own variables if you want to tweak it's behaviour.

'cd' into the variables store to see them

Variables

For example, want to see the preferences you can change?

'dir' ?

It's worth acknowledging that dir doesn't make much sense in these contexts.

But then, dir doesn't really exist. Remember what I said about PowerShell commands all being verb-noun ?

So how does 'dir' fit into all this?

Aliases

A quick check with 'Get-Command' shows what 'dir' is. It's an alias for 'Get-ChildItem' a command that gets the 'child' items of a resource. Which makes more sense for getting a list of certificates in a certificate store, for example.

There are several built-in aliases some, like 'dir', are there to make PowerShell a bit familiar for those of us who are a bit set in our ways.

Others are there to save us a bit of time.

I mentioned before that PowerShell's verbose command-set is great for making instantly readable scripts. And this is true. Verbose is great when you've got to look after someone else's scripts. Or scripts that you wrote months ago and have completely forgotten about.

But it sucks when you're just hacking about at the command line. So you don't have to type in the full name for arguments just enough to differentiate it.

You don't even need to type the full name of commands you can use an alias. Even with tab-completion to help, 'gps' is still quicker to type than 'Get-Process'.

But how would you know that it's 'gps'? Well, okay, so that is a bit fiddly. But, again, it is consistent, and therefore quite learnable.

'Get-Command *Alias' reminds me that the command I need to get a list of aliases is 'Get-Alias' (surprise!)

Look at all of the aliases that start with 'g'. They are all (pretty much) 'Get-' commands. 'Get-' commands are shortened to g-something-something.

Look at the aliases that end in *al. They are the alias commands. So the alias for Get-Alias is gal.

Stopping a process

kill -9 `ps -aef | grep 'notepad' | grep -v grep | awk '{print $2}'`

Now on to my favourite aspect of PowerShell. It's best explained with an example.

How would you stop a particular process in UNIX-land?

kill -9 followed by the process id. How do you get the process id? You can use 'ps'.

Easy!

Stopping a process

kill -9 `ps -aef | grep 'notepad' | grep -v grep | awk '{print $2}'`

Why so complicated?

Except... it really isn't.

I'm not even sure that this line would work - I never really liked awk, sed, and all of those.

The problem is that these pipes are piping text 'ps' identifies the process id, but some of that information is lost when it is printed out in a table. You then need to recreate that context by knowing which column the process ids will be in, and trying to grab that text out.

We lose too much information by relying on passing text between commands. Consider commands that return a date when piped to another command which need you to recreate the date from the text representation of it.

This is what makes this sort of thing so complicated. Converting stuff into text means we have to learn complex ways to get stuff out of text like awk.

Stopping a process

Get-Process notepad | Stop-Process

This is the PowerShell equivalent.

Perhaps a contrived example, but it makes the point well.

Stopping a process

Get-Process notepad | Stop-Process

Why so much easier?

Why is it so much easier?

Because PowerShell is an object-oriented shell. It doesn't pass strings between commands it passes the whole object. (.NET objects, as you might have guessed, as something produced by Microsoft).

So 'Get-Process' gets the .NET object representing the notepad process(es), and passes it to the 'Stop-Process' command.

So let's go back to the first command that I started with using Get-Process to get a list of the current running processes. People used to 'ps' might have assumed that Get-Process prints a list of processes. It does appear to.

But it doesn't!

Get-Process returns a number of process objects. It just so happens that, if you don't tell what PowerShell what to do with an object returned to it, it will helpfully print out some of it's properties in a way that it thinks is the most appropriate (in this case, a table).

But, you don't have to let it do that. You can tell PowerShell to send the output somewhere else, print them to the console in a different format, or... do something else with them.

How do you know what you can do with an object?

This brings us to the fourth of our 'HELP' commands: Get-Member.

If you pipe an object to 'Get-Member', it returns a list of properties that the object has, and methods it has that can be invoked.

You don't need to accept the default output chosen by PowerShell, you can choose which properties you want it to display.

Select

It's quite SQL-like in it's syntax.

You select the names of properties from the available properties in the 'Get-Member' output.

And again, to reiterate an earlier point, as with most things in PowerShell, wildcards are supported.

If you are looking for process properties relating to size you could...

1) use Get-Member to get a list of all properties with 'size' in the name, then use 'Select' to get them

or, more simply,

2) 'Select' all properties from the Process objects which have 'size' in the name.

(In hindsight, looking at this output, you'd probably also want the process name in the output, but the table it draws makes the point how you can get the output you want).

Remember what I said about all commands are verb-noun? So what about 'Select'? I keep contradicting myself but noone stops me :-)

Again, it's an alias. 'Select' is an alias for Select-Object.

It's one of a number of utility commands.

If you're used to object-oriented programming, you're familiar with the idea that everything can be described as an object.

The noun part of a verb-noun command describes the object that the command can be used on. So these commands can be used on any object.

For example, you can compare objects or group them by identified properties.

To get a full list of these utility commands, we can use 'Get-Command'

Select

Where

Sort

Compare

These are the utility commands I tend to use the most often.

Sort, Select, Where, Compare

As with Select, although in full these commands would be written with -Object, they are typically written using the shorter alias

So you can use 'Where' to apply a filter to the set of objects returned by a command.

Again, if you're used to SQL-type syntax, this sort of thing will look familiar.

Here I want a list of processes which have a virtual memory size greater than 200 megabytes.

(Notice the use of -gt for greater-than rather than the > character, which can be confused with redirects in a command line)

You can make this filter as complex as you like using -and and -or to set filters on multiple properties.

(Notice that this is put in curly brackets as it is something that needs to be executed essentially it needs to be something that will return true or false)

Piping the list of objects to Where-Object returns another (smaller) list of objects a subset of the set of objects given to it.

This subset can then be piped to Sort-Object.

Here I am identifying the property to sort by - virtual memory size (using the short alias to save me some typing), and specifying the direction to sort in.

Finally, I pass my sorted subset of the full list of process objects to Select-Object specifying the properties that I want to be displayed. (Note that these don't need to be properties that I've used to filter or sort as at every stage we have been passing the whole process object between the pipes, and no information has been lost).

Again, I'm using curly brackets to show something that I want to be executed. Rather than displaying virtual memory size in bytes, I think it would be more helpful to see it displayed in MB. So I've told Select-Object to return me the virtual memory size divided by 1024.

Another (albeit bizarre) example of wildcards in action.

Get me a list of all processes which start with an a, b, or c, and end with a letter c through q.

Format the list grouped by the 'Company' property

We've looked at the utility commands commands which can be used with any object. And it's worth learning these you learn them once and can use them with any command for any product that you need to administer with PowerShell.

In a similar way, it's worth getting familiar with the common parameters that all* cmdlets support.

* - well... ish.

To find out more about these, you can read the help topic about it

Get-Help about_commonparameters

will return all of the information about them, and how they work.

-Confirm

-Verbose

-WhatIf

-Debug

In short, there are four parameters that it's useful to learn.

These are best explained with an example.

Consider our Get-Process piped to Stop-Process example again.

-WhatIf

useful if you're not sure that you've got a command right before you run it

the -WhatIf parameter tells a cmdlet to not actually do anything, but to tell you what it would have done if you had run it

so for my Stop-Process -WhatIf example, you get a list of processes that would have been stopped

-Confirm

useful if you think you've probably got a command nearly right, but want to double-check something before it's done

the -Confirm parameter tells a cmdlet to ask for confirmation before doing anything

so for my Stop-Process -Confirm example, you get an Are you sure? prompt before each process is stopped

I said before that when you have a list of objects returned by a command, you can tell PowerShell to send them somewhere else.

Get-Process | Export-Csv

For example, you can send it to a CSV file (comma-separated-values)

This means that you can look at the objects in Microsoft Excel. (From where you can draw graphs or charts, or perform the sort of neat statistical analysis that I never really understand).

CSV files are also a nice way to store a collection of objects so that you can use them later.

Recall that I mentioned one of the utility commands earlier: Compare-Object

Compare-Object give this cmdlet two sets of things, and it will compare them (or just the properties that you specify), working out which are different

So you can do this

Compare two lists of processes

The first the list of process objects you get when you import the CSV file containing the process objects we exported earlier

The second using Get-Process to get a current list of processes

We can compare the system now with the processes that were running at an earlier known state.

And you can see that there are some differences

Snitter and Twitteroo (don't ask) are running now but weren't before

And a wiki app, calculator, notepad, SplashID and TaskToday were running earlier but aren't now

This is a very powerful capability for something that only needed a couple of short commands.

I mentioned that Compare-Object can compare specified properties of objects.

So what if we did the same as before, but getting Compare-Object to compare both the name and the workingset of the process objects.

This lets you see not only whether there are any processes which weren't running before, but if any of the processes have a different working set size to what they had before.

You could take this further by specifying how much of a change for example, looking for processes which have had a substantial change in the amount of memory since a previous known good state.

Using .NET

That covers PowerShell's built-in commands. But you're not limited to that.

As PowerShell is built on top of .NET, you can use any .NET library at the command line, too.

In short, pretty much anything that can be done in a .NET application can be done in a PowerShell script.

Let's take a look at the sorts of .NET objects you can use.

If I do a quick sum at the command line, you can see that I get back the result '5'.

But what type of object am I getting back?

An 'int'

If you've done any C# coding before, you will probably be familiar with the GetType method.

But you can also run the same PowerShell commands that we've gone through like using Get-Member to see what methods you can invoke on the object

Having the whole .NET library means that you have some very powerful string handling methods available for use in your scripts.

If I use Get-Member on my string object, you can see how many there are this is just the top of the list before it scrolled off the page.

Searching, editing, cutting out substrings, modifying... there is a lot that you can do.

And if you've been a .NET programmer, then you already know how to do it.

(If you've not been a .NET programmer, then PowerShell is a nice way to learn the .NET basics)

Another object type that is useful in sysadmin scripts is the DateTime object

There is actually a built-in PowerShell cmdlet to create a datetime object or get the current date: Get-Date

But here I want to ignore that for the moment to show how I can directly access .NET namespaces

The syntax is a little quirky you wrap the namespace in square brackets, then use a double-colon to access something. But you do get used to it.

And you can see here where I've piped this to 'Get-Member' that the range of date methods and properties is huge there is a lot that you can do with a datetime object.

Another neat example of using .NET function directly at the command line

([xml](new-object Net.WebClient).DownloadString($bbc_news_rss_url)).rss.channel.item| Select title, Desc*, *date -first 8

Here I am downloading the RSS feed for the BBC News website

I then pipe that XML object to Select-Object, and choose the title, description and date properties for display

Note:- square brackets to cast an object to a type where I cast my object to be an 'xml' object- dollar sign for variables - I assigned my $bbc_news_rss_url variable on the previous line to keep this line a little shorter- wildcards again saves me having to spell Description, or remember that the date field in XML is called pubDate- Select has additional parameters -first and -last which let you see, in this case, just the first eight objects.

Quickly... let's take the anything-in-.NET example to a ridiculous extreme

Here I want to use a library that is not part of the core .NET

That's fine you can load any .NET DLL into PowerShell with a single command. You can see here that I've loaded Windows Forms into PowerShell.

I can then create a Form object, set a few properties, and, when I enter showDialog() ...

... my Window appears.

This is just .NET programming done interactively.

Anything that you can do in a .NET program can be done in a PowerShell script. And any function available in a .NET DLL can be loaded in, and made available to PowerShell scripts.

How can I hack PowerShell?

This should give you a clue as to the answer to the last question I wanted to tackle how can you extend PowerShell so that it can be used to administer your product.

But first... I think it's worth addressing:

Why?

Why?

Why might you want to do this?

Let me explain with an example. I used to work on a product called WebSphere MQ. I have been working to add support for WMQ administration to PowerShell, so I'll quickly explain why I thought this would be useful

WebSphere MQ

Queue

Message

Queue Manager

It's not really important to understand what WMQ is for the purposes of this, but for those wanting a little background, WMQ let's you have a conceptual server called a Queue Manager which hosts a number of queues. You can then put and get messages to these queues.

So imagine a sys admin. He uses PowerShell for his work to administer a number of Microsoft server applications, and to do basic user and system admin stuff.

He's now been asked to be responsible for WebSphere MQ admin, which he's never done before.

This means probably having to learn how to use 'runmqsc' the command line tool that comes with WMQ.

Okay, so this isn't the end of the world, but it is a different command with it's own quirky syntax, it's own approaches to parameters, it's own nuances. It's fine when you get used to it, but it's not immediately intuitive.

You can see here that to get a list of the queues on my queue manager server, I've done DIS (for Display) QL(for a local queue) (*) (to get all queues you wrap the name in parentheses)

Fine when you know how, but it probably wouldn't have been our sys admin's first guess

Get-Process

a verb

a noun

Remember that our sys admin knows that in PowerShell, commands will be a verb followed by a noun.

In fact...

Get-ProdObject

a verb

product name

object type

http://msdn2.microsoft.com/en-us/library/ms714657.aspx

The convention for commands administering external products or systems is that the noun is broken up into the product name followed by the object type.

Knowing that the command will include the product name means our sys admin can use Get-Command to try searching for it...

Get-Command *WMQ* returns a list of commands.

Many of them will look familiar to our sys-admin

For example,

Get-WMQQueuewill probably return a list of WebSphere MQ queue objects

Some of the queues used by the sales team are getting a bit full. Can you increase the amount of space in them?

So our sys admin gets asked his first question...

Okay, so first, he'd probably need to get some clarification

Some of the queues used by the sales team are getting a bit full. Can you increase the amount of space in them?

SALES.*

which queues are used by the sales team?

brilliant they're all named SALES dot something

easy enough

Some of the queues used by the sales team are getting a bit full. Can you increase the amount of space in them?

depth > (max depth - 10)

what is a bit full?

they mean queues that are 10 or fewer messages away from their maximum depth

this could be expressed in a few ways (depth + 10 > max depth etc.) but for now, let's go with this way

Some of the queues used by the sales team are getting a bit full. Can you increase the amount of space in them?

double max depth

how much should the max depth be increased by?

easy enough just double whatever it is at the moment

(probably a bit extreme, but it will do for our example!)

This is what our sys-admin might try

Get-Command Get-*WMQ*Queue

To start off with... let's double-check we know what command we want. To be honest, tab-completion would have been enough to find this. To get a list of queues, we know we want a Get- command, and we know that it will have Queue as the object type

Get-WMQQueue

So we know that Get-WMQQueue will return us a list of queue objects

Now, we don't want to mess around with all queues just the SALES team's queues which are getting full.

But our sys admin doesn't need to learn any quirky parameters for this new WMQ command he can use the common utility commands that work on any object.

To start with...

Where {$_.Name -like SALES.* -and $_.CurrentDepth -gt ($_.MaximumDepth - 10)}

Where

Where the name is like SALES dot something

And the current depth is bigger than the max depth minus 10

Other than needing to know the property names, there is nothing new in here.

And our sys admin knows that he can use Get-Member to get a list of properties if he needs to check

All of this is something that a PowerShell admin could have done in response to the query we saw before, even without knowing anything about WMQ

Select Name, CurrentDepth, MaximumDepth

Just to check, we can run the command like that getting us a list of objects

I've added a Select Name, CurrentDepth, MaximumDepth to get us the interesting bits...

Sure enough, there are some queues which are getting a bit full

SALES.2 is five away from being fullSALES.3 is six messages away

and so on

Now let's do the same command again, but this time make the changes.

Just as the verb to get an object is always Get, the verb to modify an object is always Set.

ForEach-Object -process {Set-WMQQueue $_ -MaximumDepth ($_.MaximumDepth * 2)}

PS C:\> Get-WMQQueue * * | Set-WMQQueue -Description hacked!If we were going to adjust the maximum depth to the same value for each of the queues returned by the previous command, then I could have just piped it to Set-WMQQueue

For example, this command will set the description property of every queue to hacked!

Get-WMQQueue returns me a list of queue objectsSet-WMQQueue will modify the description property of every object it is given

ForEach-Object -process {Set-WMQQueue $_ -MaximumDepth ($_.MaximumDepth * 2)}

But in this case, to double the existing maximum depth property for each queue, our sys admin will need to be a little more clever

This can be done with another of the utility commands: ForEach

$_ represents the current object

So this line runs Set-WMQQueue on the current queue object given to it, setting the MaximumDepth property to the current queue object's MaximumDepth times 2

Tthere is nothing WMQ-specific here. And nothing that a PowerShell admin wouldn't have been able to do without needing to be an WMQ expert. This would be possible because they are mainly using the standard PowerShell commands (and the new WMQ ones have been named and work in a way that is consistent with the PowerShell norms).

And there we go. A quick Get-WMQQueue command to confirm that the depths have been adjusted as you'd expect.

Not only has this been much quicker than it would have been possible to do with the existing runmqsc command line tool, but perhaps just as importantly the learning curve has been massively reduced.

I mentioned that our sys-admin might not know the properties that a WMQ queue object has.

Get-Member

It's easy to check pipe the output of the Get-WMQQueue command to 'Get-Member' and you get the full list

You can see the type of every property

You can even see which ones you're allowed to modify.

For example - I cannot set the CurrentDepth this is a property determined by the number of messages on the queue, and isn't something our sysadmin can modify.

If he tries, PowerShell will return an error that it is not possible, but even here, it is providing doc and guidance in the standard way

The fact that properties still have their type they are not returned as text that needs to be parsed or interpreted means that you can use them in queries

Where {$_.CreationDateTime -ge $(Get-Date -month 10 -day 15 -year 2007)-and $_.CreationDateTime-le $(Get-Date -month 10 -day 20 -year 2007)}

For example, if I want a list of queues that were created between the 15th October and the 20th October...

Pipe the objects returned by Get-WMQQueue through Where, with a query that uses date objects to compare against

Again none of this is WMQ-specific, other than the name of the CreationDateTime property which you can get from Get-Member

Set the maximum depth for all cluster queues that start with a letter between 'D' and 'K'to 20.

Our sys-admin's next job

Set the maximum depth for all cluster queues that start with a letter between 'D' and 'K'to 20.

Get-WMQQueue * * | Where {$_.Name -like "[D-K]*" -and $_.ClusterName -ne ''} | Set-WMQQueue -MaximumDepth 20

Easy

Get all the queues, use a Where to filter it to the ones which start with the right letter, and have something in their cluster name property

Then pipe that set of objects to a Set-WMQQueue command

Remember that aliases are not limited to commands parameters can have aliases defined, too.

To make life easier for people who are used to the existing runmqsc command line tool, you can have property name aliases that look like the ones they might be used to. So instead of MaximumDepth, an experienced sysadmin could use:

Set the maximum depth for all cluster queues that start with a letter between 'D' and 'K'to 20.

Get-WMQQueue * * | Where {$_.Name -like "[D-K]*" -and $_.ClusterName -ne ''} | Set-WMQQueue -MAXDEPTH 20

MAXDEPTH

Doesn't matter will do the same thing

Get a list of non-system sender channels, showing the name, connection name, transmission queue, SSL Cipher Spec, and the name of the queue manager it is on.

One last quick example

The point of this one is to highlight that objects in .NET typically have properties which are objects themselves

Here we will be getting a channel. What a channel is is not important, but what is important is that one of the properties of a channel object is a handle to a queue manager object. We want the name property of that queue manager

Get a list of non-system sender channels, showing the name, connection name, transmission queue, SSL Cipher Spec, and the name of the queue manager it is on.

Get-WMQChannel * * | Where {$_.ChannelType -eq [IBM.WMQ.MQC]::MQCHT_SENDER -and $_.Name -match "^(?!SYSTEM).*"} | Select Name, Conn*Name, Trans*Name, SSLCiph*, @{e={$_.QueueManager.Name};n='Host'}

Name ConnectionName TransmissionQueueName SSLCipherSpec Host ---- -------------- --------------------- ------------- ---- SECURE dlane.hursley.ibm.com(9090) TRANS1 NULL_MD5 post SECURE.R dlane.hursley.ibm.com(9091) TRANSR TRIPLE_DES_SHA_US test

The syntax is a bit icky, but it's still essentially the same idea

It all goes in curly brackets because it needs executing

You use $_ to get the current channel object$_.QueueManager to get the queue manager handle$_.QueueManager.Name to get it's name

Export-CSV

ConvertTo-HTML

Finally, remember that our new WMQ sys admin can output queries to CSV spreadsheets or HTML tables.

Remember how we used Export-CSV to export the current process information to a spreadsheet? This let us do comparisons with a known state seeing what processes have changed and so on.

The sys admin could use the same techniques that he is used to with WMQ. Export the queue objects to CSV then schedule regular Import/Compare commands to see if any of the queue properties are significantly different from the known good state.

Again, not only is this sort of monitoring ability easy to do, but it's consistent and with a much reduced learning curve.

How?

That's the sales pitch out of the way. This is why I think adding support for PowerShell admin to a product has value you get access to a powerful shell and scripting environment, and for users familiar with PowerShell, learning your product becomes much easier.

Assuming that you are convinced that you should do this... how do you do it?

I'm going to stick with WebSphere MQ as an example.

WMQ has a .NET library. And remember anything that you can get at in a .NET DLL can be loaded into PowerShell.

So all of the stuff in manuals and books like these can be used.

What can I do with PowerShell?

Ad-hoc scripts

Production scripts

I said before that PowerShell is a broad environment covering anything from formal polished GUIs to an interactive shell where you can hack about and try things out.

Adding PowerShell function runs the range from writing ad-hoc scripts to formal production function, and pretty much anything in-between

What do I mean by that?

What can I do with PowerShell?

Ad-hoc scripts

Production scripts

arguments don't need to be namedarguments don't need to be typedscripts don't need to be signedetc.You can write informal scripts

These are the things that a sys admin will throw together for his own use, to save time by not repeatedly entering common commands.

They aren't formal. You don't have to bother naming arguments, or specifying their type.

What can I do with PowerShell?

Ad-hoc scripts

Production scripts

arguments can be typedrich error handlingprotection against uninitialised variablesmultiple output streamsscripts digitally signedetc.At the other end of the spectrum, you can write production scripts with typed, named arguments. Function with rich error handling, support for multiple output streams (for example, sending errors to one destination, debug output to another, standard output to another etc.), and so on.

What can I do with PowerShell?

try stuff out in an interactive shell

tie a few things together in utilities

build commands up into a script

generalize (parameterize, etc.)

clean up and productize

share!

This is often an evolutionary process.

You start by running things at the shell, and getting into habits of commands you frequently run which you can string together in a function.

Over time, these might be built up into a script.

Later, as you find more uses for them, you might generalize them parameterizing them and so on

Eventually, you could clean these up and productize them to the point where they can be shared with customers or third-parties.

Ad-hoc

function Get-WMQQueue ($qmgr){ # display details of any WMQ errors encountered in this function Trap [IBM.WMQ.MQException] { Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode) continue }

# if we have a connection to a queue manager... if ($qmgr -ne $null) { $qNames = Get-WMQQueueNames($qmgr)

foreach ($queuename in $qNames) { $nextQueue = $qmgr.AccessQueue($queuename.Trim(), [IBM.WMQ.MQC]::MQOO_INQUIRE)

Write-Output $nextQueue } } else { Write-Host "No queue manager connection" }}Let's start by looking at an ad-hoc approach to adding WMQ support to PowerShell.

The code isn't very important, and I wont read through it all, but there are a few points worth highlighting.

1) We put all of this in a .ps1 file. If you put this in the right directory, it will be available to every new PowerShell window you open

2)You wrap your script in a 'function' block

Ad-hoc

function Get-WMQQueue ($qmgr){ # display details of any WMQ errors encountered in this function Trap [IBM.WMQ.MQException] { Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode) continue }

# if we have a connection to a queue manager... if ($qmgr -ne $null) { $qNames = Get-WMQQueueNames($qmgr)

foreach ($queuename in $qNames) { $nextQueue = $qmgr.AccessQueue($queuename.Trim(), [IBM.WMQ.MQC]::MQOO_INQUIRE)

Write-Output $nextQueue } } else { Write-Host "No queue manager connection" }}

I've skipped a couple of steps and got a bit advanced by using Trap (sort of like a .NET try...catch) to capture errors

If an exception of the type MQException is thrown, this bit of script here gets run

Ad-hoc

function Get-WMQQueue ($qmgr){ # display details of any WMQ errors encountered in this function Trap [IBM.WMQ.MQException] { Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode) continue }

# if we have a connection to a queue manager... if ($qmgr -ne $null) { $qNames = Get-WMQQueueNames($qmgr)

foreach ($queuename in $qNames) { $nextQueue = $qmgr.AccessQueue($queuename.Trim(), [IBM.WMQ.MQC]::MQOO_INQUIRE)

Write-Output $nextQueue } } else { Write-Host "No queue manager connection" }}

If you want to display a message, you don't print to the console, you use Write-Host

Because the user might not want the output going to the console they might be directing it to another command. So Write-Host returns the text to the appropriate host

Ad-hoc

function Get-WMQQueue ($qmgr){ # display details of any WMQ errors encountered in this function Trap [IBM.WMQ.MQException] { Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode) continue }

# if we have a connection to a queue manager... if ($qmgr -ne $null) { $qNames = Get-WMQQueueNames($qmgr)

foreach ($queuename in $qNames) { $nextQueue = $qmgr.AccessQueue($queuename.Trim(), [IBM.WMQ.MQC]::MQOO_INQUIRE)

Write-Output $nextQueue } } else { Write-Host "No queue manager connection" }}

You use Write-Output to return stuff

Remember that commands are designed for living in a pipeline you don't return a finished set of objects. You return each object as you get it.

Ad-hoc

function Get-WMQQueueManager($name, $hostname, $port, $svrconn){ # display details of any WMQ errors encountered in this function Trap [IBM.WMQ.MQException] { Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode) continue }

# hashtable to describe the connection to the queue manager $connProperties = New-Object System.Collections.Hashtable

if (($hostname -eq $null) -and ($port -eq $null) -and ($svrconn -eq $null)) {

# user has not provided any information for a client connection # so we default to a local bindings connection type

$connProperties.Add([string][IBM.WMQ.MQC]::TRANSPORT_PROPERTY, [string][IBM.WMQ.MQC]::TRANSPORT_MQSERIES_BINDINGS)

}

I wont read through the details of these, but the slides are available if you want to examine them later.

I've included them to show some examples of syntax

Remember:- square brackets to cast- namespaces in square brackets with double-colon to access things - New-Object (one of the utility cmdlets) lets you create anything available to you in .NET

Ad-hoc

else {

# user has provided some information for a client connection # # a future version of this should provide support for other # connection types (e.g. managed or XA client) but for # my initial purposes, bindings and client connections are # sufficient

$connProperties.Add([string][IBM.WMQ.MQC]::TRANSPORT_PROPERTY, [string][IBM.WMQ.MQC]::TRANSPORT_MQSERIES_CLIENT)

if ($hostname -ne $null) { $connProperties.Add([string][IBM.WMQ.MQC]::HOST_NAME_PROPERTY, $hostname) }

Ad-hoc

if ($svrconn -ne $null) { $connProperties.Add([string][IBM.WMQ.MQC]::CHANNEL_PROPERTY, $svrconn) } else { # use a sensible default # this wont be to everyone's tastes, but will often be # right for me, and will save me a lot of typing!

$connProperties.Add([string][IBM.WMQ.MQC]::CHANNEL_PROPERTY, "SYSTEM.DEF.SVRCONN") }

Remember if you are converting C# code to a PowerShell function, you need to change your comparison operators.

PowerShell uses operators like -gt -ge -lt -le -eq -ne

Ad-hoc

if ($port -ne $null) { $connProperties.Add([string][IBM.WMQ.MQC]::PORT_PROPERTY, $port) } else { # use a sensible default # this wont be to everyone's tastes, but will often be # right for me, and will save me a lot of typing!

$connProperties.Add([string][IBM.WMQ.MQC]::PORT_PROPERTY, "1414") } }

return New-Object IBM.WMQ.MQQueueManager($name, $connProperties)}

Ad-hoc

function Get-WMQQueueNames($qmgr){# display details of any WMQ errors encountered in this functionTrap [IBM.WMQ.MQException]{Write-Error ("WMQ Exception: CC=" + $_.Exception.CompletionCode + " RC=" + $_.Exception.ReasonCode)continue}

# if we have a connection to a queue manager...if ($qmgr -ne $null){# using PCF to access a list of queue names## this is sort of cheating - this is an undocumented, unsupported# API, and I wrote this using tab-complete to identify what# seems like sensible method names## please do *not* take this as any sort of IBM recommendation# or endorsement to use PCF in C##[IBM.WMQ.PCF.PCFMessageAgent]$agent = New-Object IBM.WMQ.PCF.PCFMessageAgent$agent.Connect($qmgr)

Ad-hoc

[IBM.WMQ.PCF.PCFMessage]$request = New-Object IBM.WMQ.PCF.PCFMessage([IBM.WMQ.MQC]::MQCMD_INQUIRE_Q_NAMES)$request.AddParameter([IBM.WMQ.MQC]::MQCA_Q_NAME, "*")$request.AddParameter([IBM.WMQ.MQC]::MQIA_Q_TYPE, [IBM.WMQ.MQC]::MQQT_LOCAL) [IBM.WMQ.PCF.PCFMessage[]]$responses = $agent.Send($request, $TRUE)[IBM.WMQ.PCF.PCFParameter[]]$pcfParms = $responses[0].GetParameters()$queueNames = $pcfParms[0].GetValue()

# we don't want to display temporary queues# (such as that which will have been created by the PCF command!)# so we filter the response array before returning itreturn $queueNames | Where-Object -FilterScript {$_ -notlike "AMQ.*"}}else{Write-Host "No queue manager"}}

Ad-hoc

PS C:\> Get-WMQQueue (Get-WMQQueueManager test) | Select Name, CurrentDepth

Name CurrentDepth---- ------------DALE 0SALES.1 1SALES.2 15SALES.3 9SALES.4 1SALES.5 4SALES.6 2SYSTEM.ADMIN.ACCOUNTING.QUEUE 0SYSTEM.ADMIN.ACTIVITY.QUEUE 0SYSTEM.ADMIN.CHANNEL.EVENT 0SYSTEM.ADMIN.COMMAND.QUEUE 0SYSTEM.ADMIN.LOGGER.EVENT 0SYSTEM.ADMIN.PERFM.EVENT 0SYSTEM.ADMIN.QMGR.EVENT 1SYSTEM.ADMIN.STATISTICS.QUEUE 0SYSTEM.ADMIN.TRACE.ROUTE.QUEUE 0SYSTEM.AUTH.DATA.QUEUE 61SYSTEM.CHANNEL.INITQ 0SYSTEM.CHANNEL.SYNCQ 0SYSTEM.CICS.INITIATION.QUEUE 0SYSTEM.CLUSTER.COMMAND.QUEUE 0

And here we go the function on the previous half-dozen slides lets you do this.

And most of it came from .NET code that I had lying around anyway converting it to a PowerShell script took no time at all.

http://dalelane.co.uk/blog/?p=153 some more info on this that I wrote a while ago

http://channel9.msdn.com/ShowPost.aspx?PostID=256835

Production

Let's look at the other end of the spectrum you can produce a DLL snap-in with new cmdlets to extend the core PowerShell set.

The easiest tool to do this with is probably Visual Studio.

And the plugin at this URL makes it even easier.

It lets you create Visual Studio projects that already have the settings configured to build DLLs suitable for PowerShell snap-ins.

And it adds menu items to create new CmdLet objects these create new skeleton classes with the mandatory methods already in place, ready for you to fill in an implementation.

This all gets you a development environment ready for writing your first Cmdlet without needing to worry about how to build it.

(There is a nice blog post linked off that page which gives more detailed instructions, too.)

Production

How to Create a Windows PowerShell Cmdlet walkthrough

http://msdn2.microsoft.com/en-us/library/ms714598.aspx

Writing a Cmdlet is a bit tricky, and I don't have time to go into much detail.

In the next few slides, I will give a quick introduction, but I would recommend this MSDN page as a place to start if you decide to give this a try it's a walkthrough taking you step-by-step through the work involved in writing a Cmdlet.

Production

How to Create a Windows PowerShell Cmdlet walkthrough

http://msdn2.microsoft.com/en-us/library/ms714598.aspx

PowerShell Cmdlet guidelines

http://msdn2.microsoft.com/en-us/library/ms714657.aspx

I'd also recommend the Cmdlet guidelines here.

These are a good place to start when planning what your Cmdlet should do.

This is the definitive place for finding out what norms your Cmdlet should conform to.

#region GetProcCommand /// /// This class implements a Get-Proc cmdlet that has no parameters. ///

[Cmdlet(VerbsCommon.Get, "Proc")] public class GetProcCommand : Cmdlet { #region Cmdlet Overrides /// /// For each of the requested process names, retrieve and write /// the associated processes. /// protected override void ProcessRecord() { // Get the current processes Process[] processes = Process.GetProcesses(); // Write the processes to the pipeline making them available to the // next cmdlet. The second parameter tells PowerShell to enumerate the // array, and send one process at a time to the pipeline WriteObject(processes, true); } #endregion Overrides

} #endregion GetProcCommandProduction

The walkthrough takes you through the steps necessary to write an implementation of
Get-Process

(With the extension installed) Visual Studio will create you the skeleton Cmdlet class with ProcessRecord being the method you need to implement.

So for your product, you would replace this code in ProcessRecord with code which got a particular type of object used in your product. For WMQ, this could be queues, for example.

Production

Visual Studio will compile your code into a DLL.

Production

You then install this using InstallUtil

Production

PS C:\> Add-PSSnapin WebSphereMQ

Then, open a PowerShell prompt and run

Add-PSSnapIn to add the new cmdlet(s) to your PowerShell environment

What can I do with PowerShell?

try stuff out in an interactive shell

tie a few things together in utilities

build commands up into a script

generalize (parameterize, etc.)

clean up and productize

share!

I mentioned before that the PowerShell functions can be polished into production-quality commands that you can share with customers or third-parties.

If you write your own Cmdlets you could share this as a DLL, suitable for adding to PowerShell as I've just shown.

Or, you could build them into a shell suitable for sharing

Make-Shell lets you create custom shells suitable for sharing so you can provide your customers or third-party with a finished executable that includes your new cmdlets.

Extending function--> scripts and Cmdlets

Extending data stores--> providers

Extending PowerShell

I've talked about how to add new function to PowerShell by writing scripts or custom cmdlets

This isn't the only way you can extend PowerShell.

Remember the different data sources that PowerShell supports.

You might not want to extend the functions that PowerShell has, but to add support for a different data source. This is also possible, by writing a custom provider.

There wasn't time in an (already packed!) hour presentation to talk about this, but if there is enough demand, I could go through this in a separate presentation.

What is Windows PowerShell?

How does PowerShell work?

How can I hack PowerShell so that it can be used with my product?

Recap

So to recap.

Windows PowerShell an environment for system administrators, providing support for advanced scripting and an interactive shell, as well as a platform on which to build GUIs. An object-oriented shell which conforms to standard naming and function patterns, letting you move from product to product with a reduced learning curve. A .NET shell, giving access to anything in the .NET landscape from the command line.

Finally, an extensible environment, with support for scripts, as well as custom functions (through cmdlets) and data types (through providers).

Dale Lane

[email protected] Hursley Park

Windows
PowerShell

An introduction to...

Please feel free to contact me if you have any questions.

(For IBM audiences) I'm happy to help with any PowerShell projects if you're looking to add PowerShell support to your product, please don't hesitate to get in touch.

beforenow

GUIMMCGUIs built on top of PowerShell

Interactive shellCMDPowerShell

ScriptingBAT in CMDPowerShell

COM WMI (VBScript and JScript)PowerShell