Well, after some extensive hacking this long weekend, I’m happy to announce the opening of the refurbished (and renamed) PowerShell Code repository at it’s new domain: PoshCode.org with a few new features and a slightly overhauled look.

I expect that soon we’ll be replacing the back end with something which has a few enhancements over this version (at least in terms of tracking the many versions of a particular code contribution), but even without that, we hope that the current feature set will be enough to push the repository into mainstream usage as the primary place to share PowerShell code.

The new PoshCode site is still based on the old pastebin GPL source code, but I’ve hacked it up so much you’ll hardly recognize it. Sadly, I haven’t kept it as generically clean as I could have, so although I’m making the source code available, you’ll have to clean up the “layout” files if you want to run your own repository.

New Features

The most obvious new feature is that the front page now prominently features the most recent contributions. This list is filtered to only show the last in a series of contributions (so if you make a new contribution based on a previous one, the front page won’t be swamped with iterations of a single script the way the old one was).

The best new feature is that you can now browse by author (currently, you simply get a list of their 25 most recent contributions, in the future we’ll add some paging functionality as the need arises).

Finally, I’ve changed the description field (which previously allowed any html) to use only a restricted subset of Textile. I’ll put up some documentation about this later, but for now: you can make paragraphs by leaving a blank line, and can use asterisks for bold and underscores for italics … and starting each line with an asterisk or a pound sign will get you bulleted or numbered lists. Any links will be rel=“nofollow” and you can’t use raw HTML at all.

Earlier Enhancements

In addition to these new features, there are a few other enhancements that I added on to the original code: a couple of extra fields, permanent storage, search functionality, and a recent contributions RSS (Really Simple Syndication) feed.

Incidentally, the search functionality allows you to search the author, description, title, and main content fields using MySQL’s full-text indexing search, and is accessible via the new PoshCode script functions (available as both a PowerShell v2 CTP-compatible PoshCode module and a v1 PoshCode script).

The cool thing about the way authenticode signatures are implemented is that even if a script is signed with a self-issued certificate, you can still tell if the script has been tampered with… Check this out:

[1]:ls SCRIPTS:\UnknownCert\Sample*.ps1,SCRIPTS:\TrustedCert\Sample*.ps1 | gas

Directory: SCRIPTS:\UnknownCert\

SignerCertificate Status Path
————————- ——— ——
0DA3A2A2189CD74AE371E6C57504FEB9A59BB22E UnknownError Sample.ps1
0DA3A2A2189CD74AE371E6C57504FEB9A59BB22E HashMismatch SampleBAD.ps1

Directory: SCRIPTS:\TrustedCert\

SignerCertificate Status Path
————————- ——— ——
B658C20AAD070B9FF105C69BBC47ADCF56FD5576 Valid Sample.ps1
B658C20AAD070B9FF105C69BBC47ADCF56FD5576 HashMismatch SampleBAD.ps1

As you can see, in the case of a UNTRUSTED, but correct, signature, you get the UnknownError status. If you checked the output object, it has a StatusMessage which says “A certificate chain could not be built to a trusted root authority”. If the script has been altered (as in my SampleBAD.ps1 scripts) then the signature is incorrect, you get the HashMismatch status, and the corresponding StatusMessage is: “The contents of file SCRIPTS:\TrustedCert\SampleBAD.ps1 may have been tampered because the hash of the file does not match the hash stored in the digital signature. The script will not execute on the system…”

One odd thing is that the messages are inaccurate about not executing the script: if you have your execution policy set to Unrestricted, the signatures aren’t checked at all, and if you have it set to RemoteSigned they are only checked for remote scripts. Furthermore: if you do have your execution policy set to AllSigned, neither the UnknownError nor the HashMismatch script will execute — only the one Valid scripts will.

So what?

The bottom line is: you can verify that nothing has happened to the script — even if you don’t trust the person who signed it nor the person, group, or company that issued a certificate to them. Why does this matter? Well, I recently wrote a post about generating self-signed code-signing certificates which can be used for signing PowerShell scripts, and if you chose to distribute scripts signed with one of those certificates, nobody would be able to verify the root CA(Certificate Authority) and so the signatures would never come out as valid.

Is there any usefulness in this? Well, I guess that depends on your perspective, but basically, I think that if I published my scripts signed and tell you on my blog what my certificate thumbprint is … that you’d be more able to trust those scripts than you are now (when they’re not signed at all). Of course, I could go one step further, and publish my own self-signed root CA certificate so you could choose to trust that …

I was recently having a conversation about the future of the PowerShell Script Repository and it involved some discussion of whether it would be safe to use the Repository Scripts to download dependencies automatically… The answer, obviously, is no.

But it started me thinking again about scripts being signed. If you had already chosen to run a script provided by me (which was signed by a certificate you couldn’t verify), maybe you’d be willing to trust other scripts signed by the same certificate, so we could automatically download them. Well, maybe even then you wouldn’t want to trust it, but lets assume that you were running a copy of the PowerShell Script Repository internally at your company …

Would you use automatic dependency downloading?

We could easily have a function that takes the script name and verifies that you have that script available — and if not, it could fetch the script from your designated repository and verify that the signature is valid even if the certificate isn’t signed by a root certificate authority you trust.

Of course, such automatically downloaded scripts would need to be marked as “Remote” so if you had your Execution Policy set to AllSigned or Remote Signed, then the script would only run if you had trusted it’s author (and you wouldn’t even be offered the option if you hadn’t trusted the CA(Certificate Authority) that issued his script. In that case you would need to review the script and re-sign it yourself — or manually remove the “remote” bit.

Imagine something like this:


# Get-Paste.ps1
function Get-Paste {
   Resolve-Dependency Get-Webfile
   # lots of code here that uses the Get-Webfile function ...
   Get-Webfile http://HuddledMasses.org/
}
 

When you tried to execute Get-Paste, it would check for Get-Webfile, and if it couldn’t find it, would attempt to download it (presumably this would involve asking your permission, and placing it in some specific location that was in your PATH, so that the script could find it when it tried to execute it on the next line).

Or maybe, an Apt-Get?

Perhaps instead of this mechanism, we could use the new embeddable “Data Language” to provide a list of dependencies, like: DATA Dependencies { scripts = Get-WebFile } and run a Resolve-Dependencies function against each script before trying to execute it — this way, if you downloaded a script from the repository using Get-Paste, it could automatically Resolve-Dependencies and offer to download the other scripts at the same time.

The fact is that doing this correctly will require some major reworking of the script repository to allow tracking new versions of scripts better, and to let the script repository track dependencies explicitly so that you don’t have to download the whole script to find out what it’s dependencies are, but this could be done, if people are actually interested in it.

A web of trust?

Ad I’m thinking about this, I’m wondering again about the possibility of creating an informal web-of-trust style code-signing certificate tree. The idea would be that the Script Repository would have a CA certificate of it’s own, and would issue code-signing certificates to PowerShell developers cheaply (free?) by skipping over some of the usual verification steps. In an ideal world, Microsoft would issue the PowerShell Community a “SubCA” certificate signed by their root — in the interests of promoting code signing for PowerShell …

However, if we couldn’t get a SubCA certificate for “free” or cheap, we could simply generate and self-sign our own, and publish it on the Script Repository website, requiring users to download and import it into their trusted roots if they wanted to use trust permissions. Regardless of whether they chose to trust it or not, they could still verify the scripts were valid, which is better than what we have now — the rest would be up to the user.

Of course, if we were issuing certificates that were self-signed anyway, we could go a step further and sign SubCAs and distribute them to, say, the Microsoft PowerShell MVPs and trusted community leaders after verifying email addresses and physical mailing addresses etc … trusting them further to issue (less trusted) code-signing certificates to additional developers.

Call to action

All of this is extra work for the people maintaining the script repository web site (right now, that’s me), but it might be worth it if it makes it easier to use the script repository, easier to trust the scripts on it, and easier to verify that an author is who he says he is … what do you think?

  1. Should we put in the work to set up a web of trust or should we leave it up to individual developers to self-sign and generate their certificates (and publish their public roots on their websites or something)?
  2. Should we just leave it at that (scripters can sign their scripts if they feel like it), or should we push and promote script signing? As an incomplete example of promoting code-signing, I mean:
    1. We can use certificates as the primary (or only) way for authors to identify themselves (that is: no log-ins, unsigned scripts are marked “anonymous” ... but we track the thumbprints and allow you to browse scripts signed by the same author, etc).
    2. We can include the signature thumbprint with the short descriptions output on the search results and from the scripts which interact with repository.
    3. We can restrict “latest version” updates to only scripts which are signed, and optionally to new versions signed by the same certificate.
  3. Is there any point (or hope) in trying to get a signed CA certificate? Can Microsoft help us out? Do any of you work at a certificate authority?

One of the major security features of PowerShell is the support for code signing of scripts, so that you can set an execution policy that requires scripts to be signed before they can be run. Of course, it goes a bit further than that. When a script has been signed by a certificate with a root Certificate Authority (CA) that you don’t already “know” or trust it can’t be run at all until you add the root CA to the system’s certificate store.

Even after you trust a specific authority, you haven’t trusted a script author — so any signed script you run will prompt you whether you want to allow it or not, like so:

[19]: .\test-script.ps1

Do you want to run software from this untrusted publisher?
File C:\Users\Joel\Documents\WindowsPowerShell\test-script.ps1 is published by
E=NoUser@HuddledMasses.org, O=Huddled Masses, L=Rochester, S=New York, C=US
and is not trusted on your system. Only run scripts from trusted publishers.
[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help (default is “D”):

The important thing to note here is that you’re really being asked not about the script, but about the author. If you choose the Never or Always options, the certificate that was used to sign the script is added to the appropriate certificate store (“Untrusted Certificates” or “Trusted Publishers”, respectively). To be clear: this happens for each every new author certificate, regardless of whether it’s signed by a self-signed cert (where you’ve already installed the root certificate in your root store) or a certificate issued by a commercial CA — there’s no loophole, no matter what anyone may have said in the past.

So, you see … the support for code signing is built into the core of PowerShell — and it’s really a shame not to take advantage of it. There are plenty of articles out there about how to sign your scripts, and more, so I’m not going to get into that much — I want to address the question of how hard it is to create the certificates in the first place (and finish by giving you a sample script which will generate and import them to your dev box with a single line command).

(more…)

There are two requests that I get with overwhelming regularity for the PowerShell script repository: first, that I would add some sort of “browsing” functionality, and second, that it should have an RSS (Really Simple Syndication) feed. Well, browsing may have to wait until a future iteration of the repository, but feeds are easy, because they’re basically just a hack-up of the “recent” items that are in the repository sidebar already, so I did it this evening.

Presenting …

PowerShellCentral.com/scripts/feed

Right now the number of items is limited to about ten, but if traffic picks up I can increase that easily — you can limit the count by passing the number of items you want to list like this: ?list=2.

You can also create a feed for specific search results (the feed will show items in relevance order, but the date is in the RSS so if you want to sort by that, you can). For instance, if you wanted to keep up to date on scripts that used SecureStrings, you would append your search terms like this: ?q=*securestring*. :)

As usual, my modifications to the PasteBin.com site are available on the repository site, and without much in the way of documentation. I will say this: in addition to creating the feed.php file, I moved some of the common translation functions into a translate.php file, and modified the dates that were being returned from db.mysql.class.php — it’s all under the Affero General Public License that Paul Dixon wrote PasteBin under in the first place.

So, people keep talking about a better PowerShell script repository, and there’s at least 3 or 4 different people working on improvements, but for now, the one we have is working ok, and already has hundreds of scripts in it — but there hasn’t been a way to add or retrieve scripts from within PowerShell.

So while I was taking a break from working on my own replacement script repository ;) I whipped up some scripts to Send-Paste, Get-Paste and Find-Paste. These scripts still need some work, but I think you’ll find them useful, so I’ve posted a version that is compatible with PowerShell v1 (and works fine on the 2.0 CTP) and doesn’t need any additional scripts (it includes the latest Get-WebFile script for downloading the files).

The Find-Paste function takes a search string, and adds wildcards — but otherwise performs the same search as you can already do on the web page. It returns the results in a PowerShell friendly PSObject which by default is just displayed in the host.

In order to download a script using Get-Paste, you must know the ID of the script — which you can get from the output of Find-Paste. Note that because Find-Paste does a full-text search, you may get results that are not the script you’re looking for (if they call that script, for instance), and you may get many versions of the script (and newer versions are not necessarily at the top of the search results).

The Send-Paste script is most useful for sending things to the temporary PowerShell pastebin on my site but can be used successfully to send scripts to the permanent PowerShell Script Repository … in fact, that’s how I uploaded the PowerShell Pastebin Functions to the repository … you just have to be careful to specify the title and description when you do that. Edit: I attached the script here on my site also because I made a mistake and deleted the original copy from the repository. [new]

So, you now have something a little bit more like a proper script repository. Hopefully the Usage comments in the script will be enough to help you figure out how to use the various commands, (and I did blog about the Send-Paste script about a month ago) but just in case you need help, let me give you a simple example.

Suppose I need to find a script to encrypt text so I can store some SecureString objects (like the passwords in a PSCredential object) in a file…


[100]: Find-Paste encrypt

Id          : 116
Title       : Start-Encryption
Description : Functions to encrypt and decrypt strings using the Rijndael symmetric key algorithm
Author      : Joel Bennett
Date        : 133 days ago
Link        : http://powershellcentral.com/scripts/116

Id          : 416
Title       : Pastebin Functions
Description : Send-Paste, Get-Paste, and Find-Paste ... to let you use PowerShellCentral.com/Scripts a little bit more
              like CPAN while you wait for us to get our act together and give you something better. :)
              IMPORTANT NOTE: the Send-Paste script defaults to our *temporary* pastebin site because we do not want
              you to fill up the central pastebin site with snippets ... but the other two default to the main long-term
              script repository.
Author      : Joel Bennett
Date        : 1 hour ago
Link        : http://powershellcentral.com/scripts/416
 

It looks like I found two, but clearly the second one is not the right one (I mentioned this in one of my examples in the pastebin scripts, and amusingly enough, that screwed up my example). So to download the script I want, I just do:


[101]: Get-Paste 116 Start-Encryption.ps1

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\Joel\Documents\WindowsPowerShell\Scripts

Mode           LastWriteTime       Length Name
----           -------------       ------ ----
-a---     5/30/2008 12:23 AM     2.378 KB Start-Encryption.ps1
 

Simple enough, right? I could have used the -Passthru option to see the script in the console, but since I was pretty sure it was the only one that might be useful (and since using -Passthrough just to display it on the console would mean downloading it a second time, if I actually wanted it). Another option would be to use the Tee-Object cmdlet, which would let us download it, show it in the host, and also save it in the file, all at once! :)


Get-Paste 116 -Passthru | tee -file Start-Encryption.ps1
 

Incidentally, I have a version of this which uses my PoshHttp snapin and Get-Web cmdlet, but it’s also written as a module and therefore requires PowerShell 2.0 CTP2 — for various reasons, I’m choosing to release that one separately later. If you are using the CTP, you could take advantage of the WPF UI abilities and try piping Find-Script ... | "Select-Grid":http://huddledmasses.org/wpf-from-powershell-select-grid/ | Get-Script to pick the script you want visually. :-D

I was just having some fun with some recent blog posts…

WPF & PowerShell – Part 5 has a script for “Get-Listbox” and for “Show-Control” and Halr9000 wrote a script he called Get-PSBlogroll

I had modified the example from the WPF post to create a listbox which will “start” whatever you double click …


Get-Listbox Title | Show-Control @{
   "MouseDoubleClick" = { [Diagnostics.Process]::Start( $window.Content.SelectedItem ) }
}
 

And I tried something like this to let me launch links from hal9000’s blogroll:


Get-PsBlogRoll | % { $_.HtmlUrl } | Get-Listbox | Show-Control @{
   "MouseDoubleClick" = { [Diagnostics.Process]::Start( $window.Content.SelectedItem ) }
}
 

Of course, what I’d like is to see the titles, instead of the URL, but because of how Get-ListBox was written, it only accepts strings, and therefore only returns strings. I tried adding the URL as a NoteProperty on the title string, but there’s a bug in the script cmdlet implementation that strips ETS properties. So if you specify your parameter type as a [string], you loose the NoteProperty.

Idea 1: Use [PsObject] for your pipeline parameter

Of course, if they fix the bug this tip will go away, but in the meantime, you’re better off not casting the input from the PowerShell native [PsObject] type, because you loose any extended type system attributes (and if you cast XML to a string, it comes out yucky).

Idea 2: Don’t output xml nodes

Even after fixing the Get-Listbox script so it outputs what it gets in without casting it, I still have to craft PsObjects with the output from Halr9000’s script, because otherwise Get-ListBox just outputs the .ToString() representation of the xml node, which is invariably useless — I could data-bind to the xml attributes, but it requires a separate syntax in the XAML — which means you’d have to have separate code for xml vs objects. So basically, you’re better off with a | Select * at the end of your Get-PsBlogRoll.

Idea 3: Display scripts should take a property (or a property list)...

What I eventually ended up doing is rewriting Get-Listbox to take a “Properties” parameter so that I could specify which of the attributes of an object I wanted to use … that way, I can pass in the almost unmodified output from Get-PSBlogRoll into the new Get-ListBox, and run with it. In the end, it looks something like this:


cmdlet Get-Listbox {
   param(   
    [Parameter(ValueFromPipeline=$true, Mandatory=$true)]$Input,
    [Parameter(Position=0,Mandatory=$false)][string[]]$Properties
    )
   Begin{
      $listItems = @()       
   }
   Process
   {
      if( $input -is [type] -and $input.IsEnum )
      {
         $listItems+= [Enum]::GetValues($input)
      }
      elseif( $input.GetType().IsEnum )
      {
         $listItems+= [Enum]::GetValues($input.GetType())           
      }
      else
      {
         $listItems += $input
      }               
   }
   
   End
   {
      $listbox = New-Object System.Windows.Controls.Listbox
      $listBox.ItemsSource = $listItems # | Select -unique

      # An optional DataTemplate, if they specified which properties they want to see
      if($Properties) {
         $xml = "<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><StackPanel>"
         foreach($property in $Properties) {
            $xml += "<TextBlock Text='{Binding Path=$($property)}'/>"
         }
         [XML]$xml = $xml + "</StackPanel></DataTemplate>"
         
         $listBox.ItemTemplate = [Windows.Markup.XamlReader]::Load(
            (New-Object System.Xml.XmlNodeReader $xml))
      }
     
      $listBox
    }
}

function Get-PsBlogRoll {
   $webClient = new-object System.Net.WebClient
   [xml]$Opml = $webClient.DownloadString("http://bloglines.com/export?id=halr9000")
   $PsBlogroll = $Opml.opml.body.outline | Where-Object { $_.text -eq '@Powershell' }
   ## The Select statement outputs a custom PSObject instead of the original (XmlNode) object
   $PsBlogroll.Outline | Select-Object title, text, htmlUrl, xmlUrl
}

Get-PSBlogRoll | Get-Listbox title | Show-Control @{
   "MouseDoubleClick" = {
      # But now I can use the "HtmlUrl" property to launch it in my browser
      [Diagnostics.Process]::Start( $window.Content.SelectedItem.HtmlUrl )
   }
}
 

Of course, because of how I wrote the DataTemplate for the properties (using a StackPanel and a loop to output multiple TextBlock objects) you could choose to show the Url as well, and all you’d have to do is change the call to Get-Listbox like this: Get-Listbox title,htmlUrl … which is pretty cool. Does anyone else have any ideas for making these even easier to compose?

A really nice trick would be if the wpf cmdlet could use Write-Output in the click handler and actually yield to allow the rest of the pipeline to process that item, but so far, I haven’t been able to find a way to do that (short of closing the WPF window, of course).

Modules are a new feature of PowerShell v2 which were added to CTP2 with very little fanfare and no documentation. After struggling with the demo from the release notes, and searching in vain for help files, I was finally able to figure some of this out with some help from x0n of Nivot Ink and since I still can’t find anything on the web about modules, I figured I’d share what little I know, even though I’m sure the moment I do the PowerShell team will write it up properly on their blog ;) ... let’s start with a direct quote from the release notes:

Modules allow script developers and administrators to partition and organize their Windows PowerShell code in self-contained, reusable units. Code from a module executes in its own self-contained context and does not affect the state outside of the module. Modules also enable you to define a restricted runspace environment by using a script.

The point of a module is that it allows script developers and administrators to organize and partition PowerShell scripts into self-contained, reusable units. Code from a module executes in its own self-contained context and does not have to affect the state outside of the module. Modules have access to the $PsScriptRoot variable which holds the path to the folder the script is running in, which allows you to dot-source additional scripts from the module directory, so you can split up your code into multiple files as you like — perhaps a file per function, or whatever works for you. Modules can even define a restricted runspace environment.

The self-contained state of modules is also persistent, and there can be private and public member functions (or cmdlets). This might be the key feature of script modules: Script-scope variables are visible to all functions loaded inside the module, and their state and values are persistent through multiple calls to cmdlets or functions from the same module. They are invisible to code outside the module, which means that multiple functions that work together need not store settings or state in the global scope, nor worry about name clashes. Later this week I’ll post the database helper functions which I use at work and have rewriten as a module, and hopefully that will help you see how this is useful, but for now let’s just move on.

Modules are the new Snapins?

As I’ve been experimenting, it turns out there’s way more to this than simple script modules. Modules appear destined to replace snapins as the main way to extend PowerShell. In fact, in the current CTP2 you can load most existing snapins as a module instead, which means you don’t have to be an administrator to load a new snapin — there’s no need to registery them by running InstallUtil.exe — you can simply place them in a folder and tell PowerShell where to find them.

Package Paths?

In order to load a module, it should have it’s own folder inside a package directory. You can load any script as a module by specifying the full path as it’s name — but if you create a folder in your PsPackagePath with the same name, and place them in it, you can load them by name without specifying the path. By default, the only package directory is the “Packages” directory (which you must create) inside your “WindowsPowerShell” directory (in your documents folder). However, you can override this to specify a different directory by setting the PsPackagePath environment variable. It’s just like any other path variable, so you can specify multiple directory paths separated by semi-colons.

The key thing to understand is that what PowerShell expects is that inside the PsPackagePath there should be a folder for each module, and the name of the folder becomes the name by which you call the module — the module file and the folder should have the same name, so for instance if you want to load a script TestModule.ps1 as a module, you need to put it in ~\Documents\WindowsPowerShell\*TestModule*\TestModule.ps1 and then you can just Add-Module TestModule. The name of the folder is the name of the module, and the main module file must have the same name even if it’s a dll module.

Here is my TestModule.psm1 script. This example is fun because it demonstrates quite vividly the difference between a psm1 module and a ps1 script:


function fooize {
   if(!(Test-Path variable:script:count)) { $script:count = 0 }
   $script:count++
   "He spoke 'foo' and then there were {0}!" -f $script:count
}
function barize {
   if(!(Test-Path variable:script:count)) { $script:count = 0 }
   $script:count--
   "She spoke 'bar' and then there were {0}!" -f $script:count
}
function foobar($value) {
   $script:count = $value
}
Export-ModuleMember fooize, barize
 

If you comment out the last line of that script and name it TestModule.ps1 (after putting it in an appropriate folder), you can Add-Module TestModule and run fooize and barize … they work fine. The problem is that the $count variable leaks into your runspace, and since it isn’t initialized as part of the script, you can’t reset it by re-running Add-Module TestModule, even with the -Force parameter, and Remove-Module TestModule won’t work. You can also arbitrarily access the count variable: "The count is $count" will print the current value, and $count = 42 will change it without running the associated code.

However, if you rename it to TestModule.psm1 and restore the Export-ModuleMember call … then when you run it, it has it’s own internal $count variable. In fact, if you do that without resetting your runspace from the previous example, you’ll see that even though your $count is still at 42, the fooize and barize functions are using a separate counter which you can’t modify externally. If you run Add-Module TestModule -Force now, the $count variable will reset!

Cmdlets for Modules

There are so far four cmdlets which are available for dealing with modules, but there isn’t any help or documentation for any of them, short of the release notes, so apart from what I’ve said so far, let me give you their parameter set definitions, since that’s the best we’ve got, and I’ll add my explanations, as I understand them.

Get-Module (returns PSModuleInfo objects)

  • Get-Module [[-Name] <String[]>]

Get-Module lets you get all, or some, of the loaded Modules. It returns PSModuleInfo objects for them which can be used in the Add and Remove cmdlets.

Remove-Module

  • Remove-Module [-Name] <String[]>
  • Remove-Module -ModuleInfo <PSModuleInfo[]>

Oddly enough, in the current CTP Remove-Module doesn’t really do what you expect. As far as I can tell, all that it does, is removes the PSmoduleInfo from the list of loaded modules. This means you can run add-module against it again (otherwise, running Add-Module a second time on a module that’s already loaded does nothing unless you use the -Force parameter) but it doesn’t really do anything else. In fact, you can continue to use cmdlets and functions from these “removed” modules without any problems that I can see so far. I’m not sure if this is considered a bug, or if the team expects to leave it like this. I dearly hope that in the next CTP we will at least see the cmdlets and functions removed so they can’t be executed, especially since I can already remove the functions by hand by simply deleting them from the provider.

Add-Module

  • Add-Module [-Name] <String[]> [[-ImportList] <String[]>] [-Force] [-PassThru] [-ScriptBlock <ScriptBlock>]
  • Add-Module [-Name] <String[]> [-ScriptBlock] <ScriptBlock> [[-ImportList] <String[]>] [-Force] [-PassThru]
  • Add-Module [-Force] [-PassThru] [-ScriptBlock <ScriptBlock>] [-ModuleInfo <PSModuleInfo[]>]

The Add-Module cmdlet is the main cmdlet that provides the functionality of the modules. It loads modules into your runspace, and it can load various types of modules. The first, and most obvious from the release notes, is the script module — a PowerShell script with the extension PSM1 — I’ll come back to these in a bit. The second type is what we now call “Snapins” in v1 — as far as I can tell, you can load nearly any snapin dll as a module if you configure it carefully — although I’m not sure that Providers can be loaded this way. You can also load PSD1 files, which are like metadata files about modules (which as far as I can tell, is how you would load a multi-assembly snapin-type module, or modules which needed type files, etc.). And finally, you can load plain old ps1 script files, which are loaded just as though you had dot-sourced them (which incidentally means they can’t be “Remove“d at all).

I’ll have to fill in the details of how the ImportList works as I figure it out. You can also use -Force to reload scripts you’ve already loaded, and you can specify -PassThru to have the PSModuleInfo object output from Add-Module …

When you run Add-Module -Force, it reinitializes the script scope for the module. Take for example the previous script module named TestModule.psm1. If you Add-Module TestModule and then run “fooize” ... each time you run it, the number at the end of the string will increment. When you re-run Add-Module TestModule with the -Force parameter, the “count” variable is reset, but otherwise it remains, and even after you Remove-Module Test you can still run fooize and barize!

Export-ModuleMember

  • Export-ModuleMember [-ExportList] <String[]> [-Update]

The final module cmdlet is actually the second most important one — without it, nothing in your PowerShell script modules would be visible after you load them. Generally speaking, you run Export-ModuleMember and pass it a list of function or cmdlet names which you wish to export — to make them visible from outside the script.

Exporting a module member is analogous to marking a C# member method public, in that it marks it for use by external scripts, etc. You shouldn’t use this cmdlet in a plain .ps1 script (or on the command-line) because it won’t work, and it actually does weird things (like killing my profile) that I can’t explain :-).

And for my final trick …

In terms of “protecting” your scripts from malicious users you should know that it’s possible to access the “private” variables and methods in a cmdlet, but it’s not possible to “change” them from outside. So for instance, if we take the example TestModule again — you’ll notice there’s a function at the bottom that we haven’t exported and that isn’t used at all — but which would allow us to set the count value. Let me demonstrate why:


[1]: Add-Module TestModule
[2]: fooize
He spoke 'foo' and then there were 1!
[3]: fooize
He spoke 'foo' and then there were 2!
[4]: barize
She spoke 'bar' and then there were 1!
[5]: $count
[6]: foobar 45
The term 'foobar' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
[7]: $tm = Get-Module TestModule
[8]: $tm
Name                          Path                                         IsCompiledCode Exports
----                          ----                                         -------------- -------
TestModule                    C:\Users\JBennett\Document...                         False {fooize, barize}

[9]: & $tm { $count }
1
[10]: & $tm { foobar 41 }
[11]: fooize
He spoke 'foo' and then there were 42!
 

After looking over the scripts I’ve pasted in the last few days it struck me that all of them load the UI from XAML — and for the most part, you can do pretty much whatever you need to do in WPF in pure PowerShell if you want to.

Select-Grid ScreenshotTo demonstrate this, I wrote a simple Out-Grid script. But then I got carried away, and wrote the script I’ve shown in this screenshot. :-D The resulting script not only illustrates how to code WPF in PowerShell without XAML, it also illustrates one of the few areas where you’re much better off loading from XAML than writing the plain code — and that’s a DataTemplate. It is possible to write them in code, but believe it or not, some of their features are accessible only from XAML.

[new] I’ve updated both the script and the screenshot to correct a bug I noticed last night after I posted this … so if you tried this and got a lot more columns than you had bargained for … here is the Select-Grid script again. [new] I updated it again just now to switch my Set-PsDebug statement for a Set-StrictMode statement, after reading Jeffrey Snover’s post about strict mode best practices.

I’m not going to paste the whole script in-line, but I thought I’d paste some of it here so that I could give a little explanation of what’s happening, in case you need a little help figuring it out…

Using modules …

Some of you may know that normally in a script like this we would put the “utility” functions inside the BEGIN block so-as to encapsulate them from your main runspace, and may be wondering why I didn’t in this case. It’s because the new CTP2 of PowerShell has this concept called a “module” ... which is somewhat like a script snapin — basically it allows you to have functions in the script which don’t end up in the main runspace, but which are still available to the other functions (or cmdlets, in this case) in the script.

I will write a separate post about how PowerShell Modules work, and what you can do with them … it’s too complicated to get into here. Suffice it to say that all I had to do to the script is add a line at the bottom: Export-ModuleMember Select-Grid and rename it to [new] Select-Grid.psm1 and put it in a specific location: Documents\WindowsPowerShell\Packages\Select-Grid\Select-Grid.psm1 — but of course, I couldn’t resist cleaning it up a bit as I was writing about it (if you grabbed it before v3.4, you should grab it again — look in the comment at the top).

Having done that, all you have to do is run Add-Module Select-Grid instead of dot-sourcing it, and then run it as before (see the screenshot).

Let’s talk about code bay-bee …

There’s a few things I wanted to point out in the module’s code, so lets get to that. (more…)

After posting my last post, I started thinking that perhaps I shouldn’t really have started with something so splashy [rolleyes]. So I started thinking about what I could use as a proper example — not of what WPF can do, but of what you might want to use WPF for in PowerShell.

I came up with two really obvious ideas which I think are both good demonstrations of useful things to do from PowerShell, and are good examples of something that really just doesn’t work with WinForms.

User Input prompts

I’ll write more about this in future posts, but for now, take as an ultimate example the latest post from mow, The PowerShell Guy in which he rewrites his PowerShell WMI Explorer to be WPF based — it seems to not only run faster, but be based on less code (of course, that might just be because he’s gained experience since the first version).

Data-bound Graphs

A WPF databound bar graph dialog from PowerShellThere are dozens of ways you can do data-bound graphs in PowerShell — including 3d — but the simplest to demonstrate (and coincidentally the easiest for me to come up with a use-case for) is a plain bar graph. I’ve written a simple graph window in XAML, and created a script Out-BarGraph which lets you send data to it … but there are, as the Genie says, some caveats, provisos, addendums, and quid pro quos. ;) (more…)

In my last post I wrote about how you could make a WPF Splash screen window in PowerShell, but I stated that: “if the images are remote, the WPF window has to download them, and therefore won’t work” correctly. I had played with downloading images directly by setting the Source attribute of the image to the image URL, and hadn’t been able to find a way to get the threading to work well enough to actually download the image.

Well, I figured that out, so I thought I’d go ahead and share … basically, all you have to do is call Invoke() on the window’s dispatcher. The reason that works is that WPF handles events and “work” in general in the order it needs to be done, so since the most important thing is drawing the UI it will do that first, and since your UI is based on downloading the image, it will take care of that first.

This is PowerShell v2 CTP2 code…

All of this code is written to target the CTP2 release of PowerShell v2 — it will not work in PowerShell v1 or even in v2 CTP1 … and it may not work in later releases, although I expect it will. Also, as with any code which uses WPF from PowerShell, these code examples require you to launch PowerShell with the -STA option.

Having said that, lets just straight to a WPF example (more…)