The PowerShell Conference Book

Back in May, Mike Robbins (@mikefrobbins) asked if I wanted to contribute a chapter to a book he was putting together. The book would include chapters from different scripters in the PowerShell community and each would provide material that would be similar to a session at a conference.
In addition, the proceeds from the sale of the book would support the DevOpsCollective OnRamp scholarship for new IT pros going to the PowerShell and DevOps Conference in 2019.

Sounded like fun, so I signed on. We did the writing in markdown in a private GitHub repo. Not at all what I’m used to for writing but it was a really good experience. Mike was joined by Mike Lombardi (@barbariankb, from the STLPSUG) and Jeff Hicks (@jeffhicks). They did a great job corralling over 30 writers and at this point, the book is at 90% complete.

If you haven’t heard about this, go over to leanpub and take a look. The table of contents alone should convince you that this book is worth your time.
My chapter, Rethinking PowerShell GUIs, went live last friday (8/3) and talks about the beginnings of WPFBot3000 and a “companion” module called ContextSensitiveMenus which I haven’t blogged about yet.

I would enjoy hearing feedback on my chapter and the book in general.

–Mike

PowerShell DSL Module Considerations

Just a quick note to mention a couple of things I’ve come across with PowerShell modules that encapsulate DSLs (Domain Specific Languages, specifically WPFBot3000).

PowerShell Command Name Warnings

PowerShell modules have always issued warnings if they contain commands that don’t use approved verbs.  What’s fun with modules for DSLs is that the commands in general don’t use verbs at all.  Since these commands aren’t “proper”, you might expect a warning.  You won’t get one, though.

PowerShell Module Autoloading

PowerShell modules have had autoloading since v3.0.  Simply put, if you use a cmdlet that isn’t present in your session PowerShell will look in all of the modules in the PSModulePath and try to find the cmdlet somewhere.  If it finds it, PowerShell imports the module quietly behind the scenes.

This doesn’t work out of the box with DSLs.  The reason is simple.

DSLs generally have commands that aren’t in the verb-noun form that PowerShell is expecting for a cmdlet, so it doesn’t try to look for the command at all.

The fix for this is simple (for WPFBot3000, at least).  All I’ve done is replace the two top-level commands (Window and Dialog) with well-formed cmdlet names (New-WPFBotWindow and Invoke-WPFBotDialog).  Then, I create aliases (Window and Dialog) pointing to these commands.

Now that I think of it, I’m not sure why that works.  If PowerShell is looking for the aliases, why wasn’t it finding the commands?  Nevertheless, it works.

 

That’s all for today, just a couple of oddities.

–Mike

WPFBot3000 – Approaching 1.0

I just pushed version 0.9.18 of WPFBot3000 to the PowerShell Gallery and would love to get feedback. I’ve been poking and prodding, refactoring and shuffling for the last month or so.

In that time I’ve added the following:

  • Attached Properties (like Grid.Row or Dockpanel.Top)
  • DockPanels
  • A separate “DataEntryGrid” to help with complex layout
  • A ton of other controls (DataGrid and ListView are examples)
  • A really exciting BYOC (bring your own controls) set of functions

Mostly, though, I’ve tried to focus on one thing: reducing the code needed to build a UI.

To that end, here are a few more additions:

  • Variables for all named controls (no more need to GetControlByName()
  • -ShowForValue switch on Window which makes it work similarly to Dialog

In case you haven’t looked at this before, here’s the easy demo:

$output=Dialog {
    TextBox FirstName
    TextBox LastName
    Calendar Birthdate
}
'{0} {1} was born on {2}' -f $output.FirstName,$output.LastName,$output.Birthdate

There are tons of features to talk about, and I’ll be following up with a series of posts illustrating them.

In the meantime, use Install-Module WPFBot3000 (or Update-Module WPFBot3000) and let me know what you think.

Are there things that are missing?

Are there things that are broken?

Is something difficult to do that shouldn’t be?

Feel free to enter issues in the github repo or let me know through twitter, email, Reddit, etc.

-Mike

Introducing WPFBot3000

Preamble

After 2 “intro” posts about writing a DSL for WPF, I decided to just bit the bullet and publish the project. It has been bubbling around in my head (and in github) for over 6 months and rather than give it out in installments I decided that I would rather just have it in front of a bunch of people. You can find WPFBot3000 here.

Before the code, a few remarks

A few things I need to say before I get to showing example code. First, naming is hard. The repo for this has been called “WPF_DSL” until about 2 hours ago. I decided on the way home from work that it needed a better name. Since it was similar in form to my other DSL project (VisioBot3000), the name should have been obvious to me a long time ago.

Second, the main reasons I wrote this are:

  • As an example of a DSL in PowerShell
  • To allow for easier creation of WPF windows
  • Because I’m really not that good at WPF

In light of that last point, if you’re looking at the code in the repo and you see something particularly horrible, please enter an issue (or even better a pull request with a fix). As far as the first two go, you can be the judge after you’ve seen some examples.

Installing WPFBot3000

WPFBot3000 can be found in the PowerShell Gallery, so if you want to install it for all users you can do this:

#in an elevated session
Install-Module WPFBot3000

Or, if you want to install it for the current user only, do this:

Install-Module WPFBot3000 -Scope CurrentUser

If you’d rather, you can clone the repo from github (it has examples and tests) and install it from the WPFBot3000 subfolder.

A first example

Here’s a pretty simple example that should look familiar if you read the previous posts.

Import-Module WPFBot3000
Dialog {
   TextBox FirstName
   TextBox LastName
   TextBox EmailAddress
   DatePicker ReminderDate
}

Running that shows a window like this (the gridlines are for debugging layout and they will be going away/configurable in an upcoming release):

Comparing the code to the examples in the previous posts you’ll notice the main command is now Dialog rather than Window.  There is still a Window command, but it just outputs the window object.  The Dialog command uses Window to build the object (after adding an OK and Cancel Button) and then calls ShowDialog() on it.  If the user presses OK, Dialog builds an object representing the controls in the window and outputs that object.  The output object has properties that match the names of the controls* in the window:

* Only named controls that know how to return a value

You’ll also notice that DatePicker has been implemented in addition to TextBox.

The current list of controls that have been implemented is as follows:

  • Button
  • CheckBox
  • ComboBox
  • CredentialPicker
  • DatePicker
  • DirectoryPicker
  • FilePicker
  • Grid
  • GridSplitter
  • Image
  • Label
  • ListBox
  • MutliLineTextBox
  • Password
  • StackPanel
  • TabControl
  • TabItem
  • TextBlock
  • TextBox
  • TreeView

Other Features

I’m not going to go into every feature present (more posts to come), but here are some teasers:

  • Context Menus

  • Event Handlers

  • Changing Properties (but not attached or derived properties yet)

That’s enough for tonight, but I’ll be posting more in the days and weeks to come.

Let me know what you think. Feel free to post

A PowerShell WPF DSL (Part 2) – Getting Control Values

Some Background

Before I start, I should probably take a minute to explain what a DSL is and why you would want to use one.

A DSL (domain-specific language) is a (usually small) language where the vocabulary comes from a specific problem domain (or subject).  Note that this has nothing to do with Active Directory domains, so that might have been confusing.

By using words that are naturally used in describing problems in this subject, it is possible to write using the DSL in ways that look less like programming and more like describing the solution.

For instance, in Pester, you might write part of a unit test like this (3.0 syntax):

It "Has a non-negative result" {
   $result | Should Be GreaterThan 0
}

There are some aspects of this that look like PowerShell (the $, the | and the {}), but in general it doesn’t look much like code. It looks more like a description of the test that we’re writing.

In the WPF DSL that I’m describing in these posts, I want to be able to write PowerShell scripts that create WPF applications in a way that the “WPF” part of the script is easy. At least, a lot easier than trying to generate XAML or automate the WPF classes using brute force. That being said, the code in my DSL is not some sort of magic, it’s just PowerShell code.

One interesting aspect of DSLs is that because they don’t look quite like code, they often ignore coding conventions. Note that the Pester example had a PowerShell cmdlet called “It”. No verb-noun convention followed there. Also, Using positional parameters is more common in a DSL. Not a problem, just something to be aware of.

Getting Control Values

One of the problems I listed at the end of the previous post (#3) was that reading the text property won’t work once we have other controls to think about besides textboxes.

My solution here has worked out really well. I decided to add a method to any control that I would want to get a value from. The method is called GetControlValue. It is responsible for knowing how to get the values from the control (which, as part of the control object it will have no problem with).

Here’s the updated Textbox function:

function TextBox {
    [CmdletBinding()]
    Param($Name)
    $Properties = @{ Name = $name ;MinWidth=100}
    New-Object System.Windows.Controls.TextBox -Property $properties | 
        Add-Member -Name GetControlValue -MemberType ScriptMethod -Value {$this.Text} -PassThru
}

Since it’s a ScriptMethod, we get to use $this to refer to the control, so $this.Text is the value we want.

With that in place, we need to update the If statement at the end of the Window function to use the ScriptMethod. Here’s what it looks like:

if($window.ShowDialog() -or $true){
if($window.ShowDialog() -or $true){
  $output=[Ordered]@{}
  foreach($item in $controls){
    $output[$item.Name]=$item.GetControlValue()
  }
  [PSCustomObject]$output
}

Conclusion

Here’s the checklist now:

  1. Only textboxes? Need several other controls
  2. OK/Cancel
  3. With other controls, reading the text property won’t be a strategy
  4. Nested controls
  5. Populating controls
  6. Events
  7. ContextMenus
  8. Setting control properties

Stay tuned for more…

–Mike

Starting a PowerShell DSL for WPF Apps

The problems

There is always a problem. In my case, I had two problems.

First, when I teach PowerShell, I mention that it’s a nice language for writing DSLs (domain-specific languages). If you want an in-depth look, Kevin Marquette has a great series on writing DSLs. I highly recommend reading it (and everything else he’s written). I’m going to cover some of the same material he did, but differently.

Anyway, back to the story. When I mention DSLs, I generally get a lot of blank stares. Then, I get to try to explain them, but I don’t have a great example (Pester, Psake, and DSC are a bit advanced). So I was looking for a DSL I could write that would be easy to explain, with the code and output straight-forward. That’s the first problem.

The second problem, again from teaching, is when I talk about writing GUIs. This is always a popular topic, and it’s a lot of fun to discuss the different options. I get asked about when it is a good idea to write a UI in PowerShell vs. when it would make more sense to do it in a managed language. My answer is something along the lines of “If it’s something simple like a data-entry form, then PowerShell is a great fit. If it has much complexity you are probably going to want to use C#.” I got thinking after teaching last November that writing a data-entry form in PowerShell really isn’t that easy.

PowerShell to the rescue!

I decided that I needed to remedy the situation. Writing a data-entry form (where we’re not super concerned about the look-and-feel) should be trivial.

My first thought was that I should be able to write something like this:

Window {
    TextBox FirstName
    TextBox LastName
    TextBox EmailAddress
}

And have PowerShell present you with a reasonable looking UI. After pressing Ok (Ok/Cancel would be added automatically), it would output an object with properties named FirstName, LastName, and EmailAddress.

The first swag at the code looked something like this (fwiw, this is a reconstruction, not the actual code).

First, the TextBox function:

function TextBox {
    [CmdletBinding()]
    Param($Name)
    $Properties = @{ Name = $name ;MinWidth=100}
    New-Object System.Windows.Controls.TextBox -Property $properties
}

That’s pretty simple. It takes the name as a parameter and outputs a textbox with that name.

The Window function is a bit trickier. The controls (textboxes and labels) are going to go in a 2-column grid (so we don’t have to fight hard with the layout).

Also, for now I’m going to leave off the ok/cancel buttons (so I also have to pretend you pressed ok), but this is a great start.

Here’s attempt 1 at the code:

Add-Type -AssemblyName PresentationFramework
function Window {
    [CmdletBinding()]
    Param($contents)
    $window = new-object system.windows.window -Property @{ SizeToContent = 'WidthAndHeight'}
    $grid = new-object System.Windows.Controls.Grid -Property @{Margin=10}

    $window.Content = $grid
    $grid.ColumnDefinitions.Add((new-object System.Windows.Controls.ColumnDefinition -property @{ Width = 'Auto'}))
    $grid.ColumnDefinitions.Add((new-object System.Windows.Controls.ColumnDefinition -property @{ Width = '*'}))
    $controls = & $Contents
    $row = 0
    foreach ($item in $controls) {
        $grid.RowDefinitions.Add((new-object System.Windows.Controls.RowDefinition ))
        $label = New-Object System.Windows.Controls.Label -Property @{Content = $item.Name}
        [System.Windows.Controls.Grid]::SetRow($label, $row)
        [System.Windows.Controls.Grid]::SetColumn($label, 0)
        $grid.Children.Add($label) | out-null

        [System.Windows.Controls.Grid]::SetRow($item, $row)
        [System.Windows.Controls.Grid]::SetColumn($item, 1)
        $grid.AddChild($item) | Out-Null
        $row++
    }
    if($window.ShowDialog() -or $true){
        $output=[Ordered]@{}
        foreach($item in $controls){
          $output[$item.Name]=$item.Text
        }
        [PSCustomObject]$output
      }
}

Most of the code is pretty straight-forward. I execute the $Contents parameter to get the controls. Then I loop through those and create labels in the first column and put the control in the second column.

After calling the ShowDialog() method on the window (and using -or $true to fake out the missing OK button), loop through the controls and output an object with properties and values harvested from the name and text of the controls.

Here’s what the example looks like when you run it:

 

 

 

 

Not a lot of code for that result.

What’s missing though?

  1. Only textboxes?  Need several other controls
  2. OK/Cancel
  3. with other controls, reading the text property won’t be a strategy
  4. Nested controls
  5. Populating controls
  6. Events
  7. ContextMenus
  8. Setting control properties

Stay tuned for more of this WPF DSL.

–Mike

P.S.  It has been pointed out to me that I didn’t explain what a DSL is.  For now, I’d point you to the Kevin Marquette articles I linked above.  I’ll post tomorrow with a quick “what is a DSL and why”.

Celebrating 1 Year of Southwest Missouri PowerShell User Group (SWMOPSUG)

So…we’ve been meeting in Springfield, MO for a year now.

Our first meeting was in June 2017 and had our “anniversary” meeting earlier this month.  Thanks to Scott for presenting a talk about PowerShell jobs!

We haven’t had big crowds, but we have had good consistent attendance.  Looking forward to another year and reaching out to more people in the community.

If you’re in the southwest Missouri area (or close by), let me know and I’ll be happy to see where we can get you scheduled to speak.

If you’re interested, you can find details about upcoming events on our meetup page.

–Mike

 

A Modest Proposal about PowerShell Strings

If you’ve used the PowerShell Script Analyzer before you are probably aware that you shouldn’t be using double-quoted strings if there aren’t any escape characters, variables, or subexpressions in them.  The analyzer will flag unnecessary double-quotes as problems.  That is because double-quoted strings are essentially expressions that PowerShell needs to evaluate.

Let me repeat that…

Double-quoted strings are expressions that PowerShell needs to evaluate.

Single-quoted strings, on the other hand, are just strings.  There’s nothing (aside from doubled single-quotes being replaced by a single single-quote) that needs to be done with them.

When I teach about PowerShell, I usually say something along the lines of “double quotes are an indication to PowerShell that there’s some work to do here”.  I was thinking about this the other day and I think a shift in terminology will be helpful.  Calling them double- and single-quoted strings is descriptive, but not very helpful.

My proposal is simply this:

Single-quoted strings will henceforth be called “strings”.

Double-quoted strings will henceforth be called “string expressions”.

“String expression” gives me the idea that it is going to be evaluated.  Which fits double-quoted strings perfectly.  It’s also shorter than saying “double-quoted string”, which is a bonus.

“String” sounds (to me, at least), like something static.  Matches the situation again.

For those that think “why not just use double-quoted strings all the time?”, I would counter: Would you use Resolve-Path against C:\?  Probably not, because it would be a waste of time.  There’s nothing to do.  Resolve-Path expands wildcards and there are no wildcards there.  I guess you could use Resolve-Path with every path just to be safe…but you get the point.

What do you think of this proposal?  Do “string” and “string expression” convey enough that they should be used?  Let me know in the comments.

FWIW, I’m going to be using them whether you agree.  🙂

 

–Mike

Get-Learning – Launching Powershell

I thought I’d take a few minutes and show several ways to launch PowerShell. I’ll start with the basics and maybe by the end there will be something you haven’t seen

before.

The Start Menu

One of the first places to look for PowerShell is in the Start Menu. Opening the start menu and typing “PowerShell” will get you something like this:


Note that there are several options

  • Windows PowerShell (64-bit console)
  • Windows PowerShell ISE (64-bit ISE)
  • PowerShell-6.0.0 (PowerShell Core…you might not have this)
  • Windows PowerShell (x86)  (32-bit console)
  • Windows PowerShell ISE (x86) (32-bit ISE)

There’s also a “debuggable package manager”, which is a Visual Studio 2017 tool (and essentially the 32-bit console).

For each of these, you can click on it to launch, but there are other options as well:

  • Click to run a standard PowerShell session
  • Right-Click and choose “Run As Administrator” to run an elevated session (if you are a local administrator)

You’ll also notice that the right-click menu has options to run the other versions (ISE/Console, 32/64-bit).

The Run dialog

From the Run dialog (Windows-R), you can type PowerShell or PowerShell_ISE to launch the 64-bit versions of these tools.

What you may not know (and I just learned this recently, thanks Scott) is that if you hit ctrl-shift-enter, instead of just hitting enter, it will run them elevated (as administrator).

Windows Explorer

The final place I’m going to mention is Windows Explorer.  If you have it open, you can launch PowerShell or the ISE in the current directory by typing PowerShell or PowerShell_ISE in the address bar like this:


Can you think of other ways to launch PowerShell (other than from PowerShell…that would be cheating)?  Let me know in the comments.

 

–Mike

Old School PowerShell Expressions vs New

In a recent StackOverflow answer, I wrote the following PowerShell to find parameters that were in a given parameter set (edited somewhat for purposes of this post):

$commandName='Get-ChildItem'
$ParameterSetToMatch='LiteralItems'

$ParameterList = (Get-Command -Name $commandName).Parameters.Values

foreach($parameter in $parameterList){
    $parameterSets=$parameter.ParameterSets.Keys
   if ($parameterSets -contains $parameterSetToMatch ){
        write-output $parameter.Name
    }
}

A quick note…it’s not correct. It only shows parameters that are explicitly in the parameter set. Items that aren’t marked with any parameter set are in all parameter sets, and this doesn’t include them. That is beside the point.

Note that I’m looking through collections with a loop and an if statement.

A bit better

I could have made it a bit better with Where-Object (it’s still a bit bad because hashtable iteration isn’t nice):

$ParameterList.GetEnumerator() | 
   Where-Object {$_.Value.parameterSets.Keys  -contains 'Items'} | 
   Select-Object -Property Key

The “new” way

When I say new, I mean “PowerShell 3.0 new”. I still have a lot of PowerShell 1.0 muscle-memory that I need to get rid of. This post is part of the attempt. 🙂

Now, I’m going to use two features that were added to PowerShell 3.0 that I don’t use often enough: Member Enumeration and Where().

Member Enumeration says I can refer to members of the items in the collection through the collection.

For instance,

(Get-ChildItem -file).Length

Get-ChildItem returned a collection of files which each have a length property.

So instead of using ForEach-Object or Select-Object, I can use dot-notation against the collection and get the properties of the items in the collection. Nifty shortcut.

The second feature I’m going to use is the Where() method. This method is available with any collection object, and is (in the simplest case) just like using Where-Object.

Putting those two together, I get this:

$ParameterList.GetEnumerator().Where{$_.Value.parameterSets.Keys  -contains 'Items'}.Key

What is amazing about this to me isn’t how short it is (although it is short).

The amazing part is that I logically looped through a collection of objects looking for items in the collection which matched specific criteria, then extracted a particular property of those objects….and I didn’t need to use a pipeline at all.

I’m a big fan of pipelines, and the general-purpose *-Object cmdlets allow us to manipulate data with ease. However, that power comes at a price. Pipelines cost memory and time. This expression doesn’t incur the “penalties” of using a pipeline but gets us all of the benefit.

What do you think? Is the new version better?