NuGet + PowerShell = (also) Crazy Delicious

First off, I should note that I'm kinda paraphrasing this title from Scott Hanselman's excellent PDC 2010 talk, but I'm sure he's ok with thatSmile. By now, if you've been following the news coming out of the Microsoft Web Platform and Tools team, you've probably heard about our latest open-source project: NuGet. I'll let Scott Guthrie fill you in if you haven't heard about it already. Well, since shipping Web Pages v1/MVC v3, we've been working on general clean-up of both our code and our processes. For me, part of that entailed wiping all my dev machines and re-installing from scratch (there's a certain level of happiness provided by a nice fresh machine Smile). As part of that, I was organizing my little PowerShell utility scripts into Modules. I've also been thinking of organizing them a bit so that they could be released, but I ran in to a problem: There isn't really a great way to distribute PowerShell Modules!

In PowerShell, a Module is really just a script within a special folder. When you "Import" the module, the script is run and the functions/variables/etc. it exports are added to the shell environment. There's much more to it, but I'll leave it to MSDN to explain the rest if you're interested.

Deploying a Module is as simple as dropping a folder in C:\Users\[user name]\Documents\WindowsPowerShell\Modules. But in order to get a Module from the Web, like say the very useful PowerShell Community Extensions package, you have to download a ZIP and extract it to the right place. Earlier this week I had a brainwave: "Isn't this _exactly_ what NuGet is designed to do?"

So, I spent a bit of spare time working on something I call "PsGet", which I'm releasing a very early version of right now. PsGet is a PowerShell Module that uses NuGet to deploy other PowerShell Modules. Right now, you do have to manually install PsGet, but I'm hoping to set up a single bootstrapping script that you can download and run to install it automatically. Let's walk through how it works:

First, download the PsGetBoot module from the BitBucket site I've set up. Here's a direct link to the ZIP file. Then, make sure the ZIP is unblocked in Explorer:

image

Now, open/create your PowerShell Modules folder in Explorer. It needs to go in "C:\Users\[username]\Documents\WindowsPowerShell\Modules". If you have moved your My Documents folder (like I have, to the D: drive in this case), you should update the path accordingly. Open the ZIP file in Explorer and copy the PsGetBoot folder into your Modules folder:

image

Next, you need to ensure you've enabled the execution of unsigned scripts. If you've already done this, skip this step. Start a PowerShell window as Administrator and run the following command: "Set-ExecutionPolicy RemoteSigned". Enter "Y" at the confirmation prompt.

image

Ok, if you skipped the last step, open a PowerShell window. Otherwise, you've already got one open Smile. Now we need to open your profile script and add a line to activate PsGet. The profile script is the script that runs every time you open PowerShell. First, let's make sure all the necessary folders exist by running:

mkdir (Split-Path -Parent $profile)

If you get an error saying the item already exists, don't worry, it just means you didn't need to do this step Smile. Next, open your profile script up in notepad (or your favorite text editor) and make sure to create it if it doesn't already exist:

notepad $profile

Type the following somewhere in the main body of your profile. It can go at the end, the beginning, or the middle, but not within a function:

Import-Module PsGetBoot

Then save the file, shutdown your PowerShell windows and open up a new one (Admin privileges are NOT required this time). The line we added to the PowerShell Profile will load in the PsGet "Bootstrapper" module. This is a module which just imports the "PS-Get" module and starts up PsGet. The purpose of this bootstrapper is to allow PsGet to update itself using PsGet Smile.

Once you've started PowerShell back up, you're all done! You now have two new commands. The first is "Get-PSPackage". I should warn you that this command is extremely simple right now and just lists every NuGet package from the main nuget.org feed, so be prepared for it to take a few minutes when you run it. PsGet automatically works against the main NuGet feed at nuget.org, but it doesn't really make sense to install packages like Entity Framework in to PowerShell. So, as a convention for now, packages on nuget.org which are PowerShell modules should have names which start "PS-". For example, I've already posted a very very simple package called PS-Bitz which is a library of useful functions (and by library, I mean a single function, for now Winking smile). To find all the PowerShell packages, just go to nuget.org and search for "PS-", or just click here. Once you've found a package, just use the "Install-PSPackage" command to install it:

Install-PSPackage PS-Bitz

image

Once the package is installed, you should be able to see it when you get the list of available PowerShell Modules:

image

(I'm not entirely sure why multiple entries show up, please let me know if you know why Smile).

Now that it's installed, you can add it to your CURRENT shell by running Import-Module:

Import-Module PS-Bitz.0.1.0.0

You can then use all the functions provided by it. In this package, there's a single function used to get the path of special folders like MyDocuments:

image

If you want to make it available every time you launch PowerShell, just add an "Import-Module" line for it to your Profile, just as we did with PsGetBoot.

As you can probably see, PsGet is pretty rough around the edges right now, but I'm hoping to improve it over time. I've set up a BitBucket repository here, so feel free to fork it and hack away. I'm licensing it under the Apache Public License, which matches NuGet's license. Let me know what you think! And start posting PowerShell module packages to NuGet.org!