Quantcast
Channel: Windows PowerShell - SAPIEN Blog
Viewing all 308 articles
Browse latest View live

PowerShellGet-GUI Part 1

$
0
0

Back in mid-September, I presented a talk on “Building PowerShell GUI Tool Solutions” at the PowerShell Conference Asia in Singapore. The talk centered around GUI application design and development using PowerShell as the core language. During that talk , I developed an application that used PowerShell’s PSGet module to communicate with the PowerShell Gallery. Finishing work was done by Devin Leaman of SAPIEN Technologies. The recorded talk should be available at some point on the PowerShell Conference Asia Website.

The application creates an interface that displays gallery module information and allows the user to easily add and remove modules from their system. The complete source code can be found on the SAPIEN github repository here.

The github repository contains the complete PowerShell Studio source code as well as a packaged .EXE of the final project and an .MSI ready for deployment. For those without PowerShell Studio, we have exported the complete project into a .PS1 file that can be run from the PowerShell console. We have also taken the .PS1 file and “modularized” it and posted it to the PowerShell Gallery here.

So let’s take a look at the project…

PowerShellGet-GUI Interface

Figure 1

Figure 1 shows the completed application interface. The top left pane displays a sorted list of the modules found on the PowerShell Gallery. The top right pane shows details for the module selected from the module list. The bottom pane is basically a dump of all the actions that are taken by the user and PowerShell cmdlet responses.

The Details

At the top of our file we start off with two calls to Import-Module: the first to import PowerShellGet and the second to import PackageManagement. (Actually, these are inserted automatically by PowerShell Studio when we include the appropriate cmdlet in our code.)

Since GUI applications are event driven, most of our operational code goes into event-handlers. These are functions that are called by the application when a specific event occurs. For instance, when a user clicks on a button, that button’s “click” event handler is called. Default event handlers are automatically generated by PowerShell Studio when you double-click on an object in the designer. Event handlers are named using the following formula:

$<objectType><objectName>_<eventName>

So for a BUTTON named MyButton, the CLICK event handler would be named:

$buttonMyButton_Click

For the purposes of this blog article, we are only going to examine the actual code that gets executed when an event occurs. Bear in mind that many object properties have been manipulated in the object ‘s Property list to help create a working UI. These are not being discussed here.

 

Screen Shot 2015-10-30 at 10.56.24 AM

Figure 2

The first event handler that we are concerned with is the form_load event ($formPowerShellGet_Load), seen at the top of Figure 2. This event gets fired when the form is first loaded (as the name implies.) Our form_load event handler calls a user defined function called Check-NuGet which verifies the availability of NuGet on the system. If NuGet is not available it prompts the user to download and install it.  Add-TextToOutputWindow is the helper function that writes to the output pane at the bottom of the application.

With NuGet installed, we can then implement our menu selection File->Get Modules. Besides enabling some menu options which the reader can explore on their own, this event handler calls a user defined function called Update-ListView as seen in Figure 3.

Screen Shot 2015-10-30 at 1.48.20 PM

Figure 3

This is a fairly long function so let’s go through it a step at a time.

First, because we know that this function will take a bit of time (as it needs to communicate across the internet), we change the cursor to a wait cursor on line 22. We also could have put this code in a separate thread so as not to impact the UI. In lines 24-29, we store the top item’s index so that when we refresh the list we can return it to the same position. In lines 31-34, we check to see if there is an item selected, and if so, we store that item’s index.

Using the Find-Module cmdlet (line 40), we connect to the PSGallery and store the result in a local variable, $psgModules. Then in line 43 we use Get-InstalledModule to get a list of the currently installed modules and store that as well ($instModules).

In lines 46-65, we populate our list. By enclosing all the work between .BeginUpdate and .EndUpdate calls, we delay drawing the list until all the work has been done, thereby eliminating flashing of the list control.  For each module in $psgModules, we get the name and the version and place those items into the list control. We also store the module itself in the listItems’s TAG property for easy access to the module later. See this article for more detailed information about the TAG property.

In lines 55-63 we check to see if the module we just added is included in our installed modules ($instModules) array and if it is, we add a checkmark image to the list control.

Remember when we stored the current selection above in line 33? Well, next we use that to check to see if an item had previously been selected and if so, we select it in the list. Then we scroll the list to the same location we stored ($topIndex) in line 26 to restore the list to the same state it was in previously (assuming that it had been scrolled and had an item selected.)

Finally, we restore the cursor to an arrow cursor.

Whew! Quite a lot of work there. I think that is enough for this post. We’ll look at the rest of the code in part 2.

 

 

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  


PowerShellGet-GUI Part 2

$
0
0

Continuing on from where we left off in our last blog post entitled “PowerShellGet-GUI part 1″, let us examine some of the other features of the PowerShell Get GUI application.

With our list successfully loaded with PowerShell Gallery modules, the user can take several actions:

  1. Select a module to inspect it’s properties
  2. Select a non-installed module to install it
  3. Select an installed module to uninstall it

Case 1

Screen Shot 2015-11-03 at 1.54.27 PM

Figure 1

When the user clicks on an item in the list, the listview_ItemSelectionChanged event fires and is handled by our $lvModules_ItemSelectionChanged event handler seen in Figure 1 above. If an item has been selected, we first save the contents of the .Tag property to a local variable, $module. If you remember back to the previous post, when we filled our list we stored the actual module in the .Tag property for later access. See this article for more detailed information about the TAG property.

We then set a “wait” cursor, clear the details pane (top right of UI), then call Get-Member on the module and store the string output to $output. We assign that string output to the .text property of the details pane and return our cursor to the default arrow. We also set several menu options to enabled to create a good UI.

Case 2

A module from the list can be installed by selecting the File->Install Selected Module menu option or by right-clicking the module and selecting Install.

Screen Shot 2015-11-03 at 2.16.04 PM

Figure 2

Figure 2 shows the code that is executed when that menu option is selected. First we get the module name and present a verification dialog to the user. If the user clicks OK in the dialog, then we set a wait cursor. Next we call Install-Module with the module name, PSGallery as the repository, and scope it to the current user. The output of these commands is converted to a string and output to the OutPut pane at the bottom of the application. We update our list view which will put a checkmark on the module. Finally, we set our cursor back to the default arrow.

Case 3

An installed module can be uninstalled by electing the File->Uninstall Selected Module menu option or right-clicking on the module and selecting Uninstall.

Screen Shot 2015-11-03 at 2.23.51 PM

This is probably our simplest event handler. The code in figure 3 uses Try…Catch to handle any errors. In the Try section, we call Remove-Module using the text found in the first element of the list views .selectedItems array. Since there is only 1 selected item (set by an object property) we know that it will be the “0” item and the text for that selection is the module name. If this fails, then we show the error in the output pane.

Finally

There is a lot more going on in this application than what this post and its predecessor describe. Setting object properties, enabling and disabling menu options, etc. all work together to make a great user interface. There is plenty more that can be done with this app and we invite the community to experiment and make suggestions.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Engine Type Prerequisites

$
0
0

When building executables in PrimalScript or PowerShell Studio there are various engine types to choose from, each of them with a prerequisite that must be installed on the machine that will be running your program. For example, if you build an executable in PowerShell Studio and select the SAPIEN PowerShell V2 Host (Command Line) Win32 engine then the .NET Framework 2.0 is required on the target machine.

Here is a table that shows the prerequisites for each engine type:

Engine

Prerequisite

SAPIEN PowerShell V2 Host (Command Line) Win32 .NET Framework 2.0
SAPIEN PowerShell V2 Host (Command Line) x64 .NET Framework 2.0
SAPIEN PowerShell V2 Host (Windows Forms) Win32 .NET Framework 2.0
SAPIEN PowerShell V2 Host (Windows Forms) x64 .NET Framework 2.0
SAPIEN PowerShell V2 Host (Windows) Win32 .NET Framework 2.0
SAPIEN PowerShell V2 Host (Windows) x64 .NET Framework 2.0
SAPIEN PowerShell V3 Host (Command Line) Win32 .NET 4.0 Client Profile
SAPIEN PowerShell V3 Host (Command Line) x64 .NET 4.0 Client Profile
SAPIEN PowerShell V3 Host (Windows Forms) Win32 .NET 4.0 Client Profile
SAPIEN PowerShell V3 Host (Windows Forms) x64 .NET 4.0 Client Profile
SAPIEN PowerShell V3 Host (Windows) Win32 .NET 4.0 Client Profile
SAPIEN PowerShell V3 Host (Windows) x64 .NET 4.0 Client Profile
SAPIEN PowerShell V5 Host (Command Line) Win32 .NET Framework 4.5
SAPIEN PowerShell V5 Host (Command Line) x64 .NET Framework 4.5
SAPIEN PowerShell V5 Host (Windows Forms) Win32 .NET Framework 4.5
SAPIEN PowerShell V5 Host (Windows Forms) x64 .NET Framework 4.5
SAPIEN PowerShell V5 Host (Windows) Win32 .NET Framework 4.5
SAPIEN PowerShell V5 Host (Windows) x64 .NET Framework 4.5
CMD Win32 Visual Studio 2012 Runtime
CMD x64 Visual Studio 2012 Runtime
CScript (Windows Script Host) Win32 Visual Studio 2012 Runtime
CScript (Windows Script Host) x64 Visual Studio 2012 Runtime
Microsoft Windows PowerShell (Command Line) Win32 Visual Studio 2012 Runtime
Microsoft Windows PowerShell (Command Line) x64 Visual Studio 2012 Runtime
MSHTA Win32 Visual Studio 2012 Runtime
MSHTA x64 Visual Studio 2012 Runtime
SAPIEN Script Host (Command Line) Win32 Visual Studio 2012 Runtime
SAPIEN Script Host (Command Line) x64 Visual Studio 2012 Runtime
SAPIEN Script Host (Windows) Win32 Visual Studio 2012 Runtime
SAPIEN Script Host (Windows) x64 Visual Studio 2012 Runtime
WScript (Windows Script Host) Win32 Visual Studio 2012 Runtime
WScript (Windows Script Host) x64 Visual Studio 2012 Runtime

 

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Rewind: A Built-In Safety Net in PowerShell Studio

$
0
0

We just released the second video in our “Check out this feature” series, a series of short videos that focus on features you might have missed.

Our first video, Function Builder in SAPIEN PowerShell Studio, demonstrated our awesome automation for function and parameter syntax.

Our second video, Rewind in PowerShell Studio: A built-in safety net for your scripts, describes the Rewind and Create Restore Point features, which help you recover to a previous version of a script — or any PowerShell Studio file — even if you’ve saved, or closed and reopened the file multiple times. And, the video shows you how to use these features in a real-world scenario.

Rewind

You can find all of our videos on the SAPIEN Technologies YouTube channel. To be sure not to miss any of them, subscribe!

I hope you like these videos. As always, I really appreciate comments, suggestions and ideas.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Create a PowerShell GUI App with a Fixed-Size UI

$
0
0

The newest video in our “Check out this feature” series is actually an answer to a great question that was posed to me on Reddit. A PowerShell GUI with a Fixed-Size UI shows you how to use the features of PowerShell Studio to create a PowerShell GUI app that the end-user cannot resize.

FixedForm

 

A fixed-size UI is much simpler to manage because you don’t have to anchor the form controls or worry about them floating around the form when the user resizes the UI.  In this video, I demonstrate 3 different ways to do it. You can also create a “compromise” version where the background isn’t resizable, but the end-user can minimize and maximize the UI.

Our first video, Function Builder in SAPIEN PowerShell Studio, demonstrated our awesome automation for function and parameter syntax.

Our second video, Rewind in PowerShell Studio: A built-in safety net for your scripts, describes the Rewind and Create Restore Point features, which help you recover to a previous version of a script — or any PowerShell Studio file — even if you’ve saved, or closed and reopened the file multiple times. And, the video shows you how to use these features in a real-world scenario.

You can find all of our videos on the SAPIEN Technologies YouTube channel. To be sure not to miss any of them, subscribe!

I hope you like these videos. As always, I really appreciate comments, suggestions and ideas.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

New! “Check out this feature” Videos

$
0
0

How many times have you found about a feature of a SAPIEN product, like PowerShell Studio, and realized that 1) you never knew it existed and 2) it would have saved you gobs of time had you only known about it.

We’re introducing a new series of videos on our SAPIEN Technologies YouTube channel to help you keep up with all of the features in SAPIEN programs, like PowerShell Studio, PrimalScript, VersionRecall, and PowerShell HelpWriter. Even when you know these programs well, there are so many features that you might have missed a new one, or even an old one, that can make you more efficient and productive.

We just released the first one: Function Builder in SAPIEN PowerShell Studio.

I’m keeping these videos very short — less than 10 minutes — so you don’t spend too much of your valuable time watching them. There’s no chatter. We just show the feature and explain how you might use it.

I’ll tweet the new videos or mention them on our blog post, but to make sure you don’t miss one, you can also subscribe to our YouTube channel.

And, if you have any comments, requests, suggestions for videos in this short product feature series, comment here, on our forums, or tweet me a suggestion.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Parsing Parameters for a Script in an Executable File

$
0
0

This is the second blog in a multi-part series about designing a Windows PowerShell script that will be packaged in an executable file.

—–
The new input parsing features for executable files in PowerShell Studio and PrimalScript make the task of passing features to a script in an exe easier than ever. Users can enter parameter names and values when they run the exe, just as they would with a script. (To learn about the new parsing feature, see Passing Parameters to a Script in an Executable File.)

But, what if you want to do something a bit unconventional? In that case, you need to parse the input manually. And we’ve even made that easier for you by adding an $EngineArgs variable that returns the values typed at the command line as an array. You can also use the PowerShell Studio snippets in the Packager Functions directory, ‘Packager Commandline Parsing Functions’ and ‘Packager Convert EngineArgs to Hash Table.’

Why Parse Manually

In Passing Parameters to a Script in an Executable File, I explain that PowerShell Studio and PrimalScript parse Windows PowerShell parameter names and parameter values for you. You don’t need to grab the input strings, parse them, and associate them with your script parameters. It’s all done for you.

However, the executable passes only string values and, for the automatic parsing to work, the parameters must be in standard Windows PowerShell name and value format. Also, if the end-user wants to enter multiple values for a parameter, they must enclose the parameter values in a single quoted string.

Most of the time, you can live with these limitations, but if you cannot, or you want to do something unconventional, you need to parse the input strings manually. And that’s where the $EngineArgs variable comes in.

The $EngineArgs Variable

Beginning in PowerShell Studio 4.2.96 and PrimalScript 7.1.72, the $EngineArgs variable contains an array of the arguments entered at the command line.

You don’t have to define $EngineArgs. It’s added for you, much like an automatic variable in Windows PowerShell. But, it’s has values only when the script is packaged as an executable file.

For example, if I create a Get-EngineArgs.ps1 script, and run it, the $EngineArgs variable is empty.

#In Get-EngineArgs.ps1
$EngineArgs

When you run the script with a few arguments, it returns nothing, as expected.

PS C:\> .\Get-EngineArgs.ps1 Happy Birthday to you
PS C:\>

But, if I package it as an executable file in PowerShell Studio or PrimalScript, and then run Get-EngineArgs.exe, the $EngineArgs variable is populated.

PackageScript

 

And, then run the Get-EngineArgs.exe file with parameters and values (any parameters, any values), you get the values in $EngineArgs.

PS C:\> .\Get-EngineArgs.exe Happy Birthday to you
Happy
Birthday
To
You

$EngineArgs contains an array, so you can access the members of the array by using standard array notation. (Need help with array notation in Windows PowerShell? See about_Arrays.)

PS C:\> (.\Get-EngineArgs.exe Happy Birthday to you)[0]
Happy

PS C:\> (.Get-EngineArgs.exe Happy Birthday to you)[-1]
you

You can discover and filter the values in $EngineArgs.

PS C:\> $result = .\Get-EngineArgs.exe Happy Birthday to you
PS C:\> $result | where {$_ -like '*y'}
Happy
Birthday

PS C:\> 'you' -in $result
True

Using $EngineArgs for Slash and Switch Parameters

We all know the person who insists on slash-prefixed parameters (e.g. /name Joe /city Baltimore) instead of PowerShell’s dash-prefixed parameters (-Name Joe -City Baltimore). When you parse parameters manually, you can provide this experience to your users.

Also,  when you package a script in an executable file, you convert your Switch parameters to String parameters that take values of ‘True’ and ‘False’. But, if that really doesn’t work for your users, you can parse the parameters manually and process the switch parameter name without a value.

For example, the New-WordTree.ps1 script that we used in Part 1 of this series creates a word tree from a word and a number. Its AddSpace parameter, which is a Switch type, adds a space between the words in the word tree.

PS C:\> Get-Command .\New-WordTree.ps1 -Syntax
New-WordTree.ps1 [-Word] <string> [[-Number] <int>] [[-AddSpace] <Switch>] [<CommonParameters>]

PS C:\> .\New-WordTree.ps1 -Word PowerShell -Number 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

PS C:\> .\New-WordTree.ps1 -Word PowerShell -Number 5 -AddSpace
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell
PowerShell PowerShell PowerShell PowerShell
PowerShell PowerShell PowerShell PowerShell PowerShell

When you package the script in an executable file, the -AddSpace no longer works.

PS C:\&gt; .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace
Line 1: A positional parameter cannot be found that accepts argument ''.PS C:\&gt;

Instead, you have to enter a string value.

PS C:\> .\New-WordTree.ps1 -Word PowerShell -Number 3 -AddSpace True
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

When you parse the parameters manually, you can deliver an experience like this:

PS C:\> .\New-WordTree.exe /Word PowerShell /Number 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

You can also restore the experience of a switch parameter, even though we’re really passing strings.

PS C:\> .\New-WordTree.exe /Word PowerShell /Number 3 /AddSpace
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

And, still allow users to list the parameters in any order.

PS C:\> .\New-WordTree.exe /AddSpace /Word Automation /Number 3
Automation
Automation Automation
Automation Automation Automation

There are many ways to approach this task, but here’s what I did. I took the original New-WordTree.ps1 script and converted it into a function. Here’s the function code. It’s pretty cool.

function New-WordTree
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]$Word,
 
        [int]$Number = 1,
 
        [Switch]$AddSpace
    )
    $space = ''
    if ($AddSpace )
    { $space = ' ' }
 
    1..$Number | foreach { "$word$space" * $_ }
}

The New-WordTree function is now part of the New-WordTree.ps1 script. In the main part of the script, we won’t define any parameters. (You can, but it’s not necessary.) Instead, we’ll use the array in the $EngineArgs parameter.

My plan is to parse the values in $EngineArgs, create a hash table of parameters ($params), and then use splatting to pass the hash table to the New-WordTree function. (If you need help with splatting, see about_Splatting.

To parse the input in the $EngineArgs array, I use a For loop. I could have used ForEach, but when I find input that begins with a slash, I’m going to assume that it’s a parameter and that the next item in the array is the parameter value. For that sequence, I need to keep track of the positions (“indexes”) of the items in the $EngineArgs array. A For loop is the best way to manage those indexes.

My For loop is a bit unconventional, because it has a starting point ($i = 0) and a condition for continuing ($i -lt $EngingArgs.count), but it doesn’t have an automatic increment (typically $i++). I omitted the automatic increment (replaced by a ‘;’), because I want to increment by 2 when I encounter a parameter and its value, but increment by 1 when I encounter the AddSpace parameter, which doesn’t have a value.

Here’s my parsing loop.

for ($i = 0; $i -lt $EngineArgs.count;)
{
    # A parameter name (/*) followed by a value (no /)
    if ($EngineArgs[$i] -like '/*' -and $EngineArgs[$i + 1] -and $EngineArgs[$i + 1] -notlike '/*')
    {
        #Get rid of the slash. It can be a dash or unprefixed.
        $p = $EngineArgs[$i] -replace '/', ''
 
        #Add the parameter and its value to $params
        $params.Add($p, $EngineArgs[$i + 1])
 
        #Skip to the next parameter ($i = $i + 2)
        $i += 2
    }
    elseif ($EngineArgs[$i] -eq '/AddSpace')
    {
        $params['-AddSpace'] = $True
 
        #Go to the next item. Don't skip. ($i = $i + 1)
        $i++
    }
    else
    {
        throw $FormatError
}

If you want this parsing loop to enable Switch parameters, but keep dash-prefixed parameters, it’s just a bit simpler.

for ($i = 0; $i -lt $EngineArgs.count;)
{
    # A parameter name (-…) followed by a value (no -)
    if ($EngineArgs[$i] -like '-*' -and $EngineArgs[$i + 1] -and $EngineArgs[$i + 1] -notlike '-*')
    {
        #Add the parameter and its value to $params
        $params.Add($EngineArgs[$i], $EngineArgs[$i + 1])
 
        #Skip to the next parameter ($i = $i + 2)
        $i += 2
    }
    elseif ($EngineArgs[$i] -eq '-AddSpace')
    {
        $params['-AddSpace'] = $True
 
        #Go to the next item. Don't skip. ($i = $i + 1)
        $i++
    }
    else
    {
        throw $FormatError
}

The parsing is the hard part. The rest of the script is easier. I create some text strings and the $params hash table. Then, I make sure that $EngineArgs has a value. If it doesn’t, I display a help string. (For information about Help in an executable file, see Displaying Help for a Script in an Executable File.)

Finally, if the $params hash table has values in it, I call the New-WordTree function with the $params hash table. Because I’m splatting, I replace the ‘$’ of the $params variable with a ‘@’.

$params = @{ }
if (!$EngineArgs)
{
    New-WordTree -Word Help
}
else
{
    < parsing For-loop >
 
    if ($params.Count -gt 0)
    {
         New-WordTree @params
    }
}

Now, package the script as usual:  Home/Package/Build, and save it in an executable file, New-WordTree.exe

PS C:\> .\New-WordTree.exe /Word PowerShell /Number 3 /AddSpace
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

Using $EngineArgs for Multiple Values

You can also use manual parsing to make it easier for users to enter multiple values for a parameter. This is a bit more complex, so make sure it’s worth it to your users.

For example, the automatic parsing requires users to enter multiple values for a single parameter in a quoted string. Otherwise, the second value is interpreted as a positional value for the next parameter.

PS C:\ps-test> .\New-WordTree.exe -Word PowerShell, NanoServer, DSC -Number 3
Line 1: A positional parameter cannot be found that accepts argument 'NanoServer'.PS C:\ps-test>

The user must enter the value collection in a quoted string.

PS C:\ps-test> .\New-WordTree.exe -Word "PowerShell, NanoServer, DSC" -Number 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell
NanoServer
NanoServerNanoServer
NanoServerNanoServerNanoServer
DSC
DSCDSC
DSCDSCDSC

By parsing the $EngineArgs string manually, you can enable users to enter a typical Windows PowerShell command with a comma-separated string value.

NOTE: This scenario assumes that the user is running the executable file in Windows PowerShell. If they run it in Cmd.exe, the Command Prompt window, you need to remove the commas manually in your script.

PS C:\ps-test> .\New-WordTree.exe -Word PowerShell, NanoServer, DSC -Number 4
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell
PowerShellPowerShellPowerShellPowerShell
NanoServer
NanoServerNanoServer
NanoServerNanoServerNanoServer
NanoServerNanoServerNanoServerNanoServer
DSC
DSCDSC
DSCDSCDSC
DSCDSCDSCDSC

PS C:\> .\New-WordTree.exe -Word PowerShell, NanoServer, DSC -Number 4 -AddSpace
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell
PowerShell PowerShell PowerShell PowerShell
NanoServer
NanoServer NanoServer
NanoServer NanoServer NanoServer
NanoServer NanoServer NanoServer NanoServer
DSC
DSC DSC
DSC DSC DSC
DSC DSC DSC DSC

 

To make this work, I start with the simpler version of the For-loop.

for ($i = 0; $i -lt $EngineArgs.count;)
{
    # A parameter name (-…) followed by a value (no -)
    if ($EngineArgs[$i] -like '-*' -and $EngineArgs[$i + 1] -and $EngineArgs[$i + 1] -notlike '-*')
    {
 
        #Add the parameter and its value to $params
        $params.Add($EngineArgs[$i], $EngineArgs[$i + 1])
 
        #Skip to the next parameter ($i = $i + 2)
        $i += 2
    }
    elseif ($EngineArgs[$i] -eq '-AddSpace')
    {
        $params['-AddSpace'] = $True
 
        #Go to the next item. Don't skip. ($i = $i + 1)
        $i++
    }
    else
    {
        throw $FormatError
}

But, in this version, when I find a parameter, I save it in a $p variable, then use a While loop to continue examining the items in the $EngineArgs array. If I find a parameter value (-notlike ‘-*’), I add it to a $v string array. When I hit another parameter (-like -*), I stop. Then, I add the parameter in $p and the values in $v to the $param hash table. There’s a bit of fiddling to put the commas in the right place in the string array.

$p = $EngineArgs[$i]
# Create a variable to hold the values.
$v = [string]@()
 
# To find multiple values, start with the next item in $EngineArgs
$i++
while ($EngineArgs[$i] -and $EngineArgs[$i] -notlike '-*')
{
    # Unless it's the first item, it needs a comma
    if ($v -eq '')
    {
        $v += $EngineArgs[$i]
    }
    else
    {
        $v += ',' + $EngineArgs[$i]
    }
    $i++
}
$params.Add($p, $v)

Here’s the complete For loop with the While loop that manages multiple values.

    for ($i = 0; $i -lt $EngineArgs.count;)
    {
        if ($EngineArgs[$i] -like '-*' -and $EngineArgs[$i + 1] -and $EngineArgs[$i + 1] -notlike '-*')
        {
            $p = $EngineArgs[$i]
            $v = [string]@()
 
            #Find multiple values. Start with the next item in $EngineArgs
            $i++
            while ($EngineArgs[$i] -and $EngineArgs[$i] -notlike '-*')
            {
                if ($v -eq '')
                { $v += $EngineArgs[$i] }
                else
                { $v += ',' + $EngineArgs[$i]}
                $i++
            }
            $params.Add($p, $v)
        }
        elseif ($EngineArgs[$i] -eq '-AddSpace')
        {
            $params['AddSpace'] = $True
            $i++
        }
        else
        {
            throw $FormatError
        }
    }

As in the simpler case, when I’m done parsing the $EngineArgs values, I call the New-WordTree function with the $params hash table in its splatted form (@params).

if ($params.Count -gt 0)
{
    New-WordTree @params
}

That’s quite a bit of work, but it allows you to satisfy your most discerning users.

The default parameter name and parameter value parsing works for most Windows PowerShell scripts in executable files. But if you need to parse parameters in another style, the $EngineArgs variable makes this task manageable.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Displaying Help for a Script in an Executable File

$
0
0

This is the third blog in a multi-part series about designing a Windows PowerShell scripts that will be packaged in an executable file.

—–
The Get-Help cmdlet displays help topics about concepts and modules (About topics) and about commands (cmdlets, scripts, functions, workflows, and CIM commands). It does not display help topics for applications in executable files.

When you wrap a Windows PowerShell script in an executable file, even if the script has comment-based or XML help, Get-Help cannot find the help file. However, you can use a few techniques to restore some help functionality.

Recognize Help Strings

As we learned in Part 1 of this series, all parameters in a script that is wrapped in an executable file must take string types or types that Windows PowerShell converts from strings. So, we can use selected strings to recognize that the user is looking for help, instead of trying to run the command.

For example, I have a Get-InputTypes.ps1 script that finds parameters that take pipeline input. The script has a mandatory CmdletName parameter that specifies the name of the command to analyze.

When I run the .exe normally, it returns input type information.

PS C:\> .\Get-InputTypes.exe -CmdletName Get-Command

TypeNames       ParameterName ValueFromPipelineByPropertyName
---------       ------------- -------------------------------
System.String[] Name          True

The CmdletName parameter is positional, so I don’t need to type the parameter name.

 .\Get-InputTypes.exe Invoke-Command

TypeNames                             ParameterName ValueFromPipelineByPropertyName
---------                             ------------- ---------------------------
System.Management.Automation.PSObject InputObject True

But, when I run it with a value of ‘Help’ or ‘/?’, it displays help.

PS C:\> .\Get-InputTypes.exe /?
NAME
Get-InputTypes.exe
SYNOPSIS
Gets input types for cmdlet help topics.
SYNTAX
Get-InputTypes.ps1 [-CmdletName] <String> [-Full <String>]

DESCRIPTION
This app gets the input types for a cmdlet help topic, that is, it gets the .NET types that
you can pipe to the cmdlet. By default, it gets only types that can be piped
...

Here’s how I did it.

First, I added a $HelpString variable that contains help for the cmdlet. (For details, see Create a Help String‘. Next, I changed the first parameter in the script (Position 0) to recognize values of ‘/?’ and ‘Help’. Windows PowerShell has special processing for the ‘-?’ string, so I avoid that one.

This script has a CmdletName parameter that is mandatory and, by default, position 0.

[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true)]
[ValidateScript({Get-Command $_})]
[String]$CmdletName,
 
[Parameter()]
[ValidateSet('True', 'False')]
[String]$Full
)

To add help functionality, removed the ValidateScript attribute.

[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true)]
[String]$CmdletName,
 
[Parameter()]
[ValidateSet('True', 'False')]
[String]$Full
)

Then, I added a condition that looks for CmdletName values of ‘Help’ and ‘/?’. If found, the script displays a $HelpString string, instead of its regular processing.

if ($CmdletName -eq 'Help' -or $CmdletName -eq '/?')
{
    $HelpString
}
elseif ($full -eq 'True')
...

Add a Help Parameter

If you want to be a bit more sophisticated, rather than using the CmdletName parameter for an unintended use, you can add a Help parameter.

Here’s the original parameter set of the script:

[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true)]
[ValidateScript({Get-Command $_})]
[String]$CmdletName,
 
[Parameter()]
[ValidateSet('True', 'False')]
[String]$Full
)

I added a Help parameter that takes a string value. Then, because the CmdletName and Help parameters are exclusive, I’ve created separate ‘DefaultSet’ and ‘HelpSet’ parameter sets.

In a script or function, the parameters are positional by default. But when you add parameter sets, all parameters become named. For most cases, named parameters are a best practice. However, in this case, I want the user to be able to type: ‘Get-InputTypes.exe /?’ so I make the Help parameter mandatory and position 0 in the HelpSet parameter set.

Here’s the result.

[CmdletBinding(DefaultParameterSetName = 'DefaultSet')]
param
(
[Parameter(ParameterSetName = 'DefaultSet', Mandatory = $true. Position=0)]
[ValidateScript({Get-Command $_})]
[String]$CmdletName,
 
[Parameter(ParameterSetName = 'DefaultSet', Position=1)]
[ValidateSet('True', 'False')]
[String]$Full,
 
[Parameter(ParameterSetName = 'HelpSet', Mandatory = $true, Position = 0)]
[String]$Help
)

I also added code to take any value of the Help parameter.

if ($Help)
{
    $HelpString
}
elseif ($Full -eq 'True')
{
...

Now, when I wrap the script in an exe and run it:

PS C:\>.\Get-InputTypes.exe -CmdletName Set-ExecutionPolicy

TypeNames                            ParameterName ValueFromPipelineByPropertyName
---------                            ------------- --------------------------
Microsoft.PowerShell.ExecutionPolicy ExecutionPolicy True

PS C:\> .\Get-InputTypes.exe -Help True
NAME
Get-InputTypes.exe
SYNOPSIS
Gets input types for cmdlet help
SYNTAX
Get-InputTypes.exe [-CmdletName] <String> [-Full <String>]
Get-InputTypes.exe [-Help] <String>
...

PS C:\> .\Get-InputTypes.exe /?
NAME
Get-InputTypes.exe
SYNOPSIS
Gets input types for cmdlet help
SYNTAX
Get-InputTypes.exe [-CmdletName] <String> [-Full &lt:String>]
Get-InputTypes.exe [-Help] <String>
...

Make Help the Default

Many traditional command-line tools display help as the default. That is, if you run the command without parameters, it displays help. This is not true for Windows PowerShell. In fact, many PowerShell cmdlets, including Get-Command, Get-Process, and Get-Module, have very useful default displays. But, if you want to make your .exe file display help by default, it’s pretty easy to do that.

If you made your Position 0 parameter accepts special values, like ‘/?’ and ‘Help’, you can make that parameter optional. Then, add a case for a $Null or empty string value (if !$CmdletName…).

[CmdletBinding()]
Param
(
[Parameter()]
[String]$CmdletName,
 
[Parameter()]
[ValidateSet('True', 'False')]
[String]$Full
)
 
if (!$CmdletName -or $CmdletName -eq 'Help' -or $CmdletName -eq '/?')
{
    $HelpString
}
elseif ($full -eq 'True')
...

If you created a Help parameter in its own parameter set, to make the Help parameter accept an empty value:

— Make the Help parameter optional
— Make the HelpSet parameter set the default
— Check for the HelpSet parameter set in the script

For example:

[CmdletBinding(DefaultParameterSetName = 'HelpSet')]
param
(
[Parameter(ParameterSetName = 'DefaultSet', Mandatory = $true)]
[ValidateScript({Get-Command $_})]
[String]$CmdletName,
 
[Parameter(ParameterSetName = 'DefaultSet')]
[ValidateSet('True', 'False')]
[String]$Full,
 
[Parameter(ParameterSetName = 'HelpSet', Mandatory = $false, Position = 0)]
[String]$Help
)
 
if ($PSCmdlet.ParameterSetName -eq 'HelpSet')
{
    $HelpString
}
elseif ($Full -eq 'True')
{
...

Now, when I wrap the script in an exe and run it with no parameters, I get the help.

PS C:\> .\Get-InputTypes.exe
NAME
Get-InputTypes.exe
SYNOPSIS
Gets input types for cmdlet help topics.
SYNTAX
Get-InputTypes.ps1 [[-Help] <String>]
Get-InputTypes.ps1 [-CmdletName] <String> [-Full <String>]

DESCRIPTION
This app gets the input types for a cmdlet help topic, that is, it gets the .NET types that
you can pipe to the cmdlet. By default, it gets only types that can be piped
...

And, I can still use the Help parameter explicitly with any value.

PS C:\> .\Get-InputTypes.exe -Help /?
NAME
Get-InputTypes.exe
SYNOPSIS
Gets input types for cmdlet help topics.
SYNTAX
Get-InputTypes.ps1 [[-Help] <String>]
Get-InputTypes.ps1 [-CmdletName] <String> [-Full <String>]

DESCRIPTION
This app gets the input types for a cmdlet help topic, that is, it gets the .NET types that
you can pipe to the cmdlet. By default, it gets only types that can be piped
...

Create a Help String

There are many ways to create the $HelpString value for a Windows PowerShell script in an executable file.

To make it look like the Get-Help display, I write comment-based or XML help for script, run ‘Get-Help <ScriptName>.ps1 -Full,’ copy the content that Get-Help returns, and paste it in a here-string in my script. To make it easier to read, I add a few blank lines at the top and bottom of the string.

$helpString = @"
 
NAME
C:\ps-test\Get-InputTypes.ps1
 
SYNOPSIS
Gets input types for cmdlet help
 
SYNTAX
Get-InputTypes.exe [-CmdletName] &lt;String&gt; [-Full &amp;lt:String&gt;]
Get-InputTypes.exe [-Help] &lt;String&gt;
 
DESCRIPTION
This script gets the input types for a cmdlet
help topic, that is, it gets the .NET types that
you can pipe to the cmdlet.
 
…
"@

It’s much easier to use Get-Help to display help, but when it’s not available, it’s great to have easy substitutes.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  


Enable WMI Explorer Windows 8 Remote Connection Permissions

$
0
0

Earlier in October, I posted a blog about remote connection failures that occur when using WMI Explorer on a remote computer running Windows 8. This solution doesn’t apply to computers running other versions of Windows.

To sum it up, you need to make three changes for WMI Explorer to connect to a Windows 8 remote machine.

  • Allow Remote Administration through the Windows Firewall
  • Give the user the required remote access to COM security
  • Give the user the required remote access to the WMI root namespace and sub-namespaces.

For those who have this issue, but don’t have the time to go through all of the steps, I have created a script to solve the problem.

To use the script, run it locally on the remote machine in an elevated session (Run as administrator). This script is not designed to be run remotely, for example, by using Invoke-Command. Because it grants permission to the current user, be sure that the script runs in the user account of the person running WMI Explorer.

To follow the progress of the script, use its Verbose parameter.

About the Script

After digging around for a PowerShell replacement to netsh firewall, I found which seemed like what I needed, but there was just one problem. When running the actual command:

Set-NetFirewallRule –DisplayGroup 'Remote Administration'

It threw an ObjectNotFound exception, saying that there were no rules with a DisplayGroup equal to ‘Remote Administration’.

WMI_Permissions_Image_1

 

Come to find out that the firewall rules don’t exist until you run:

netsh firewall set service RemoteAdmin enable

With that, I decided that, in my script, I would use netsh firewall as opposed to Set-NetFirewallRule to avoid having to build each rule individually and ensure they were configured properly.

WMI_Permissions_Image_2

 

Up next is the COM security permissions. You need to ensure that the current user has remote access permissions. In my original post, I made sure that Everyone had remote access, because it worked and security wasn’t an issue on a VM.

Instead, I asked around and learned that I could assign the current user to the Distributed COM Users group which it has remote access permissions by default. To add a user to a group, I found a post by the Scripting Guys describing just how to do this in PowerShell. The big difference between their post and my script was adding $User and $Computer variables to represent the current username and computer name.

The final step was to assign Remote Access rights to the Authenticated Users group so that they can access the root namespace and sub-namespaces. When it comes to doing this in a script, I owe a lot to Karl Mitschke and his post, Script remote DCOM / WMI access for a non admin.

Of course, there were quite a few things that needed to be removed, since we simply don’t need them, such as the $computers variable, foreach loop, and variables associated with DCOM permissions. In his script, the $SDDL variable determines which permissions are set and it is currently set up to give remote access to the specified namespace, but not to the sub-namespaces. By adding CI; to the string, it becomes $SDDL = “A;CI;CCWP;;;$SID” and now it gives the permissions across sub-namespaces as well.

Run the script with Administrator permissions. – Set-WMIExplorerPermissions_FINAL

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Spotlight on the Label Control

$
0
0

The Spotlight on Controls series describes the controls, that is, the objects in the System.Windows.Forms namespace, so you can use them effectively when building GUI apps in PowerShell Studio and PrimalScript.

Each post focuses on one control and lists its most important properties, methods, and events, including the default event that PowerShell Studio adds to your script when you double-click the control. The posts include many examples written in Windows PowerShell, so you can use them right away.

Read more: Spotlight on Controls: Common properties and methods

This post describes the Label control.

Label Control [System.Windows.Forms.Label]

Creates a clickable text box in a GUI application. Use a label to display text strings.

image

Tip: To display multiple lines of text, use a Textbox or RichTextBox control.


Important properties of the Label object

Important Events:



 

Text property: Determines the text in the label

Use the Text property to set, get, or change the text that is displayed on the label.

Value typeSystem.String
Default value: (None)

To set the initial value of Text property, that is, the value that appears when the GUI app starts, use the Properties pane. On the Text row, type the text that appears on the Label.

LabelText

NOTE: When you change the value of the Text property of a Label in the Properties pane, PowerShell Studio changes the name of the variable that stores the Label object in your script. For example, if you change the Text property value to ‘Close’, PowerShell Studio changes all references of the Label variable to $LabelClose.

To get or change the label text while the GUI app is running, use the Text property in the event handlers in your script.

To get the text in the label:

$LabelMain.Text

To set the text in the label use an assignment statement. You can include variables and expressions in the assigned string value.

$Name = 'PowerShell'
$LabelMain.Text = "Hello, $Name"

To change the text in the label:

$LabelMain_click = {
 
    # Get the text currently in the label and evaluate it.
    If ($LabelMain.Text -eq 'Click')
    {
        $LabelMain.Text = 'Clicked' # Change the text in the label.
    }
    Else
    {
        $LabelMain.Text = 'Click'
    }
}

Tip: To direct Windows PowerShell to evaluate an expression before inserting the result in a string, use a subexpression. The syntax is $( <expression ).

For example:

Without subexpression:

$LabelMain.Text = "Building automation on (Get-Process 'PowerShell Studio').MainWindowTitle"

image

With subexpression:

$LabelMain.Text = "Building automation on $((Get-Process 'PowerShell Studio').MainWindowTitle)"

image

Font property: Determines the style and size of the label text

Use the Font property to get, set, or change the font of the label text.

Value Type: System.Drawing.Font
Default: Microsoft Sans Serif 8.25.

To set the font easily, in the Properties pane for the Label, in the Font row, click the ellipsis (…) and then use the Font selector.

image

 

To display the properties of the Font object, in the Properties pane, click the Font group arrow.

image

To script a font change, set the Font property to a string with the Font name, size, and style.

$labelMain.Font = 'Segoe UI, 15.75pt, style=Bold, Italic'

image

To determine other properties of the font, create a System.Drawing.Font object.

$ClickFont = [System.Drawing.Font]::new('Microsoft Sans Serif', 8.25, [System.Drawing.FontStyle]::Regular)
$ClickedFont = [System.Drawing.Font]::new('Segoe UI', 16, [System.Drawing.FontStyle]::Italic)
 
$labelMain_Click={
    if ($labelMain.Text -eq 'Click')
    {
        $labelMain.Text = 'Clicked'
        $labelMain.Font = $ClickedFont
    }
    else
    {
        $labelMain.Text = 'Click'
        $labelMain.Font = $ClickFont
    }
}

Tips:

  • To enlarge the label automatically to fit its contents, set the AutoSize property of the Label to $True.
  • When scripting a font, be sure to set the Font property of the Label. The Text property of the Label does not have a Font property.

ForeColor property: Determines the color of the label text.

Use the ForeColor property to get, set, and change the color of the text in the label.

Values: Any [System.Drawing.Color] type
Default: ControlText (Black)

To set the ForeColor property in the Properties pane.

image

image

To set ForeColor property in a script:

$Label1.ForeColor = [System.Drawing.Color]::Red

-or-

$Label1.ForeColor = "Red"

BackColor property: Determines the color of the background in the label

Use the BackColor property to get, set, and change the color of the label background.

Value Type: [System.Drawing.Color]
Default: (Value of BackColor property of the label.)

To set the BackColor property in a script:

If ($Label1.BackColor -eq 'Control')
{
    $Label1.BackColor = [System.Drawing.Color]::ControlDark
}

-or-

$Label1.BackColor = "ControlDark"

image

TextAlign property: Determines the position of the text in the label

Use the TextAlign property to get, set, or change the alignment of the text relative to the label border.

Value Type: [System.Drawing.ContentAlignment] enumeration
Values: TopLeft, TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottomCenter, BottomRight
Default: MiddleCenter
To set the TextAlign property in the Property pane:

image
To set the TextAlign property in a script:

$label1.TextAlign = 'TopRight'

BorderStyle property: Determines the style and thickness of the label border.

Use the BorderStyle property to get, set, or change the label border. Label borders are fixed. The end-user cannot resize any label border style.

Value Type: [System.Windows.Forms.BorderStyle] enumerator.
Values: None, FixedSingle, Fixed3D
Default: None

$label1.BorderStyle = 'None'

-or-

$label1.BorderStyle = [System.Windows.Forms.BorderStyle]'None'

image

$label1.BorderStyle = 'FixedSingle'

image

$label1.BorderStyle = 'Fixed3D'

image

Image property: Determines that image that appears on the label

Use this property to display an icon, bitmap, or image of any type on the label.

Value Type: [System.Drawing.Image]
Default: (None; Same as Form.BackgroundImage)

To add an image from a file to a label, use Image property in the Property pane. Click the ellipsis and, in File Explorer, select a file. PowerShell Studio converts the file into a bitmap string (64-bit byte array) so it is independent of the local system.

image

On the label:

image

Tip: If you are using a label to display an image without text, consider using a PictureBox object.

Note: To add an image to a label, PowerShell Studio converts the image to a 64-bit string and then uses the FromBase64String static method to pass a byte array as the System.Drawing.Image type. It does not pass a path or use any local system values.

Enabled property: Determines whether the label can be clicked

Use the Enabled property to temporarily enable or disable the click feature of the label. For example, use Enabled = $false to disable the click feature after the label has been clicked or while a function is running.

Value Type: [System.Boolean] ($True, $False).
Default: $True

To completely disable the click feature of the label, omit the Click event handler or enter an empty script block.

To set the Enabled property in the script:

$Label1.Enabled = $false

For example:

If ($Label1.Text -eq 'Click')
{
    $Label1.Text -eq 'Clicked'
    $Label1.Enabled = $false
}

Click event: Determines how the label responds when a user clicks it.

The event handler for the Click event is called when the label is clicked. Use the Click property to trigger a script block when a user clicks on the button.

$label1_Click={
    $label1.Enabled = $false
    $label1.Text = "Disabled"
}

Before Click event:

Label_Click_Before

After Click event:

Label_Click_After

 

Note about events:

To discover which control called the event script block, use the $this automatic variable. The $this variable is particularly useful when you have multiple controls calling the same event script block. The $this variable tells which control triggered the event, so you can respond in a control-specific way.

For example, this Click event script block disables the control that called it:

$Disable_Click={
    $this.Enabled = $false
}

Looking for more Spotlight posts? In PowerShell Studio, in the Toolbox or in the Properties pane, right-click a control and then click ‘View Spotlight Article’.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Output from a Script in an Executable File

$
0
0

This is the fourth blog in a multi-part series about designing a Windows PowerShell script that will be packaged in an executable file.

When you write scripts in Windows PowerShell, you really don’t think much about the format of the output. Your script generates objects and they appear in the console. You learn quickly that you should never use any formatting commands on your output, because the format cmdlets return format objects, instead of the original objects. Also, the user is the best judge of the format they prefer and can easily format any objects your return.

But, all of those rules change when you package a script in an executable file. That’s because scripts in executable files always return strings — and only strings.

General Rule: Don’t format output objects

Let’s begin with the general rule. When a script generates output, don’t format it. Let the user do it. Otherwise, you return format objects that don’t have the properties and methods of the original objects.

For example, the Get-MyProcess.ps1 script returns the Process object (System.Diagnostics.Process) for the PowerShell Studio process.

Param
(
    [Parameter()]
    [String]
    [ValidateNotNullOrEmpty]
    $List
)
 
if ($p = <b>Get-Process</b> 'PowerShell Studio')
{
    if ($List)
    {
        $p | <b>Format-List</b> -Property *
    }
    else {$p}
}
PS C:\> .\Get-MyProcess.ps1 | Get-Member

   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
NPM                        AliasProperty  NPM = NonpagedSystemMemorySize64
PM                         AliasProperty  PM = PagedMemorySize64
SI                         AliasProperty  SI = SessionId
...

But, if I use the List parameter, which tells the script to run the Format-List cmdlet, the output object is a Format object, not a Process object.

PS C:\> .\Get-MyProcess.ps1 -List True | Get-Member

   TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

Name                                    MemberType Definition
----                                    ---------- ----------
Equals                                  Method     bool Equals(System.Object obj
GetHashCode                             Method     int GetHashCode()
GetType                                 Method     type GetType()
ToString                                Method     string ToString()
autosizeInfo                            Property   Microsoft.PowerShell.Commands
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e
groupingEntry                           Property   Microsoft.PowerShell.Commands
pageFooterEntry                         Property   Microsoft.PowerShell.Commands
pageHeaderEntry                         Property   Microsoft.PowerShell.Commands
shapeInfo                               Property   Microsoft.PowerShell.Commands


   TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData

Name                                    MemberType Definition
----                                    ---------- ----------
Equals                                  Method     bool Equals(System.Object obj
GetHashCode                             Method     int GetHashCode()
GetType                                 Method     type GetType()
ToString                                Method     string ToString()
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property   string ClassId2e4f51ef21dd47e
groupingEntry                           Property   Microsoft.PowerShell.Commands
shapeInfo                               Property   Microsoft.PowerShell.Commands...

 

So, you never return formatted objects. Well, almost never.

Executable Files Return Strings — Always

Now, the exception to the do-not-format rule. When you package your script in an executable file, it always returns strings objects, regardless of the underlying object type, because it writes to stdout — standard output — which is always text.

The executable runtime uses the Out-String cmdlet, which does a pretty good job of preserving the appearance of the output, but the output loses the properties and methods of the original objects.

PS C:\> .\Get-MyProcess.exe #Looks the same...

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName
-------  ------    -----      ----- -----   ------     --  -- -----------
   2896     178   266168     315796  1170   926.55   4428   1 PowerShell Studio

PS C:\> .\Get-MyProcess.exe | Get-Member #Just a good-looking string.

   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone(),
CompareTo        Method                int CompareTo(System.O
Contains         Method                bool Contains(string v
CopyTo           Method                void CopyTo(int source
EndsWith         Method                bool EndsWith(string v
Equals           Method                bool Equals(System.Obj
GetEnumerator    Method                System.CharEnumerator

In fact, in this case, it’s six good-looking strings, including blank lines.

PS C:\> (.\Get-MyProcess.exe).count
6

PS C:\> (.\Get-MyProcess.exe)[0]

PS C:\> (.\Get-MyProcess.exe)[1]
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName

If you run the Get-MyProcess.ps1 script, you can get the property values from the object, or call its methods, but the string does not have the Process object properties or methods.

PS C:\> (.\Get-MyProcess.ps1).PeakWorkingSet
359100416

PS C:\> (.\Get-MyProcess.exe).PeakWorkingSet
PS C:\>

And the PowerShell options to format the properties and values into tables and lists have no apparent effect on strings.

PS C:\> .\Get-MyProcess.exe | Format-Table

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName
-------  ------    -----      ----- -----   ------     --  -- -----------
   3298     178   266168     315796  1170   926.61   4428   1 PowerShell Studio

PS C:\> .\Get-MyProcess.exe | Format-List -Property *

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName
-------  ------    -----      ----- -----   ------     --  -- -----------
   3298     178   266168     315796  1170   926.61   4428   1 PowerShell Studio

PS C:\> .\Get-MyProcess.exe | Format-List -Property * -Force
Length : 0
Length : 156
Length : 156
Length : 156
Length : 0
Length : 0

New Exe Rule: Add a Format Option

When you’re writing scripts that are designed to be packaged in executable files, you might consider adding formatting options.

For example, the little Get-MyProcess.ps1 script (and its Get-MyProcess.exe variant) has a List parameter that runs the Format-List cmdlet and returns all properties and values of the object in a list.

The output of the executable file is still a string — it’s always a string — but now the user can see the properties and values.

PS C:\> .\Get-MyProcess.exe -List True

Name                       : PowerShell Studio
Id                         : 4428
PriorityClass              : Normal
FileVersion                : 4.2.98.0
HandleCount                : 3562
WorkingSet                 : 323387392
PagedMemorySize            : 272556032
PrivateMemorySize          : 272556032
VirtualMemorySize          : 1226399744
TotalProcessorTime         : 00:15:26.7187500
SI                         : 1
Handles                    : 3562
VM                         : 1226399744
WS                         : 323387392
PM                         : 272556032
NPM                        : 182704
...

Save the output as XML or JSON

[Thanks to Joel Bennett (@Jaykul) and Rob Campbell (@mjolinor) for the idea.]

You can also add an option to save the output object in an XML or JSON file, instead of return it as a string. Then, the user can convert the contents of the file to a more usable object type and include it in subsequent PowerShell commands.

In this version of the script, I’ve added two new parameters, XMLFilePath and JSONFilePath. The parameters take the path to an XML or JSON file. I don’t validate the input, because the output cmdlets return reasonably helpful error messages.

Because the List, XMLFilePath, and JSONFilePath parameters are exclusive, I put each in its own parameter set.

To preserve the default (no parameter) option, I made the ListSet parameter set the default and made its List parameter optional. When all parameters are strings, as in an exe, you must specify a default parameter set, because the parameter binding logic in Windows PowerShell cannot use types to figure out which parameter set you want.

(No, I did not memorize the syntax for all of this. I used the Function Builder. Prefer a video?).

Param
(
        [CmdletBinding(DefaultParameterSetName = 'ListSet')]
        [Parameter(ParameterSetName = 'ListSet')]
	[ValidateNotNullOrEmpty()]
	[String]
	$List,
 
	[Parameter(ParameterSetName = 'XMLSet',
			   Mandatory = $true)]
	[String]
	$XMLFilePath,
 
	[Parameter(ParameterSetName = 'JSONSet',
			   Mandatory = $true)]
	[String]
	$JSONFilePath
)

The code is pretty simple. I used an IF/ELSEIF sequence to check the parameters and write them out as XML or JSON.

Because neither Export-Clixml nor Out-File (nor Set-Content) return any data, I always use Get-Item to return the new file to the user. If they don’t need it, they can always pipe it to Out-Null, but I bet it saves users an extra command to check the file.

if ($p = Get-Process 'PowerShell Studio')
{
	if ($List)
	{
		$p | Format-List -Property *
	}
	elseif ($XMLFilePath)
	{
		$p | Export-Clixml -Path $XMLFilePath
		Get-Item $XMLFilePath
	}
	elseif ($JSONFilePath)
	{
		$p | ConvertTo-Json | Out-File $JSONFilePath
		Get-Item $JSONFilePath
	}
	else { $p }
}

Now, the user can run Get-MyProcess.exe, save the output in a file, import the output as a deserialized XML object (Import-Clixml) or convert it to a PSCustomObject (ConvertFrom-Json), and use it as needed.

PS C:\>.\Get-MyProcess.exe -XMLFilePath .\Scripts\SPSProcess.xml

    Directory: C:\Scripts

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       12/17/2015   2:33 PM         634284 SPSProcess.xml

PS C:\> $p = Import-Clixml -Path .\Scripts\SPSProcess.xml
PS C:\> $p

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id  SI ProcessName
-------  ------    -----      ----- -----   ------     --  -- -----------
   1506     176   287812     283776  1212    88.84   6776   1 PowerShell Studio


PS C:\> $p.Handles
1506

PS C:\> $p | Format-List *

Name                       : PowerShell Studio
SI                         : 1
Handles                    : 1506
VM                         : 1271144448
WS                         : 290586624
PM                         : 294719488
NPM                        : 179984
...

You can also use string-parsing techniques and cmdlets like Convert-String and Convert-FromString to convert the string output to a custom object.

Wrapping scripts in executable files is valuable, but remember to help the user with the string output.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.Parsing Parameters for a Script in an Executable File

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Where are v3 and v4? PowerShell Versions in PowerShell Studio

$
0
0

In this post, we explain why PowerShell Studio can run your scripts only the versions of Windows PowerShell that are installed on local and remote computers.


If you’ve recently upgraded to Windows 10 from Windows 7, 8, or 8.1, you’ll notice quickly that you now have Windows PowerShell 5.0. Depending on your upgrade path and subsequent updates, you might be running any version of Windows PowerShell greater than or equal to 5.0.10240.

clip_image001

Or, you might have installed the shiny new Windows Management Framework 5.0 for Windows 7 SP1, Windows Server 2008 R2, and later versions of Windows client and server.

In either case, when you start PowerShell Studio or PrimalScript, the platform options are now V5 and V2. On a 64-bit machine, you have V5 and V2 options for 32-bit and 64-bit platforms.

clip_image003

What happened to Windows PowerShell v3 and v4?

Well, they’re gone.

When you install a new version of Windows PowerShell, the previous version is automatically uninstalled and is no longer available on that computer.

Can I install multiple versions of PowerShell?

No.

If you try to install an older or newer version of Windows PowerShell, you might be able to replace the current version, but you cannot add. You cannot install any two versions of Windows PowerShell side-by-side or in sequence on the same computer. One PowerShell.exe always overwrites the previous one.

Version 2.0 appears to be an exception to the one-version rule, but it’s really not. Instead, Version 2.0 is included in a special mode in every subsequent version. So, there’s still just one PowerShell.exe. Unfortunately, this special mode is designed for and works only on Version 2.0, because the backward compatibility requirement covers only Windows PowerShell 2.0, not any later versions.

Can PowerShell Studio install new versions of PowerShell?

No. (We’ve tried!)

Because we all need to test our scripts and modules on all versions of Windows, we have tried mightly to find some way to install multiple versions of Windows PowerShell on a single system. We’ve tried all sorts of tricks to package it or isolate it or hide it or squeeze it in. However, Windows and the Windows PowerShell installers prevent it.

Instead, we’ve worked very hard to make it easy for you to run and test your scripts on remote computers, including those running different versions of Windows PowerShell.

Which versions of Windows PowerShell do I have?

If you have Windows PowerShell 2.0, you have only 2.0. If you have a later version of Windows PowerShell, you have your version of Windows PowerShell plus 2.0.

clip_image005

  • To find all installed versions of Windows PowerShell, run PowerShell.exe with its Version parameter. PowerShell.exe starts a Windows PowerShell session.The Version parameter starts the specified version of Windows PowerShell in the new session. It behaves like a MinimumVersion parameter. If you don’t have the specified version, it starts the version that is greater than or equal to the specified version.

    For example, to get the earliest installed version, I start by specifying version 1. The results show that my earliest version is 2.0. Then, to find the next version, I specify version 3. The result show that my next version is 5.0, so I have 2.0 and 5.0.

    clip_image007

How do I test on multiple versions of Windows PowerShell?

This is the right question and, fortunately, we have some good answers.

It’s critical to test your scripts and modules on all of versions of Windows PowerShell that you support. Be sure to include a #Requires -Version statement on all shared scripts and modules. This statement makes your supported version intent clear to users.

Here are several common strategies:

    • Remote and virtual computers. To test on multiple versions of Windows PowerShell, install different PowerShell versions on your local computer, remote computers, and/or virtual machines.For example, I’m running Windows PowerShell 5.0 on my local computer, but I have a Windows Server 2012 R2 virtual machine that runs Windows PowerShell 4.0 and Windows 7 virtual machine that runs Windows PowerShell 3.0.

      In PowerShell Studio, to run a script on a remote or virtual machine, click Home and, in the Run section, click Remote (Ctrl+R). Enter your remote credentials. PowerShell Studio saves the credentials (even if you close the script or close PowerShell Studio), so the next time you want to run a script on that remote machine, just click Remote or Ctrl+R.Remote

      You can debug a script on a remote computer or VM. Click Home and, in the Run section, click the arrow beside Remote, and then click Debug Remotely (Ctrl + B).

      clip_image010

      You can also import cached information about a remote computer or virtual machine into PowerShell Studio. Then, it’s even easier to run on the selected computers. For instructions, see Developing PowerShell scripts for other machines (Part 1) and Developing PowerShell scripts for other machines (Part 2).

      clip_image011

    • Continuous integration testing. You can set up a series of servers that are designed to test your scripts and modules automatically on a variety of platforms. There are several popular tools to help get you started, including TeamCity and Appveyor.
    • Community Build Server. A terrific new resource became available to the PowerShell community in July 2015. The PowerShell.org Community Build Server automatically tests your scripts and modules on all supported versions of Windows PowerShell (v2 and greater) any time that you check a new version into a Git repository or accept a pull request. This great resource is available for any project that uses an open-source license. For instructions, see Community Build Server.

 

So, you cannot install more than version of Windows PowerShell on a single computer, but there are still several alternatives to help you run tests on multiple versions.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

How I learned to love DialogResult

$
0
0

This post discusses the DialogResult property of buttons, which often confuses beginning PowerShell GUI app designers.


When I first started learning how to write PowerShell GUI applications, I thought of the DialogResult property of buttons as the “evil property.”

image

What else would I think? If the DialogResult property of a button is set to any value other than None, when you click the button, the GUI app closes immediately after running the commands in my event handler.

In this little app, when you click OK, it pipes a Get-Service command to Out-GridView — um, very briefly.

 

After banging my head against the wall (and then searching the SAPIEN blog/forums), I realized that my app closes immediately because the DialogResult property of the OK button is set to OK.

Screenshot 2015-12-21 16.47.06

It also closes immediately when the DialogResult value is Cancel, Abort, Retry, Ignore, Yes, or No. When the value of the DialogResult property of a button is None, the app works perfectly.

So DialogResult is evil, right?

The First Value of DialogResult

I’m new to GUI apps, but the science and art of user experience and computer-based user interfaces is decades old. When I design my PowerShell GUI apps, I’m standing on the shoulders of people who’ve studied user experience as a discipline. I want to learn from them.

And, they’ve learned that uniformity and predictability make buttons usable. If my OK button does the same thing as your OK button, and almost every other OK button, our users learn how the OK button works. If, instead, my OK button opens a box and your OK button finds a file, the user needs more guidance before clicking OK.

You might rely on each developer to do the right thing. It’s easy enough to write a Click event handler for the button that closes the form.

$buttonOK_Click={
    form1.Close()
}

But, it’s much easier to standardize button behavior. That’s what DialogResult tries to do.

$buttonOK.DialogResult = OK

And, if the behavior of OK buttons changed, the .NET folks would only have to change what the OK value does. Developers worldwide wouldn’t have to rewrite the Click event handlers of their buttons.

It reminds me of all of the things we do in PowerShell to create a more predictable experience, including using approved verbs and common parameters. It might frustrate developers at times, but it’s really much better for users.

The Second Value of DialogResult

Actually, the DialogResult values do a lot more than just close the form. Their primary job is to tell a parent form which user action closed its child form.

Here’s the model. You start with a parent form (Main Form). When you click a button (like “Open child form”) on the parent form, the child form opens. When you close the child form, the system returns the DialogResult value of the closing action to the parent form.

(Want to learn how to create a parent and child form? Passing and returning values using forms)

clip_image006

In this example, the text on each the buttons matches its DialogResult value. For example, the Retry button has a DialogResult value of ‘Retry’.

clip_image008

When the user clicks the ‘Retry’ button, the system returns a value of ‘Retry’ to the parent form. The parent form can use the DialogResult value.

For example, here is the Click event handler for the Open child form button. The Call-ChildForm_psf function opens the child form. When the child form closes, the system returns the DialogResult value as output. You can ignore the DialogResult value, or you can capture the DialogResult value and use it. For example, this event handler assigns the DialogResult value to the Text property of the RichTextbox, so the value is displayed in the rich textbox.

$buttonOpenChildForm_Click = {
    $richtextbox1.Text = Call-ChildForm_psf
}

Now, when you click a button that closes the child form, the DialogResult value appears in the rich textbox.

 

There are many practical uses of this functionality. For example, this the app reacts differently to each DialogResult value. The button’s Click event handler opens the child form (Call-ChildForm_psf) and saves the DialogResult in the $dr variable. Then, it uses a Switch statement to respond to the DialogResult value in the $dr variable.

$buttonOpenChildForm_Click = {
    do
    {
        $dr = Call-ChildForm_psf
        $retry = $false
 
        switch ($dr)
        {
            OK { $richtextbox1.ForeColor = 'Green'; break; }
            Cancel { $richtextbox1.ForeColor = 'Red'; break; }
            No { $formMain.Close() }
            Retry { $richtextbox1.ForeColor = 'Red'; $retry = $true; break; }
            default { $richtextbox1.ForeColor = 'Black'; break; }
        }
        $richtextbox1.Text = $dr
    }
    while ($retry -eq $true)
}

Here’s the result:

DialogResult in a Message Box

You might not work with parent and child forms, but you can use DialogResult in a message box, too. The MessageBox control is a child of its parent form.

I often use message boxes to display errors to users. For example, in the Click event handler for a Start button, I run a Get-Service command, but if it fails, I use a Try-Catch block to catch errors and display them in a message box. To create the message box, I use the msgbox preset snippet in PowerShell Studio.

$buttonStart = {
    Try {
        Get-Service -ComputerName $textboxCn.Text.Trim() | Out-GridView
    }
    Catch {
        [void][System.Windows.Forms.MessageBox]::Show('Cannot get services from the specified computer', 'Computer name error')
    }
}

Notice that the output type on the MessageBox snippet is [void], which means $null or “nothing.” It’s like piping the output of a command to the Out-Null cmdlet. But, if you don’t void out the output, the system returns the DialogResult value of the control that closed the message box.

In this code, I save the DialogResult output in the $dr variable. Also, I selected an argument list (“overload”) for the Show method that creates a message box with Yes and No buttons that return a DialogResult of Yes and No, respectively. In the Catch block, the error message asks the user if they want to get services on the local machine. If they say ‘Yes’, it runs a Get-Service command locally. Otherwise, it just displays the parent form again.

$buttonStart = {
    Try {
        Get-Service -ComputerName $textboxCn.Text.Trim() | Out-GridView
    }
    Catch {
        $dr = [System.Windows.Forms.MessageBox]::Show('Failed. Get services on local computer?', 'Computer name error', 'YesNo')
        if ($dr -eq 'Yes') { Get-Service | Out-GridView }
    }
}

This is just one of many possible uses of DialogResult in a message box.

Fixing DialogResult Errors: Add a button

Most PowerShell Studio users first encounter the DialogResult property when they use the Dialog Style Template.

The Dialog Style Template is awesome because it creates a fixed-size (not resizable) GUI app with no minimize or maximize buttons, so you don’t have to worry about controls floating around on your form when the end-user resizes the box.

clip_image010

But, because it follows best-practice standards, the OK button in the Dialog Style Template has a DialogResult value of OK.

clip_image011

If you try to re-purpose the OK button by writing an event handler for it, it runs the event handler and then closes immediately — and rather abruptly.

$buttonOK_Click={
    Get-Service | Out-GridView
}

To fix the problem, you can set the DialogResult property of the OK button to None. That fixes your app, but it breaks the best practice expectation of how OK buttons work.

Instead, add a button that doesn’t have an expected purpose (not one of the DialogResult values), like this Start button, and set its DialogResult property to None.

clip_image012

Then, move the feature code to the Click event handler for the Start button. To make sure that I don’t misuse the OK button, I add a comment reminding me about the DialogResult value.

$buttonOK_Click={
    #DialogResult = OK
}
 
$buttonStart_Click={
    Get-Service | Out-GridView
}

 

Now that I understand DialogResult, I realize that it’s not really evil. It’s helps to enforce best practice standards and I can even use productively. I can’t quite say I love it, but I certainly have a crush on it.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

PowerShell GUI Apps: Why think in events?

$
0
0

Always committed to starting the New Year right, I’m heading to Scottsdale, Arizona on Tuesday, January 5 at 5:30 PM to meet with the newly revived Arizona PowerShell User Group. We’ll meet at the Microsoft Store in Scottsdale Fashion Square. (I will not be distracted by the Surface Pro 4 on display. I will not…)

clip_image002

 

I’ll be there to lead an interactive hands-on lab in which participants build a basic PowerShell GUI application – essentially, a graphic user interface for a PowerShell script. When you press a button, it runs part of the script. When you type text in a textbox, the script can get the text and use it, such as for parameter values.

clip_image003

 

GUI apps like this one are not a replacement for Windows PowerShell scripts, but they’re a great strategy for delegated administration, which is a fancy way to describe a scenario in which someone who doesn’t know PowerShell needs to run a script. It’s also great for creating dashboards of scripts and automating PowerShell features.

The hands-on lab is fun because everyone participates and learns from each other. And, it’s truly a learning experience, not just a listening fest, because everyone is building something. We hit the first-timer bugs and tackle them right there, so you don’t encounter them when you’re alone at work under deadline pressure.

 

Thinking in Events? Why?

This lab is about PowerShell scripts, graphic user interfaces, and apps, so why is it called “Thinking in Events?”

        A Windows PowerShell script for a GUI application consists of an unordered collection of event handlers.

Yes, you read that correctly. If it sounds strange to you, you’re not alone. This is not the Windows PowerShell you learned.

And, there’s more.

        The order in which the events handlers run is determined by the end-user.

If you work with end-users, you can now add “scary” to “strange.” PowerShell scripts for GUI applications do not run in the predetermined linear way that standard scripts run.

When you write a GUI application in an object-oriented language, like Windows PowerShell, the elements of the user interface — the background form, the textboxes, the buttons, and the labels — are objects, just like processes, services, strings, and AD users.

These objects have properties and methods (and we use them), but they also have a member type called an event. Events are triggered by end-user actions. When an event happens, the system looks for instructions to tell it how to respond to the event. These instructions are called event handlers and you can write them in Windows PowerShell. To associate an event with an event handler, you register the event.

clip_image005

For example, my GUI app has a Close button (a button labeled “Close.”) When you click the Close button, the Click event of the Close button happens or “is raised” and the system looks for an event handler for the Click event of the Close button. If you’ve written a script and registered it as the event handler for the Click event of the Close button, whenever the user clicks the Close button, the system runs your event handler script.

New way of thinking: Non-linear event scripting

It’s a bit odd. We’re building a graphic user interface front-end for a Windows PowerShell script. But, the most novel part is not the form, buttons, or text boxes — they’re just objects. Instead, the most novel part is the way you think about it.

A GUI application typically contains multiple event handlers. At a minimum, there are click events for all of the buttons, text-changed events for the text boxes, and a Load (startup) event for the application form. You might also have events for menus and icons, like the one that displays help.

So, while designing your events, you have to decide which part of the code goes in which event handler, also known as: “Where do I paste this code I found on Google?” Not your typical PowerShell script.

Also, when you write a standard Windows PowerShell script, you determine the order in which the statements in the script run. The script might include some functions, or some IF or SWITCH statements that create a branch, but the script author still determines the running order.

In GUI app development, regardless of the language or the technology you select, you have to thinking in terms of end-user-initiated events.

Because end-users can and will type in and click anything you give them, you need to use an entirely different strategy to assure that you have the information you require before you run a cmdlet in your script. Otherwise, your script will generate preventable errors.

 

Displaying Output and Errors

The worst thing about an error in a GUI application is that the user can’t see it unless you create a display for it. In fact, the user cannot see any output unless you create a display for it.

In a typical Windows PowerShell script, the errors and other output, including verbose, warning, and information messages, appear the console. The script author doesn’t need to think about it.

However, in a GUI application, there is no console. So, if you don’t create a display for output and errors, users don’t see them at all. You can imagine the consequences.

 

So, that’s why I call this PowerShell GUI lab “Thinking in Events.” You’ll write a PowerShell script, build a GUI app to run it, and then package your script and app in an executable file for distribution. But, the most intriguing part of the exercise is learning to think in a whole new way.

June Blender is a technology evangelist at SAPIEN Technologies, Inc.and a Windows PowerShell MVP. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

iPowerShell Pro adds support for iOS 9 Features

$
0
0

SAPIEN has just released another update to iPowerShell Pro, its iOS-based PowerShell editor and remote connectivity tool. With this update, we focussed primarily on iOS 9 compatibility.

iPad Multi-Tasking

iPowerShell Pro is now a good citizen in the iPad multi-tasking community. If you’re using an iPad running iOS 9, you can split the screen and use two apps at the same time. This can take several forms depending on your iPad version.

Slide over – Slide Over lets you interact with a second app without leaving the app you’re currently using. To see your previously-used apps, swipe from the right edge of the screen to the left. The primary app on the left side is grayed out and is not active. Slide Over works with  iPad Pro, iPad Air, iPad Air 2, and iPad mini 2 or later.

iPowerShell Pro in Slide over

Figure 1 – iPowerShell Pro as secondary app in slide over

iPowerShell Pro as primary app in slide over

Figure 2 – iPowerShell Pro as primary app in slide over

 

 

 

 

 

 

 

 

Split View – With Split View, you can open and use two apps at the same time. Open the first app. Then, to use both apps, tap or drag the app divider. Split View isn’t available for all apps, but iPowerShell Pro now fully supports it. You can use Split View in a 1/2 split, as seen in Figure 3, or a 2/3 split, as seen in Figure 4. Split View is available on iPad Pro, iPad Air 2 or iPad mini 4.

iPowerShell Pro as secondary app in split view

Figure 3 – iPowerShell Pro as secondary app in split view

iPowerShell Pro as primary app in split view

Figure 4 – iPowerShell Pro as primary app in split view

 

 

 

 

 

 

 

 

Picture in Picture – You can watch videos in iPowerShell Pro in the Picture in Picture view. For example, using picture in picture in the iPowerShellPro Videos section, you can watch a training video while browsing through cmdlet descriptions. You can use Picture in Picture with iPad Pro, iPad Air, iPad Air 2, and iPad mini 2 or later.

Watch videos and browse cmdlet descriptions

Figure 5 – Watch videos and browse cmdlet descriptions

 

Better Display of content

We have upped our game when displaying cmdlet help, About help, and Alias and Provider Help content. Nicer fonts and presentation for code snippets and text, all of which are responsive to the system font sizing, make reading the help files much easier.

Better content display

Figure 6 – Better content display

 

Home Screen Quick Access

For users of iPhone 6s and 6s Plus, we have implemented Home Screen Quick Access. When you press lightly on the iPowerShell Pro home screen icon, a popup menu appears with shortcuts to Cmdlets, Aliases, About-Help and Providers. To start iPowerShell Pro Quick Access mode automatically, just tap on an item in the popup menu.

Home Screen Quick Access

Figure 7 – Home Screen Quick Access

 

Peeking and Popping

Also, for iPhone 6s and 6s Plus users, we have added peeking and popping for cmdlets, aliases, About Help and providers. To highlight a selection, press lightly on any list, as seen in Figure 8. A bit more pressure displays the description in a temporary (it disappears when you release the pressure) popup window (Figure 9).  Adding more pressure to the screen makes the description POP to full screen (Figure 10) and remain displayed even when you remove your finger. This is a very convenient feature to peek into a cmdlet to determine if that is the one you need and then pop it to full screen to read the full description.

Peeking and Popping - Slight pressure

Figure 8 – Peeking and Popping – Light Pressure

Peeking and Popping - Medium Pressure

Figure 9 – Peeking and Popping – Medium Pressure

IMG_1163

Figure 10 – Peeking and Popping – Full Pressure

 

 

 

 

 

 

 

 

 

 

 

Better Security

iOS 9 implements App Transport Security (ATS) which requires that all web-based communication be implemented via HTTPS rather than HTTP. This release of iPowerShell Pro implements ATS for all endpoints that we can control. As third party endpoints change over to HTTPS, we will extend the implementation to include those locations.

 

Spotlight Search Support

We have added NSUserActivity indexing to cmdlets, about_help and providers. When you view any item in these lists, that full content is now added to the on-device search index so you can easily use Spotlight to search for PowerShell information and return to previously viewed content.

Additionally, whenever you download your data from our servers, we immediately index all of the cmdlet, about_help and provider topic names so that you can search for a cmdlet name using Spotlight and then display that content with a single tap.

Spotlight Search

Spotlight Search

We have also added support for Universal linking to the application. When we finish adding all of the cmdlet, about_help and provider information to our website, doing a web search for that information will provide a Universal Link to view the information in iPowerShell Pro.

 

Bug fixes and performance improvements

Of course, we have squashed a few bugs and improved the code in various places to continue to give the user a reliable and performant product. Make sure to download the free update to the new version of iPowerShell Pro (5.0.135). If you have never used iPowerShell Pro, its only $4.99 and its available on the App Store!

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  


Running PowerShell Scripts: Survey Results

$
0
0

We’re always evaluating the best, easiest, and most efficient ways to write, run, and publish Windows Powershell scripts and modules. But, our starting point is always how people work now.

We were curious about how PowerShell scripters, including managers, ran scripts, and how their employees ran scripts written for them.

Screenshot 2016-02-06 16.42.52

 

To gather this information, SAPIEN Technologies, Inc. created an online survey about the ways in which users run PowerShell scripts. We encouraged users to participate in a blog post and several posts on Facebook and Twitter. And, people really responded. We collected 88 responses between 12/16/2015 and 1/05/2016.

 

Here are our major findings:

  • More than 2/3 (65%) of respondents run scripts in the console. This was not correlated with # machines managed or whether the user managed other people.
  • 23% (21) respondents run scripts in Task Scheduler. That’s not much less than those who run scripts in their editor 37.5% (33) (“ISE”, “PowerShell Studio”, “PowerGUI”).
  • When employees (delegates) run scripts, they’re far more likely to run them in an .EXE or GUI, or from File Explorer. Console drops to half.
  • For an ideal script-running environment for employees, 14.4% (11) respondents mentioned web pages or web portals, even though no respondents said they used one. Other popular answers were GUI, console, and executable files, far in excess of those who actually used them.
  • About 25% of scripts are run as scheduled tasks. Some (10 each) respondents never or almost always ran scripts as scheduled tasks.
  • 2/3 (47/75) of respondents never run scripts signed. 12% (9/75) always do.
  • 77% (41/53) respondents use the RemoteSigned execution policy.
  • Almost half (47%) of users use logs to evaluate their script results. 42% stare at the console. One person waits for people to file issues on GitHub.

 

How do you run scripts?

A few things surprised me. For scripts that I use all the time, I run them from the console. But if the script does anything complex, or runs infrequently, I almost always open it in PowerShell Studio, review how it works, and then run it from PowerShell Studio.

Well, I’m not alone. In fact, 20% of users described the same “console+editor” pattern. 22% use the console exclusively and 15% use an editor exclusively.

Also, none of the answers was correlated with the size of the enterprise or number of servers/workstations managed, except that larger enterprises used homegrown workbenches or services, like Kaseya and LabTech.

A handful of respondents mentioned SCCM, SMA, Azure Automation, and Orchestrator, but they totaled only 7%.

Task Scheduler

I was also surprised 17% of respondents run scripts solely or in part in Task Scheduler. That might be because their scripts are scheduled as tasks, or they are using Task Scheduler to automatically run a script with different permissions.

How do your employees run scripts?

As you would expect, the answers changed when we asked about how managers have employees run scripts. And, many respondents were careful to distinguish between employees who were proficient in PowerShell and those who just followed instructions.

The number of people running from the console (alone or in combination) dropped from 65% to 50%. Task Scheduler dropped from 24% to 8%.

19% of respondents provide some sort of GUI interface for their employees to run scripts, 16% have them run from File Explorer, and 13% package their scripts in executable files for employees to run.

Ideal for operators running scripts

We also asked about ideal environments for delegated script execution.

GUIs were the most popular answer, increasing from 19% (actual) to 30% (ideal). But the second choice was a web page or portal. About 14% of respondents thought that was ideal, although only one respondent reported actually using one.

22% stuck with the console. One respondent explained that, in their opinion, anyone who ran scripts should know at least enough PowerShell to run them with the correct parameters and values.

Runner-ups were executable files (13%), File Explorer (6.5%), and Task Scheduler (5%).

 

Thanks to everyone who participated in the survey. If you’re interested in more information from the survey, let me know. And, if you have comments about the results, please post them here.

June Blender is a technology evangelist at SAPIEN Technologies, Inc.and a Windows PowerShell MVP. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Using Group-Object to Detect Command Name Conflicts

$
0
0

The Group-Object cmdlet, one of the original provider cmdlets, is as old as Windows PowerShell. It was introduced in version 1.0 and hasn’t changed at all since then. But, it is one of my favorites. (You can tell when I love a cmdlet by the number of examples. Group-Object has 9!) In fact, when you use it frequently, you begin to see groups as a path to many solutions.

Group-Object groups objects by the values of a property that you choose. So, it’s a quick way to find the property values that appear in a data set. Which domain controllers are used by users in this department? What command types are exported by this module? It’s also a great way to detect duplicate and unique values. (Really, try a few of the 9 examples.)

In this post, I’ll be using an old cmdlet to help with a new problem — detecting multiple commands with the same name on the same machine.  Because providers and repositories offer more modules, we can install module versions side-by-side on 5.0, and we can install them in a blink without much consideration, name collisions are more likely than ever.


Detect commands with the same name

To detect commands with the same names in your installed modules, use the Get-Command cmdlet to get all commands in all installed modules. Then, use the Group-Object cmdlet to group commands by the value of their Name property. The result is a collection of groups where each group contains commands with the same name.

Some groups have just one member. But, each group that Group-Object returns has a Count property that indicates the number of objects in the group. That makes it easy to use the Where-Object cmdlet to find groups that have more than one member.

$groups = Get-Command | Group-Object -Property Name | Where Count -gt 1

Let’s look at one of the multi-member groups. This is one for the Describe command in the Pester module. I use and study Pester pretty obsessively, so I have multiple versions of the Pester module on my test machine.

PS C:\> $groups | where Name -eq 'Describe'

Count Name                      Group
----- ----                      -----
    6 Describe                  {Describe, Describe, Describe, Describe...}

This group has 6 members, that is, 6 Describe commands.

Each group that Group-Object returns also has a Group property that contains the members of the group. In this case, the Group property of the Describe group contains the Describe commands.

PS C:\> ($groups | where Name -eq 'Describe').Group

CommandType     Name             Version    Source
-----------     ----             -------    ------
Function        Describe         3.3.13     Pester
Function        Describe         3.3.11     Pester
Function        Describe         3.3.9      Pester
Function        Describe         3.3.10     Pester
Function        Describe         3.3.8      Pester
Function        Describe         3.3.5      Pester

The Describe duplicates represent the same command in different versions of the same module. But, we also find commands with the same name in different modules, such as this Set-Clipboard command.

PS C:\> ($groups | where Name -eq 'Set-Clipboard').Group

CommandType     Name                 Version    Source
-----------     ----                 -------    ------
Cmdlet          Set-Clipboard        3.1.0.0    Microsoft.PowerShell.Management
Cmdlet          Set-Clipboard        3.2.1.0    Pscx

Detect commands with same name in different modules

To get commands with the same names in modules with different names, use Group-Object to create groups based on the Source (module name) property value of each command and look for groups with more than one unique Source value.

To count the number of sources that the second Group-Object command returns, I use the Length property that PowerShell adds to all objects, because the Count property is the number of objects in the group.

Get-Command | Group-Object -Property Name | where { $_.Count -GT 1 -and ($_.Group | Group-Object -Property Source).Length -gt 1 }

To display the groups, I use the Format-Table cmdlet with a calculated property for the Source value.

$diftModules = Get-Command | Group-Object -Property Name | where { $_.Count -GT 1 -and ($_.Group | Group-Object -Property Source).Length -gt 1 }
 
$diftModules | Format-Table -Property Name, @{Label = 'Module'; Expression = { $_.Group.Source } }

 

Which command runs?

Now, that we can find commands with the same name, I’d like to know which command runs by default when I invoke that command without a qualifying module or version. The answer is not some new-fangled cmdlet. It’s good old Get-Command.

When the value of the Name parameter includes wildcard characters (*), Get-Command gets all commands with the specified name. But, when the value of the Name parameter has no wildcards, Get-Command gets the command that runs by default; the one that takes precedence.

For example, using a wildcard character (*) in Name, you can see the two Set-Clipboard commands that Group-Object detected.

PS C:\> Get-Command Set-Clipboard*

CommandType     Name                       Version    Source
-----------     ----                       -------    ------
Cmdlet          Set-Clipboard              3.1.0.0    Microsoft.PowerShell.Management
Cmdlet          Set-Clipboard              3.2.1.0    Pscx

To find the one that runs by default, run Get-Command with no wildcard characters. It’s the newest version in the user-specific module directory.

PS C:\> Get-Command Set-Clipboard

CommandType     Name                       Version    Source
-----------     ----                       -------    ------
Cmdlet          Set-Clipboard              3.1.0.0    Microsoft.PowerShell.Management

This is very helpful. But, what do you do to make sure that you’re running the command that you want to run? Stay tuned for a series of posts on different strategies to avoid mistakes.

 

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at juneb@sapien.com and follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Using Prefixes to Prevent Command Name Collision

$
0
0

In January, I had the honor of presenting to the Mississippi PowerShell User Group (MSPSUG). I’ve known the organizers, Mike Robbins and Rohn Edwards for years, and truly respect them. The PSUG is online-only, which makes it a challenge for presenters, but they attract a very sophisticated audience, so my talks there are really conversations. This was a perfect venue for my “Avoiding Version Chaos” talk. (More at PowerShell Saturday in Tampa on March 19, 2016.)

Screenshot 2016-02-06 16.58.52

In one part of the talk, I demonstrated how to use noun prefixes to distinguish among commands with the same names. The demo flopped — we ended up with duplicate commands — so I’ll use this blog post to show how prefixes works and what went wrong.

TIP:   To detect commands with the same name on the same machine, Use Group-Object to Detect Command Name Conflicts.

 

Define Unique (Prefixed) Names

One way to prevent command name conflicts is to define command names that are likely to be unique.

For example, the cmdlets in the PowerForensics module are created with names that include “Forensic” so they’re likely to be unique. (Note that some commands in the module, like ConvertFrom-BinaryData, are not prefixed, because they are intended for a more general use.)

PS C:\> Get-Command -Module PowerForensics

ommandType     Name                                  Version    Source
-----------     ----                                 ------    ------
Cmdlet          ConvertFrom-BinaryData               1.1.1      PowerForensics
Cmdlet          ConvertTo-ForensicTimeline           1.1.1      PowerForensics
Cmdlet          Copy-ForensicFile                    1.1.1      PowerForensics
Cmdlet          Get-ForensicAlternateDataStream      1.1.1      PowerForensics
Cmdlet          Get-ForensicAmcache                  1.1.1      PowerForensics
Cmdlet          Get-ForensicAttrDef                  1.1.1      PowerForensics
Cmdlet          Get-ForensicBitmap                   1.1.1      PowerForensics
Cmdlet          Get-ForensicBootSector               1.1.1      PowerForensics
...

Add a Default Command Prefix

To prevent name conflicts, module authors can also create commands with more generic names and then specify a default command prefix in the module manifest (the .psd1 file of the module). Then, when the module is imported, Import-Module cmdlet prepends the default prefix to the nouns of all commands in the module.

To specify a default prefix, use the DefaultCommandPrefix key in the module manifest.

DefaultCommandPrefix =

To get modules with a default command prefix, look for a value in the Prefix property of the module.

PS C:\> Get-Module -ListAvailable | where Prefix

    Directory: C:\Users\JuneBlender\Documents\WindowsPowerShell\Modules

ModuleType Version    Name                    ExportedCommands
---------- -------    ----                    ----------------
Manifest   1.2.0.0    HardwareManagement      {Get-CIMHardwareInventory, Get-CIMBootOrder,

For example, the HardwareManagement module has several functions with names that might appear in other modules, such as Get-Account and Get-Computer System. So, the module author defined a default prefix, CIM. Let’s look at it.

This command gets the path to module manifest and then converts the manifest to hash table, so it’s easier to examine.
(h/t @LeeHolmes for the command format. It converts any hash table string a hash table).

#Convert the module manifest to a hash table # The manifest path is in the module's Path property
PS C:\&gt; $manifest = Invoke-Expression (Get-Content -Raw -Path ((Get-Module -List HardwareManagement).Path))

Here’s the DefaultCommandPrefix key. It has a value of ‘CIM’.

PS C:\> $manifest.DefaultCommandPrefix
CIM

The manifest also reveals that the functions in the HardwareManagement module, as defined, don’t have the ‘CIM’ prefix in the name.

PS C:\> $manifest.FunctionsToExport
Clear-RecordLog
ConvertTo-OctetString
Disable-Account
Enable-Account
Get-Account
Get-AccountMgmtService
Get-BootOrder
Get-ComputerSystem
Get-ConsoleRedirection
...

However, when you import the modules into the session, the nouns in the function names have the ‘CIM’ prefix.

PS C:\> Import-Module HardwareManagement
PS C:\> Get-Command -Module HardwareManagement

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Clear-CIMRecordLog                                 1.2.0.0    HardwareManagement
Function        ConvertTo-CIMOctetString                           1.2.0.0    HardwareManagement
Function        Disable-CIMAccount                                 1.2.0.0    HardwareManagement
Function        Enable-CIMAccount                                  1.2.0.0    HardwareManagement
Function        Get-CIMAccount                                     1.2.0.0    HardwareManagement
Function        Get-CIMAccountMgmtService                          1.2.0.0    HardwareManagement
Function        Get-CIMBootOrder                                   1.2.0.0    HardwareManagement
...

Get-Help recognizes the command name with its prefix. Note that Get-Help automatically includes the prefix in the Name, Syntax, and Examples, but not in descriptions and other written text.

PS C:\> Get-Help Clear-CIMRecordLog -full

NAME
Clear-CIMRecordLog

SYNOPSIS
Clears a record log

SYNTAX
Clear-CIMRecordLog -CimSession -InstanceID [-UseRecordLogProfile] [-WhatIf] [-Confirm]
[]

Clear-CIMRecordLog [-CimRecordLog] [-UseRecordLogProfile] [-WhatIf] [-Confirm] []

DESCRIPTION
Removes all entries from a specific record log from managed node based on support of Record Log Profile

More details about the Record Log Profile can be found here:

http://www.dmtf.org/sites/default/files/standards/documents/DSP1010_1.0.pdf
http://www.dmtf.org/sites/default/files/standards/documents/DSP1010_2.0.pdf

PARAMETERS
-CimRecordLog
...

And, you can run the command as usual with the prefix.

PS C:\> Clear-CimRecord -CimSession $cs InstanceID 1

Specify a custom prefix

The DefaultCommandPrefix is just a default. You can specify a custom prefix for the commands any module. If the module has a default command prefix, it is ignored and the custom prefix that you specify is used instead.

To specify a custom prefix for the commands in a module, use the Prefix parameter of Import-Module.
For example, because I have both the Microsoft.PowerShell.Archive and PSCX module on my test machine, I have two commands named Expand-Archive. (Note the wildcard in the command.)

#Note the wildcard. Otherwise, it would return only the function.
PS C:\> Get-Command Expand-Archive*

CommandType     Name                     Version    Source
-----------     ----                    -------    ------
Function        Expand-Archive          1.0.0.0    Microsoft.PowerShell.Archive
Cmdlet          Expand-Archive          3.2.1.0    Pscx

By default, Windows PowerShell runs the Expand-Archive function, because functions take precedence over cmdlets. So, to make it easier to run the PSCX cmdlet, I specify a ‘PSCX’ prefix when I import the PSCX module.

PS C:\> Import-Module PSCX -Prefix PSCX
PS C:\> Get-Command Expand-*Archive

CommandType     Name                  Version    Source
-----------     ----                  -------    ------
Function        Expand-Archive        1.0.0.0    Microsoft.PowerShell.Archive
Cmdlet          Expand-PSCXArchive    3.2.1.0    PSCX

Now, it’s easy to distinguish the commands and use the one I want.

PS C:\ > Expand-PSCXArchive -OutputPath ...
PS C:\ > Expand-Archive -DestinationPath ...

If a module has a DefaultCommandPrefix, the prefix that you specify in your Import-Module command is used instead of the default. For example, the default command prefix for the HardwareManagement module is ‘CIM’, but I prefer ‘Hardware’.

By default, the command prefix is CIM.

PS C:\> Import-Module HardwareManagement
PS C:\> Get-Command -Module HardwareManagement | Sort Name

CommandType     Name                             Version    Source
-----------     ----                             -------    ------
Function        Clear-CIMRecordLog               1.2.0.0    HardwareManagement
Function        ConvertTo-CIMOctetString         1.2.0.0    HardwareManagement
Function        Disable-CIMAccount               1.2.0.0    HardwareManagement
Function        Enable-CIMAccount                1.2.0.0    HardwareManagement
Function        Get-CIMAccount                   1.2.0.0    HardwareManagement
Function        Get-CIMAccountMgmtService        1.2.0.0    HardwareManagement
Function        Get-CIMBootOrder                 1.2.0.0    HardwareManagement
...

Specify the ‘Hardware’ value of the Prefix parameter.

PS C:\> Remove-Module HardwareManagement
PS C:\ps-test> Import-Module HardwareManagement -Prefix Hardware
PS C:\ps-test> Get-Command -Module HardwareManagement | Sort Name

CommandType     Name                                 Version    Source
-----------     ----                                 -------    ------
Function        Clear-HardwareRecordLog              1.2.0.0    HardwareManagement
Function        ConvertTo-HardwareOctetString        1.2.0.0    HardwareManagement
Function        Disable-HardwareAccount              1.2.0.0    HardwareManagement
Function        Enable-HardwareAccount               1.2.0.0    HardwareManagement
Function        Get-HardwareAccount                  1.2.0.0    HardwareManagement
Function        Get-HardwareAccountMgmtService       1.2.0.0    HardwareManagement
Function        Get-HardwareBootOrder                1.2.0.0    HardwareManagement
...

Limits of Command Prefixes

Prefixes are a great solution for avoiding name conflicts, right? Well, sometimes. But a lot of things can go wrong. One of them went wrong in my demo, but that’s actually a good reminder.

As pointed out by one of the Mississippi PowerShell User Group participants (one of many really great conversations), it’s not a good idea to use prefixes in a script or module that you share with others. You cannot predict what else is in the session and you might actually create a name conflict, rather than resolving one.

Also, because modules are imported automatically, it’s easy to end up with multiple copies of the same command in the session. That’s what happened in my demo (but, unfortunately, not in my practice sessions).

First I showed that the commands in the module had no intrinsic noun prefix.

PS C:\> (Invoke-Expression (Get-Content -Raw (Get-Module HardwareManagement -List).Path )).FunctionsToExport | Sort
Clear-RecordLog
ConvertTo-OctetString
Disable-Account
Enable-Account
Get-Account
Get-AccountMgmtService
Get-BootOrder

Next, I showed that PowerShell automatically used the specified DefaultCommandPrefix value of CIM.

PS C:\> Get-Command -Module HardwareManagement

CommandType     Name                                   Version    Source
-----------     ----                                   -------    ------
Function        Clear-HardwareRecordLog                1.2.0.0    HardwareManagement
Function        ConvertTo-HardwareOctetString          1.2.0.0    HardwareManagement
Function        Disable-HardwareAccount                1.2.0.0    HardwareManagement
Function        Enable-HardwareAccount                 1.2.0.0    HardwareManagement
Function        Get-HardwareAccount                    1.2.0.0    HardwareManagement
Function        Get-HardwareAccountMgmtService         1.2.0.0    HardwareManagement
Function        Get-HardwareBootOrder                  1.2.0.0    HardwareManagement
...

Then, I showed how to use the Prefix parameter of the Import-Module cmdlet to define your own prefix.

PS C:\> Import-Module HardwareManagement -Prefix Hardware

But, when I displayed the commands in my session, I had both commands with a CIM prefix and commands with a Hardware prefix.

PS C:\> Get-Command -Module HardwareManagement

CommandType     Name                                   Version    Source
-----------     ----                                   -------    ------
Function        Clear-CIMRecordLog                     1.2.0.0    HardwareManagement
Function        Clear-HardwareRecordLog                1.2.0.0    HardwareManagement
Function        ConvertTo-CIMOctetString               1.2.0.0    HardwareManagement
Function        ConvertTo-HardwareOctetString          1.2.0.0    HardwareManagement
Function        Disable-CIMAccount                     1.2.0.0    HardwareManagement
Function        Disable-HardwareAccount                1.2.0.0    HardwareManagement
Function        Enable-CIMAccount                      1.2.0.0    HardwareManagement
Function        Enable-HardwareAccount                 1.2.0.0    HardwareManagement
Function        Get-CIMAccount                         1.2.0.0    HardwareManagement
...

I thought PowerShell might be at fault, but the fault was mine. The Get-Command command auto-loaded the module with the CIM-prefixed commands. Then, I explicitly imported the Hardware-prefixed commands. This isn’t a practical problem, because running the commands with either name would work, but it’s certainly confusing.

I’ll be talking and blogging about module and command conflicts over the next few months. If you have questions or suggestions, please let me know. And, thanks to the Mississippi PowerShell User Group for the great participation.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Update-Module 5.0 adds, not updates

$
0
0

Applies to:

— Windows PowerShell 5.0.10586.63
— PowerShellGet 1.0.0.1


I’m a huge fan of sharing Windows PowerShell modules and making them easy to find (Find-Module), view (Save-Module), and install (Install-Module). So, I truly love the new PowerShellGet module.

However, you really need to understand how it works before you use it. Otherwise, you might end up with commands and modules that don’t work correctly (or at all) or don’t do what you expect. Or, you might download modules with commands that shadow or hide commands that your scripts run.

Update-Module is one of the really useful cmdlets in PowerShellGet, but if you don’t know how it works, it might surprise you. In Windows PowerShell 5.0, Update-Module doesn’t update a module. It adds one.

And, this is important to understand, because when you have side-by-side versions of the same module, you might mistakenly import the wrong version of a module or run the wrong version of a command.

I once made the mistake of piping Get-InstalledModule (all of them) to Update-Module. It took a long time to run and it installed additional versions of many of my modules in my poor, bloated Modules directory.

Here’s how Update-Module works:

  • When you use Update-Module with no version parameters, it checks to see if the newest version of the module is newer than the newest version you have. If it is, it installs the newest version beside the current version in the same directory. Otherwise, it does nothing.
PS C:\> Get-Module -List PowerForensics

Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules
ModuleType Version    Name                  ExportedCommands
---------- -------    ----                  ----------------
Binary     1.0.2      PowerForensics        {ConvertFrom-BinaryData, ConvertTo-ForensicTimeline

PS C:\> Update-Module PowerForensics
PS C:\> Get-Module -List PowerForensics
Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules
ModuleType Version    Name                  ExportedCommands
---------- -------    ----                  ----------------
Binary     1.1.1      PowerForensics        {ConvertFrom-BinaryData, ConvertTo-ForensicTimeline
Binary     1.0.2      PowerForensics        {ConvertFrom-BinaryData, ConvertTo-ForensicTimeline

 

  • When you use Update-Module with the RequiredVersion parameter, it checks to see if the specified version of the module is already installed. If it is not, it installs the specified version beside the current version in the same directory. Otherwise, it does nothing.In this example, I use -RequiredVersion to install an earlier version of the module.

 

PS C:\> Get-Module -List xJea

    Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules

ModuleType Version    Name        ExportedCommands
---------- -------    ----        ----------------
Manifest   0.2.10     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...

PS C:\> Update-Module xJea -RequiredVersion 0.2.9
PS C:\> Get-Module -List xJea

    Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules

ModuleType Version    Name        ExportedCommands
---------- -------    ----        ----------------
Manifest   0.2.10     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...
Manifest   0.2.9      xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...
  • When you use Update-Module with the MaximumVersion parameter, it determines whether it has a version equal to or greater than the specified version. If it does and if (IFF) the selected version is newer than the newest version of the module that is installed, it installs the selected version beside the current version in the same directory. Otherwise, it does nothing.
PS C:\> Get-Module -List xJea

    Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules

ModuleType Version    Name        ExportedCommands
---------- -------    ----        ----------------
Manifest   0.2.10     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...
Manifest   0.2.9      xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...

PS C:\> Find-Module -Name xJea -RequiredVersion 0.2.8

Version    Name      Type       Repository           Description
-------    ----      ----       ----------           -----------
0.2.8      xJea      Module     PSGallery            Module with DSC Resources f


PS C:\> Update-Module -Name xJea -MaximumVersion 0.2.8
PS C:\> Get-Module -List xJea

    Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules

ModuleType Version    Name        ExportedCommands
---------- -------    ----        ----------------
Manifest   0.2.10     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...
Manifest   0.2.9      xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}...

PS C:\> Update-Module -Name xJea -MaximumVersion 0.2.11
PS C:\> Get-Module -List -Name xJea

    Directory: C:\Users\JuneB\Documents\WindowsPowerShell\Modules

ModuleType Version    Name        ExportedCommands
---------- -------    ----        ----------------
Manifest   0.2.11     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}
Manifest   0.2.10     xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}
Manifest   0.2.9      xJea        {Show-JeaPresentation, show-JeaWhitePaper, Show-JeaExamples}
  • With the Force parameter, Update-Module skips all checks for versions currently installed on the computer. As a result, with -Force, Update-Module actually updates, that is, replaces, the specified version of the module if it’s already installed on the computer. If the requested version is not installed, Update-Module installs it, even if it’s older than an currently installed version.
PS C:\> Update-Module -Name xJea -RequiredVersion 0.2.10 -Verbose
VERBOSE: Checking for updates for module 'xJea'.
VERBOSE: Repository details, Name = 'PSGallery', Location = 'https://www.powershellgallery.co
IsRegistered = 'True'.
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: Using the specified source names : 'PSGallery'.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2/' and PackageMan
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id
VERBOSE: Total package yield:'1' for the specified package 'xJea'.
VERBOSE: Skipping installed module xJea 0.2.10.

PS C:\> Update-Module -Name xJea -RequiredVersion 0.2.10 -Verbose -Force
VERBOSE: Checking for updates for module 'xJea'.
VERBOSE: Repository details, Name = 'PSGallery', Location = 'https://www.powershellgallery.co
IsRegistered = 'True'.
VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: Using the specified source names : 'PSGallery'.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2/' and PackageMan
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id
VERBOSE: Total package yield:'1' for the specified package 'xJea'.
VERBOSE: Performing the operation "Update-Module" on target "Version '0.2.10' of module 'xJea
VERBOSE: The installation scope is specified to be 'CurrentUser'.
VERBOSE: The specified module will be installed in 'C:\Users\JuneBlender\Documents\WindowsPow
VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'.
VERBOSE: Downloading module 'xJea' with version '0.2.10' from the repository 'https://www.pow
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id
VERBOSE: InstallPackage' - name='xJea', version='0.2.10',destination='C:\Users\JuneBlender\Ap
VERBOSE: DownloadPackage' - name='xJea',
version='0.2.10',destination='C:\Users\JuneBlender\AppData\Local\Temp\1556275497\xJea\xJea.nu
uri='https://www.powershellgallery.com/api/v2/package/xJea/0.2.10'
VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/xJea/0.2.10'.
VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/xJea/0.2.10'
VERBOSE: Completed downloading 'xJea'.
VERBOSE: InstallPackageLocal' - name='xJea', version='0.2.10',destination='C:\Users\JuneBlend
VERBOSE: Module 'xJea' was installed successfully.

 

If you’re worried about side-by-side versions, you can use the Get-InstalledModule and Uninstall-Module cmdlets to uninstall all older versions.

PS C:\> Get-InstalledModule -Name xJea -AllVersions

Version    Name         Type       Repository           Description
-------    ----         ----       ----------           -----------
0.2.10     xJea         Module     PSGallery            Module with DSC
0.2.11     xJea         Module     PSGallery            Module with DSC
0.2.8      xJea         Module     PSGallery            Module with DSC
0.2.9      xJea         Module     PSGallery            Module with DSC

PS C:\> Get-InstalledModule -Name xJea -AllVersions |
           Sort Version -Descending |
           Select-Object -Skip 1 |
           Uninstall-Module

PS C:\> Get-InstalledModule -Name xJea -AllVersions

Version    Name       Type       Repository           Description
-------    ----       ----       ----------           -----------
0.2.11     xJea       Module     PSGallery            Module with DSC Resources for Just Enough Ad

 

So, how does Update-Module differ from the Install-Module cmdlet?

  •  It install only newer versions of the module (without -Force).
  •  It always installs its versions of the module in the same directory. It doesn’t not have a Scope or Path parameter.
  •  It does not have a MinimumVersion parameter.
  •  None of the parameters have a Version alias. In the Import-Module, Find-Module, Save-Module, and Install-Module cmdlets, -Version is an alias for the MinimumVersion parameter.

And, just to make things interesting, the new Update-Script cmdlet actually updates the specified script. It does not install a newer version of the script in the same directory or in a versioned subdirectory.

In summary, Update-Module is great, but you need to understand how it works.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at juneb@sapien.com and follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Automation — in Science

$
0
0

I recently had the great honor of attending my eldest son’s Ph.D. thesis defense. It was one of those mommy moments — you know, “my son the doctor.”

Screenshot 2016-02-13 14.08.59
Jackson Cahn, Ph.D. with his parents, June Blender and Andy Cahn
Photo by Ron Rogers

Although I’d discussed my son’s work with him for years, I didn’t expect to understand much of the advanced chemistry or protein engineering parts of his thesis defense presentation. But, I was surprised to see that one element of his talk was exceedingly familiar.

He explained something about metabolic pathways and a successful manipulation of an enzyme to make it function in a new pathway and context; the kind of scientific first that leads Caltech to give a guy a Ph.D. But, he was also interested in improving the method so it could be more widely used.

He showed a diagram of a complex and fragile process. He explained that it’s slow, painstaking, error-prone, difficult to reproduce, and requires expert knowledge. He wanted to make the process easier, so more people could do it more quickly and reliably. Now, that’s quite familiar.

clip_image002
Brinkmann-Chen, Flock et al., 2013, Proc Nat Acad Sci, 110(27), 10946-10951.

 

And, it was followed by this slide. I have one like it in one of my decks.

clip_image004

 

And, this one. Again, familiar, except for the mutants.

clip_image006

And this:

clip_image008

This is a world that I know very well.

It turns out that one problem in protein engineering is an enormous library of options that cannot all be investigated. To narrow the options to the most likely candidates for success, my son studied the intrinsic rules that he and other experts in his subspecialty use to prioritize among options and identify candidates that are most likely to be successful. The result was a heuristic — a collection of very well informed intuitions about the relationships between protein structure and function.

Fortunately, my son learned a bit of C and Windows PowerShell before heading to grad school, which made it very easy for him to learn Python. He’s also a math whiz, although I can’t take any credit for that.

He translated the scientific method that the experts use, their combined heuristic, into an algorithm; a series of Python conditionals (IF statements) with vector math (from several sophisticated math modules) that examines the physical structure of a target protein. The Python script filters a huge, intractable library of options down to a few candidates that are most likely to be successful. His validation with a representative test set of proteins showed that the method was very effective. So effective that my son’s thesis adviser, an eminent scientist, declared in her introduction that he had “solved the problem.”

The result is a classic automation. A Python script back end with a web page front end. It works quickly and reliably. You still need to be a scientist to use it, but you don’t need to be an expert in this subspecialty. When it completes, you need a biochemist and classic method to test the options that it recommends, but the automation turns a ridiculously expensive and immersive task into a sane and practical one.

After the champagne and several parties, I asked my son, (yes, my son, the doctor), about the technique. “It’s basically what you do, Mom, applied to science.”

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at juneb@sapien.com and follow her on Twitter at @juneb_get_help. Thanks to Jackson K.B. Cahn, Ph.D. for his cooperation and permission to use his slides.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Viewing all 308 articles
Browse latest View live