Powershell is a wonderful tool.  It replaced cmd.exe as my go-to shell long ago, and I’m consistently finding ways to automate painful, manual processes through simple, clean Powershell scripting.  One thing that bugged me though is that my Powershell profile was not consistent across all my many workstations.  By combining Dropbox and a little Poweshell scripting, I have pushed my Powershell profile into the cloud, and I now have a consistent Powershell experience across all of my development boxes.

The Magic of Powershell

If you are still using cmd.exe on Windows, You’re Doing It Wrong.  Unlike cmd.exe and it’s DOS-era capabilities, Powershell is a modern, robust, and very powerful shell (hence the name!)  You can script out complicated tasks quite easily, and since it’s built on .NET, you have full access to a wide array of .NET functionality.  What really sets Powershell apart though is that it operates on objects instead of text.  When you run “dir *.*” in cmd.exe, the output is simply a string listing of the directory’s contents.  In Powershell, however, what you actually get back are System.IO.FileInfo and System.IO.DirectoryInfo objects!

image

As you can imagine, this opens up some very interesting possibilities.

In addition to all this built-in functionality, there are community extensions that add additional capabilities.  My favorite is the Powershell Community Extensions (PSCX) module.  With it, you can do things like issue web requests:

image

You can also launch processes with elevated permissions:

image

Visual Studio 2010 and Powershell

Visual Studio 2010 (and all recent versions of Visual Studio, for that matter) ship with a shortcut that will launch cmd.exe and configure path variables so that you can easily access Visual Studio tools from the command line.  Since I mainly do development work, I always want Visual Studio tools to be accessible when I launch Powershell.  This is quite easy to achieve, as described by ‘Andy S’ on Stackoverflow.  I’ll take it a step further though and show you how to always have Visual Studio commands available on any of your development machines, regardless of where you’ve installed Visual Studio.

Putting Your Powershell Profile In The Cloud

I’ve mentioned before how found I am of Dropbox.  If you don’t have an account, go get one now, and get it running on all of your machines.  Dropbox is how the cloud should work: you shouldn’t even realize you’re using it. 

Let’s outline what we’re going to do.  First, we’ll create a simple wrapper Powershell profile that will run whenever we launch an instance of Powershell.  In our wrapper profile, we’ll call out to a our real Powershell profile script that we’ve stored in the cloud via Dropbox.  From here, we’ll load up Powershell Community Extensions, find where Visual Studio is installed and configure our environment variables appropriately, and finally apply any other customizations we want.  We’ll even create a stand-alone script we can run to automate the creation of our wrapper Powershell profile on each of our development boxes. 

As a prerequisite step, be sure you’ve configured Powershell so that it allows script execution.  Out of the box, Powershell is locked down and won’t allow you to run scripts.  You can fix this by opening an elevated instance of Powershell and running the following command:

Set-ExecutionPolicy RemoteSigned

image

With Dropbox installed, we’re ready to begin.  You may want to read up on Powershell profiles if you’re curious.  We’ll create a profile that will only affects our user and only loads when we run the official Microsoft Powershell… uhh… shell.  Create and open the file ‘%UserProfile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1’  This wrapper profile script will contain a single line of code to invoke our real Powershell profile script that we’ll store in Dropbox:

. "C:\Users\YOUR_NAME_HERE\Documents\My Dropbox\Powershell\MyProfile.ps1"

Yup, that’s really all there is to your local profile script.  The ‘.’ operator tells Powershell “run the following script in my current scope”, so any variables, functions, etc. the script adds apply to the session (which is what we want since that’s our real profile script). Be sure you replace YOUR_NAME_HERE with whatever your username is. The path needs to point to a folder (which we’re about to create) in your Dropbox.  If you installed Dropbox and chose to create your Dropbox at a different location on your machine, be sure you update your path accordingly. 

If we start Powershell right now, we’ll get an error because the profile script in our Dropbox doesn’t exist yet.  Let’s create it now.  Make a new ”Powershell” folder in your Dropbox, and create a Powershell script named “MyProfile.ps1”.  While you’re at it, go ahead and download the latest Powershell Community Extensions and extract them to your Powershell folder.  Since there are other modules I plan to include in my profile in the future, I actually made a Modules folder and put the PSCX folder there.  Your folder should look something like this:

image

We should be able to launch Powershell without error now, but it won’t actually do anything since our profile script is empty.   Let’s fix that:

#This is where the Visual Studio 2010 cmd.exe batch script is located. 
$VisualStudioDir = resolve-path "$($env:VS100COMNTOOLS)..\..\VC"

#This is where our shared profile script is located
$MyPath = (split-path $MyInvocation.MyCommand.Path)

pushd “$VisualStudioDir”
cmd /c "vcvarsall.bat&set" |
foreach {
  if ($_ -match "=") {
    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
  }
}
popd
write-host "`nVisual Studio 2010 Command Prompt variables set." -ForegroundColor Yellow

pushd $MyPath

#Import Powershell Community Extensions
Import-Module .\Modules\pscx

#Now we can use wget to make web requests.  It's not exactly the same as
#the real wget, but it's close enough. 
new-alias -name wget -value Get-HttpResource

popd

Our profile uses an environment variable to find where Visual Studio 2010 is installed.  From this, we can find the vcvarsall.bat script that will configure our environment with access to Visual Studio tools.  Next, we import the Powershell Community Extensions module.  The last bit, the ‘new-alias’ command, is not strictly needed.  It simply adds an alias for PSCX’s Get-HttpResource command so that I can call it using the familiar ‘wget’.   Feel free to make any other customizations you want to your profile at this point.  I recommend running ‘help about_Profiles’ if you want to see what sorts of things you can customize.

Wrapping Things Up

You are all set!  When you launch Powershell, you should see something like this:

image

I decided to take things one step further and automate the creation of my local wrapper profile script, that way I can run a single command to configure my profile on a new machine.  Create a Setup.ps1 script in the Powershell folder on your Dropbox with the following contents:

#This script will *OVERWRITE* your existing powershell profile!!!
#It is used to keep your PSH environment in sync through the cloud.

#This grabs the path to the current script, which we'll use to build up the 
#path to our profile in Dropbox. 
$MyPath = (split-path $MyInvocation.MyCommand.Path)

#Create the local Powershell profile script if it doesn't already exist.
if (!(Test-Path $profile)) {
    New-Item -ItemType File -Force $profile
}

#Have the local profile script invoke the shared profile script in Dropbox. 
"
. `"$MyPath\MyProfile.ps1`"
" > $profile

When you want to use your cloud-based profile on a new machine, invoke ‘Setup.ps1’ from your Dropbox, restart Powershell, and start enjoying your consistent environment!

 

And there you have it!  Powershell is aptly named, and you really should be using it instead of cmd.exe.  And thanks to the magic of Dropbox and cloud-backed storage, you can easily synchronize your profile between your personal workstation, your laptop, your company workstation, and any number of other boxes you use.  Give it a shot today, and please let me know if you have any additional tips or tricks I should try!