The Story Behind the Naming and Location of PowerShell Profiles

Wed, Apr 26, 2006 4-minute read


It’s probably the 63 characters in PowerShell that have the most thought behind them.  You might ask, “Could it be any longer?  More random?”

Sure!  Other considerations were:

PS:55 > $random = new-object Random
PS:56 > $output = $null; 1..100 | % { $output += [char]$random.Next(97, 122) }; "$output.ps1"

PS:57 > $output = $null; 1..100 | % { $output += [char]$random.Next(97, 122) }; "$output.ps1"

PS:58 > $output = $null; 1..100 | % { $output += [char]$random.Next(97, 122) }; "$output.ps1"

PS:59 > $output = $null; 1..100 | % { $output += [char]$random.Next(97, 122) }; "$output.ps1"


Actually, the reason we introduced the $profile variable is to make this as short as possible to you, while still serving our extensibility needs.

So, first off: “My Documents.”

A disconcerting trend is for applications to spew their files all over this directory.  “My Widgets,” “My eBooks,” etc.  They do this even if you’ve never launched the application, or used the given file.  Knowing that, we still ask users to place their profile in their “My Documents.”  This is for two reasons. 

First, we don’t generate the files in this directory - they are user-authored documents, not application configuration files.  The folder will also contain other user-authored documents, such as types and formatting customizations.  Even more, the profile is one of a shell user’s most treasured documents.  Users quickly consider this to be a vitally personal document - along with their .emacs / .vimrc.  The internet is full of these “dotfiles” that people are proud enough to share.  It is already one of the first and most common things that PowerShell users share. In Word, you generate Word documents.  In Excel, you generate spreadsheets. In PowerShell, you generate profiles, scripts, and ps1xml extension files.  If a user can’t point their finger at a file in “My Documents” and remember putting it there, then it’s in the wrong spot.

This used to be in a directory called “PsConfiguration” (to be consistent with the installation directory of the All Users' version.)  Since that is no longer where we store the machine-wide profile, this has now changed to a much more understandable “WindowsPowerShell.”

Second, the “Application Data” is hidden by default.  That breaks tab completion, discoverability, and so much more for this commonly-used document.

Next: “<Installation Directory>” (for the all-user profiles)

We chose this location because it is the only reasonable and secure location to place a startup script that runs for every user.  If a malicious user can edit this file, they can add commands to it that will elevate that user to Administrator the next time the Administrator logs in (and automatically runs that profile script.) 

We used to store these files in a protected directory under “All Users Documents,” but that could still cause security problems.  If the administrator deletes the folder from the “All Users” directory, the Windows ACL lets any user recreate it.  A malicious user could then add an auto-running profile there.

It should really go into an /etc directory, but that oddly seems to be missing in the version of Unix called Windows that I’m currently running :)

Finally, “Microsoft.PowerShell_profile.ps1”

The PowerShell model supports the concept of multiple shells (“hosts”).  For example, the PowerShell.exe console application is the host that most users will know and love.  It has a certain Shell ID.  Karl Prosser’s PowerShell Analyzer is another shell.  It has its own Shell ID. In the future, we will have a GUI host that also has its own Shell ID.  You may want code that runs in one of those shells, but not the others. Profile code that accesses $host.UI.RawUI is something that you would probably want to be specific to a shell, for example.

In those situations, you would use the $profile variable. That variable refers to the user-specific profile for the current shell.  Its name derives primarily from the Shell ID of the current shell.  In contrast, the plain “profile.ps1” file is loaded for every shell.

Technically, the PowerShell engine loads profiles in the following order:

  1. “All users” profile is loaded from “\profile.ps1”
  2. “All users,” host-specific profile is loaded from “\Microsoft.PowerShell_profile.ps1”
  3. Current user profile is loaded from “\WindowsPowerShell\profile.ps1”
  4. Current User, host-specific profile is loaded from “\WindowsPowerShell\Microsoft.PowerShell_profile.ps1”

With only one shell (PowerShell.exe,) it doesn’t really matter which you use if you use only PowerShell.exe.  I personally use the zenfully correct $profile to hold my modifications, though.

[Edit: Fixed typo in “All Users” document load location.  Thanks, Tony!]
[Edit 10/04/06: Updated to describe the more recent changes in RC2]