Customizing Windows Powershell
The Windows PowerShell is probably one of Microsoft’s best innovations over the last few years. Really. It is a brilliant piece of work that finally gives the command line shell real power.
And what gives it even more power is its extensibility and its customization, as I shall demonstrate shortly with a few simple examples.
NOTE: This is a very long article, best digested in chunks! Do not operate heavy machinery if you go through the whole article! If you need a bathroom break, take it now!
If you have the latest build of Windows PowerShell, its icon will look something like this:
One launched you will see something like this:

Changing The Prompt
Personally, I’m not too fond of this. The prompt wastes too much screen space. So I’ll want to change the prompt to make it a bit more Unix-like:

Then we will get something that looks like this:

Awesome.
But then you ask, how do I find out where I am without doing a dir / ls? Very simple. If you have some *nix background you will remember the command
Which is just an alias for the cmdlet get-location
In fact if you have a *nix background you will be pleasantly surprised at the number of aliases Microsoft have already mapped to *nix commands.
An even better solution is to put the current folder in the title bar, where it won’t take up any real estate but save us from always typing pwd.
For this we create a modify the prompt command as follows:
function prompt { “$ “ $host.UI.RawUI.WindowTitle = $(get-location) }
Paste this in the command window
Press enter twice and we’re in business.

If you like the old stye prompt do this:
function Prompt { $host.ui.RawUI.WindowTitle = $(get-location) “PS> “ }
Next, let’s move on to doing a file listing. Surprise surprise, ls is an alias for dir
First let’s move to a directory with lots of files: Downloads. Then let us list them files

First notice that I’ve not specified the directory. The / in the command appears to evaluate to the root folder of the system drive. Awesome.
Now if you scroll down a bit you will see a few things that I’m not particularly fond of
- The date formats are not appealing to me. Despite the fact that in Kenya we use the British format day/month/year lots of software has a cavalier attitude towards my settings and tends to use its own so I always find myself mentally confirming that this is the case
- The file size … Quick, is ZuluPad 150Kb, 1Mb or 1.5 GB?
- Most *nix command shells colour different extensions differently e.g. blue for directories, red for zip files, etc. I WANT THAT!
So I will customize my shell to solve all my issues. (I’m fussy like that!)
Let’s start with #1
Formatting Date
PowerShell uses format files to control the display of the output of commands. The one for the file system happens to be called filesystem.format.ps1xml. We find this in the windows folder and open it

We then open the file and scroll down to the part that looks like this:

Now this is the bit we want to customize. First of all let’s tinker with the section that displays the LastWrite Time. This should be simple enough. All we need to do is change the format string from
[String]::Format(“{0,10} {1,8}”, $_.LastWriteTime.ToString(“d”), $_.LastWriteTime.ToString(“t”))
To
[String]::Format(“{0,10} {1,8}”, $_.LastWriteTime.ToString(“d MMM yyyy”), $_.LastWriteTime.ToString(“t”))
Then we save the file.
Now we have to get PowerShell to reload this definition, since the file is read at the startup of the shell. For this we run the command
Update-FormatData
If your pc is set up like mine you will get the following spectacular error:

Naturally, the format file is a signed script (scroll to the very bottom and you will see what looks like gibberish but is actually the fingerprint

Changing the content invariably invalidates the signature. So what to do, what to do?
Securing Scripts
There are a couple of options
- Allow unsigned scripts to run (Are you nuts?)
- Sign the file ourselves
I repeat if you take route #1 you’re out of your doggone mind, m’kay?
We will follow the latter. Thought it takes a bit more elbow grease and reference to the PowerShell help I’ve sifted through and this is what you need to do.
To sign a script, we first require a digital certificate. Happily we can generate our own digital certificate using the MakeCert command. To do this open a SDK command prompt and run the following command
makecert -n “CN=PowerShell Local Certificate Root” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine


Then we run the following command
makecert -pe -n “CN=PowerShell User” -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer
Once done we return to the PowerShell and check that it worked with this command
get-childitem cert:/CurrentUser/My -codesigning

Next, we create a script file that we will be using to sign all our future scripts . In a new file paste the following text
## sign-file.ps1 ## Sign a file param([string] $file=$(throw “Please specify a filename.”)) $cert = @(Get-ChildItem cert:/CurrentUser/My -codesigning)[0] Set-AuthenticodeSignature $file $cert
Ensure the file is named sign-file.ps1. Once done we need to sign our new file. We do this by running these commands
$cert = @(Get-ChildItem cert:/CurrentUser/My -codesigning)[0] Set-AuthenticodeSignature sign-file.ps1 $cert

Phew!!!!
Now we can try to sign our modified format file

Good grief! No luck again?!
The current security policy does not seem to allow this. What policy is in place anyway?
get-ExecutionPolicy
Oho! The current policies definitions are as follows:
Restricted
- Default execution policy.
- Permits individual commands, but scripts cannot run.AllSigned
- Scripts can run.
- Requires a digital signature from a trusted publisher on all scripts
and configuration files, including scripts that you write on the
local computer.
- Prompts you before running scripts from trusted publishers.
- Risks running signed, but malicious, scripts.RemoteSigned
- Scripts can run.
- Requires a digital signature from a trusted publisher on scripts and
configuration files that are downloaded from the Internet (including
e-mail and instant messaging programs).
- Does not require digital signatures on scripts run from the
local computer.
- Does not prompt you before running scripts from trusted publishers.
- Risks running signed, but malicious, scripts.Unrestricted
- Unsigned scripts can run.
- Scripts and configuration files that are downloaded from the Internet
(including Microsoft Outlook, Outlook Express and Windows Messenger)
run after warning you that the file originated from the Internet.
- Risks running malicious scripts.
So, what we need is at least AllSigned using this command
set-ExecutionPolicy AllSigned
This relaxes the restriction on no scripts but demands that all scripts must be signed by recognized authorities (Recognized by your system that is, such as our newly created authority!). This takes effect immediately, and persists until you change it.
Now let’s try and sign our format file again
Awesome!
Now let’s try and update our format data again
Update-FormatData
Awesome!
Now let’s see if our customization worked:
Excellent. The date now displays the month clearly.
Formatting The File Size
Now let’s turn our attention to the file size. Here we want to correct two problems:
- The caption Length is not too intuitive
- We want to show the file size in bytes, kilobytes, megabytes and gigabytes.
Again we open the filesystem.format.ps1xml and scroll to this bit

We change the Length to Size

Then we scroll down a little more to this bit

Now here things get interesting. We want to change that bit from extracting a property into a script block, so that code can be run. In our code we will do our calculations and get the file size.
But first a bit of design.
- For directories, we display nothing. We could recursively get the file sizes inside but that is an exercise for another day
- For files under 1024 bits we simply show the length
- For files under 1024 * 1024 we calculate the Kb
- etc until we get to GB.
Change it to look like this
<TableColumnItem>
<ScriptBlock>
[Int64]$BYTESIZE=1024;
if($_.Mode -like “d*”)
{
“”
}
else
{
if($_.Length -gt ($BYTESIZE * $BYTESIZE * $BYTESIZE))
{
[String]::Format(“{0:0.00} GB” , $_.Length / ($BYTESIZE * $BYTESIZE * $BYTESIZE ));
}
elseif($_.Length -gt ($BYTESIZE * $BYTESIZE))
{
[String]::Format(“{0:0.00} MB”, $_.Length / ($BYTESIZE * $BYTESIZE));
}
elseif($_.Length -gt $BYTESIZE)
{
[String]::Format(“{0:0.00} KB”, $_.Length / ($BYTESIZE));
}
else{
[String]::Format(“{0} B”, $_.Length );
}
}
</ScriptBlock>
</TableColumnItem>
Then we sign the file again and update the formatdata

Finally we list the files

Awesome! Notice the caption is now Size, and the display is more intuitive.
Colouring By File Type
Finally we attempt to colour the display of different file types.
Again we’re tinkering with the format file.
Before we write any code we do some thinking.
- To effect this change we need to detect the file type before we write anything i.e. before output of each line
- We also need to preserve the original console color so we can change it back when done
For the colors we will do as follows:
- For compressed archives (zip, rar, 7z, gz, tar, etc we will use red)
- For executables we will use green
- For directories we will use blue
- For media files we will use yellow
Locate the following line

Replace it with the following code
<ScriptBlock>
if($_.Mode -like “d*”)
{
$color=“Blue”
}
else
{
$archives=“.RAR”,“.ZIP”,“.TAR”,“.GZ”,“BZ”,“.ACE”
$media = “.MP3″,“.WMA”,“.OGG”,“.MOV”,“.WAV”
$executables=“.EXE”,“.MSI”,“.BAT”,“.COM”,“.PS1″
$ext = $_.Extension.ToUpper();
if ($archives -contains $ext)
{
$color=“Red”
}
elseif ($media -contains $ext)
{
$color = “Yellow”
}
elseif ($executables -contains $ext)
{
$color = “Green”
}
else
{
$color=“Gray”
}
}
$host.UI.RawUI.ForeGroundColor = $color;
$_.Mode;
</ScriptBlock>
You can of course add your own file types and colours for the formatting. It should now look like so:

Finally we again sign the file and update the format and run LS
Yes sir, that is cool. But we’re not done yet. Notice that the header is in the colour blue. Looks like either the first row is evaluated well in advance or the header is not printed until the first row has been evaluated. Either way it’s not pretty.
If we move to another folder with different files a bigger problem becomes manifest

The last file is a zip file but notice the color never gets reset.
Problem.
Now, I’ve tried high and low to see if i can put a script block before the table gets rendered or after the footer.
No dice.
The closest solution I got was to take advantage of the fact that functions bind before cmdlets.
So we create a function like so:
function out-default { end{ $input | &(Get-Command -Type Cmdlet Out-Default) $host.UI.RawUI.ForegroundColor=“Gray”; } }
We cut and paste this into a PowerShell window. Press enter twice to finalize it
Then we can list the files again and hope things are for the better
Excellent. The color is reset to the default.
The one for the header — I’m still looking for a workaround. Let me know if you have ideas. Hopefully these gentlemen can inform us if there is a better workaround or better yet a solution
Persisting Our Changes
Finally, we want to persist some of the changes we have made. Those made to the ps1xml are obviously persisted and will be maintained for each subsequent session. What are not are:
- The prompt
- Maintaining the current location in the title
- The custom out-default function
The question is, where do we save this? We have a number of options
- The profile of the current user
- The profile for all users
I think we will use the first option. Not everyone will like or want our callisthenics. Now where do we put these changes?
As it happens there is an excellent place we can put it: A file that is executed every time we start a new PowerShell Shell. We can get this from the following variable
$profile

Now, take note that the file may or may not exist! Before we do anything we need to do some thinking
- If the profile file does not exist, we need to create it
- It will be in a new folder, WindowsPowerShell, in the My Documents folder
- Once created, open it in notepad
- In the notepad we can then paste our custom commands.
The script itself is as follows:
if (-not (test-path $profile)) { ## Get the my document folder $dochome = “$home/My Documents” ## Change to the my documents folder cd $dochome ## Create the WindowsPowerShell folder $dochome = “$dochome/WindowsPowerShell” if (-not (test-path $dochome)){ new-item $dochome -type Directory } ## Change to the WindowsPowerShell folder cd $dochome ## Create the profile file new-item $profile -type file ## open the file in notepad for edit notepad $profile }
Now, in our conveniently opened notepad window, we paste the following commands and save the file
function prompt { “$ “ $host.UI.RawUI.WindowTitle = $(get-location) } function out-default { end{ $input | &(Get-Command -Type Cmdlet Out-Default) $host.UI.RawUI.ForegroundColor=“Gray”; } }
Excellent. Now we restart our shell and see if we’re in business
Whoops! Remember our security settings! All scripts we run MUST be signed! We can’t be taking changes So let’s invoke our good old script using this nifty command and sign it ourselves
sign-file.ps1 $profile
Excellent. Now let us restart our shell

Let’s do a file listing
Woot woot!
In summary we have done the following:
- Modified our prompt
- Found a better way to display our location
- Learned how to securely execute scripts
- Learned how to sign our scripts
- Changed the format of the date
- Changed the format and caption of the file sizes
- Changed the colouring of different file types
- Learnt some neat tricks about PowerShell
- Persisted our changes across PowerShell sessions
Whew! That’s quite enough for this session. More later.
To save you time, you can download my script files. Remember to sign them yourself!
Have fun!














Modified

Lee says:
Added on November 1st, 2006 at 7:35 amThis is a really excellent post!
Some comments you might be interested in:
- The date is formatted explicitly to be locale sensitive, as it uses the local machine’s idea of the “short date pattern.” The only Kenya-specific locale that .Net knows about is sw-KE, and unfortunately it thinks that the short date pattern for that locale is mm/dd/yyyy. If you use en-GB as the current locale, for example, you will see the date as dd/mm/yyyy. A neat way to play with locales in PowerShell is this: http://blogs.msdn.com/powershell/archive/2006/04/25/583235.aspx
- Location of formatting edits. For this, it is a bit better to add your formatting definitions to your own file, and have that override the ones we wrote. Here are a couple of good resources for that: http://search.live.com/results.aspx?q=site:leeholmes.com descript.ion — explicitly, http://www.leeholmes.com/blog/DESCRIPTIONSupportInMonadPart3.aspx.
- Selecting an execution policy. For most, RemoteSigned is another very reasonable tradeoff between security and ease of use.
- File sizes: a couple of neat numeric constants that you might not be aware of: (x)kb, (x)mb, (x)gb — like “if( $_.Length -gt 1gb)”
- Colourizing: Check out these posts: http://mow001.blogspot.com/2006/01/colorized-msh-ls-replacement.html, http://groups.google.nl/group/microsoft.public.windows.server.scripting/browse_thread/thread/4a7dc594a82fb1b7/548fbc533ded1ada?lnk=st&q=Colorize Get-Childitem Output&rnum=1&hl=nl#548fbc533ded1ada
- Creating a profile. If you know that the profile doesn’t exist, you can just run “new-item -type file $profile -force”
Again, excellent post!
Lee
Luc says:
Added on November 7th, 2006 at 4:16 pmNeat, I’m using colorized dir output now too
The one thing I changed is how the output color is reset: I do it in the prompt function, because I already had one of those in my profile anyway. It now goes like this, the first two lines set the color:
function prompt
{
$host.UI.RawUI.ForegroundColor=“Blue”
$host.UI.RawUI.BackgroundColor=”White”
$p = get-location
$host.ui.RawUI.WindowTitle = “PowerShell [” $p “]”
if($host.UI.RawUI.CursorPosition.X -ne 0){ Write-host “” } # force newline if necessary
Write-host (”PS ” ” ” * ($host.ui.RawUI.BufferSize.Width-3)) -nonewline -foregroundcolor $host.ui.rawui.backgroundcolor -backgroundcolor $host.ui.rawui.foregroundcolor
return “” $p “> ”
}
Luc says:
Added on November 7th, 2006 at 4:23 pm?? Your site ate all the plus signs in the code in my comment:
“PowerShell [” PLUS $p PLUS “]”
Write-host (”PS ” PLUS ” ” * ($host.
return “” PLUS $p PLUS “> ”
parth says:
Added on November 9th, 2006 at 10:57 pmExecllent post. I was searching for a good article on customizing my powershell and this one has proved to be the best so far. Thanks a lot.
Gathunuku says:
Added on November 10th, 2006 at 1:06 pmAllow me to introduce you to Linux, where all this was available from day one as standard. The best part, it costs nothing.
Rad! (blog author) says:
Added on November 11th, 2006 at 6:48 pm@Gathunuku
That may be true, but irrelevant. Who had it first is not the issue — the fact of the matter is the PowerShell implementation is much more powerful, as I will demonstrate in subsequent articles.
And for the record in addition to my day to day workhorse of Windows 2003 Server R2 i also use a Debian box
Karl Prosser says:
Added on November 14th, 2006 at 11:58 pmVery cool to see you have caught the powershell bug. If you are spending alot of time in powershell, i think my Powershell Analyzer will be of a great aide to you..
Karl
DIL23 says:
Added on December 17th, 2006 at 5:43 amWow! Great article
All a bit daunting for a mere dotBAT man like myself! but your article has probably saved me from suicide - esp signatures - would never have got that one! - so would have been running with no restrictions - suicide for my PC or bashed my head off a wall - suicide for me!
All the best
DIL23
:)
Sr.jilarious says:
Added on December 17th, 2006 at 6:07 amI played around with doing colorized output for ls and, by modifying the script from mow, I got it working pretty well. It keeps the first part of the listing the original color and changes the color back to the original after done doing the listing. It’s not perfect though, if you do a format-list on the output from this script the first line of the first item isn’t overwritten. Hope you enjoy.
- Sr.Jilarious
# LS.MSH
# Colorized LS function replacement
# /\/\o\/\/ 2006
# http://mow001.blogspot.com
#
# Modified by Sr.Jilarious 2006
$dir = “.”
$origFg = $host.ui.rawui.foregroundColor
$origBg = $host.ui.rawui.backgroundColor
$items = get-childitem $dir
# depending on whether we are right up against the edge of the buffer we
# need to adjust the cursor position by a different amount
$delta = $host.ui.rawui.BufferSize.Height - $host.ui.rawui.CursorPosition.Y
if( $delta -gt 7 )
{
$d = 7
}
else
{
$d = $delta-2
}
#Draw the item with no color so we can then go back over it
$pos = $host.ui.rawui.CursorPosition
if( $items.Length -gt 1 )
{
$items[0]
}
else
{
$items
}
#adjust cursor pos so that overwrite the first (non-colored) entry
$pos.Y = $pos.Y $d
$host.ui.rawui.CursorPosition = $pos
foreach( $Item in $items)
{
Switch ($Item.Extension)
{
“.Exe” {$host.ui.rawui.foregroundColor = “Green”}
“.Bat” {$host.ui.rawui.foregroundColor = “Green”}
“.ps1″ {$host.ui.rawui.foregroundColor = “Magenta”}
“.msh” {$host.ui.rawui.foregroundColor = “Magenta”}
“.mp3″ {$host.ui.rawui.foregroundColor = “Cyan”}
“.flac” {$host.ui.rawui.foregroundColor = “Cyan”}
“.mpg” {$host.ui.rawui.foregroundColor = “Yellow”}
“.avi” {$host.ui.rawui.foregroundColor = “Yellow”}
“.ogm” {$host.ui.rawui.foregroundColor = “Yellow”}
“.wmv” {$host.ui.rawui.foregroundColor = “Yellow”}
“.mkv” {$host.ui.rawui.foregroundColor = “Yellow”}
“.zip” {$host.ui.rawui.foregroundColor = “Red”}
“.rar” {$host.ui.rawui.foregroundColor = “Red”}
“.arj” {$host.ui.rawui.foregroundColor = “Red”}
“.cmd” {$host.ui.rawui.foregroundColor = “Green”}
Default {$host.ui.rawui.foregroundColor = $origFg}
}
if ($Item.Mode.StartsWith(”d”)) {
$host.ui.rawui.foregroundColor = “Blue”
}
$Item
}
$host.ui.rawui.foregroundColor = $origFg
$host.ui.rawui.backgroundColor = $origBg
Iliyan says:
Added on March 27th, 2007 at 7:13 pmGathunuku, you are completely wrong, my friend.
This, what PowerShall can do, was never available, and will not be available soon. And if you open your mind just an inch, you will see that the world evolves.
God, I hate such blind Linux guys…
And, Rad!, thanks for the great article!
kylehase says:
Added on August 15th, 2007 at 3:16 amGreat writeup. Perhaps someone here will know the answer to this question.
I’m trying to create a shell extension that will add a “PowerShell here” option to the context menu. So far I have a reg file that looks like this:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\shell\PowerShell]
@=”PowerShell here”
[HKEY_CLASSES_ROOT\Directory\shell\PowerShell\command]
@=powershell.exe -NoExit -Command “cd ‘%1′”
[HKEY_CLASSES_ROOT\Drive\shell\Command]
@=”PowerShell here”
[HKEY_CLASSES_ROOT\Drive\shell\PowerShell\command]
@=powershell.exe -NoExit -Command “cd ‘%1′”
This same reg script works for my other applications such as “Cygwin here” but has two problems with PowerShell.
1) The PowerShell opens in the parent directory of the selected directory as opposed to the selected directory itself.
2) The PowerShell opens in the plain old cmd window without some of the cool features available when powershell is opened from the start menu shortcut such as the colors and `right click = paste`.
Eric-L says:
Added on October 28th, 2007 at 3:54 am1) You have a smart quote in your command. It should be:
powershell.exe -NoExit -Command “cd ‘%1′”
To open a powershell in the current directory, add this to the context menu for any file:
powershell.exe -NoExit -Command {set-location “%1″}
2) Open a powershell and set the default properties. The one you launch from the Start menu is a shortcut with custom properties.
Eric-L says:
Added on October 28th, 2007 at 4:12 amOops, this site converts to smart quotes. That gave me a problem when I tried pasting the command into ContextEdit so I thought that was the issue. Your problem is actually with your formatting. It should be like this:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\shell\PowerShell]
@=”PowerShell”
[HKEY_CLASSES_ROOT\Directory\shell\PowerShell\command]
@=”powershell.exe -NoExit -Command \”cd ‘%1′\”"
jtsm says:
Added on November 21st, 2007 at 10:23 pmHi There,
I have loaded up powershell 2, but find that i cannot get the coloured extensions to work now using your script modifications. has there been some changes in version 2 that will break this?
Thanks and great post.
jtsm
Rob says:
Added on March 5th, 2008 at 5:06 amI noticed that the directory name from an ls -r command takes on the color of the first file listed under it. Any idea how to fix that up?
I am fairly new to PS (but a long time C# and Delphi developer) and an avid CMD user. PS rocks - I hardly open CMD anymore except for maybe running an xcopy
BTW: Great article and very useful information!!
Tony says:
Added on June 26th, 2008 at 8:37 amGreat article! Thanks for the clear and detailed steps with screenshots. Very helpful!
Instead of directly modifying the default ps1xml file, would it be better to use our own customized ps1xml?
We can just make a copy of the default ps1xml and work on the copy, adding all the customization stuffs. Then call Update-FormatData -PrependPath … to tell the shell to use our customized types or formats.
In addition, we can put the above Update-FormatData command in $profile so that PowerShell will remeber to use the customized files.