Create an HTML PowerShell Help Page

Yesterday I posted an article about getting the online url for a cmdlet help topic. Today I want to demonstrate how we might take advantage of this piece of information.

Since the link is already in the form of a URL, wouldn’t it make sense to put this in an HTML document? At first glance, you might take the command from yesterday and pipe it to ConvertTo-HTML.

Get-Command -CommandType cmdlet | Get-Help |
Select Name,Synopsis,@{Name="URI";Expression={
($_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}).uri}} |
Where {$_.URI} | ConvertTo-HTML -Title "Help Links" | Out-File c:\work\pshelp.htm

This code will work just fine. But if you look at the resulting file, we don’t have a link. I suppose it would be nice if ConvertTo-HTML could auto-detect URLs and automatically add a link, but it looks like we’ll have to do it. We could probably use a custom hash table to insert the HTML Anchor tags so here’s the first attempt:

Get-Command -CommandType cmdlet | Get-Help | Where {$_.RelatedLinks} |
Select Name,Synopsis,@{Name="URI";Expression={
#add the link tags as part of the output!
$link=$_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}
if ($link.uri) {
Write "$uri"
else {
#no link so write a null
write $Null
}} | Where {$_.URI} | ConvertTo-HTML -Title "Help Links" |
Out-File c:\work\pshelp.htm

One additional change I made was to filter out cmdlets with no related links. Then in the expression scriptblock I can create a new value based on the URI value if it exists. But there is a problem with this, which you’ll see immediately if you run this code. ConvertTo-HTML sees the value of my new URI property and escapes the < and > characters.

That doesn’t help. But this is where the fact that ConvertTo-HTML only creates HTML code because we can parse the code and do a simple replace. Let me jump ahead and pull part of the finished dessert from the oven.

Function Convert-HTMLEscape {

<# convert < and > to < and >
It is assumed that these will be in pairs


Param (

Process {
foreach ($item in $text) {
if ($item -match "<") {
<# replace codes with actual symbols This line is a shortcut to do two replacements with one line of code. The code in the first set of parentheses revised text with "<". This normally gets written to the pipeline. By wrapping it in parentheses it tells PowerShell to treat it as an object so I can then call the Replace() method again and add the >.
else {
#otherwise just write the line to the pipeline
} #close process

} #close function

This function takes string input, presumably from ConvertTo-HTML and replaces the HTML escapes with the “real” characters. This I can use to write to the file.

...| Where {$_.URI} | ConvertTo-HTML -Title "Help Links" | Convert-HTMLEscape | Out-File c:\work\pshelp.htm

One of the reasons I put this in a function is to make it re-usable for future projects where I might need to escape these characters again. We’re getting closer. One last thing before we ice the final dessert: I have been using an expression to get all cmdlet help. But I like flexibility. What if tomorrow I only want a page with New* cmdlets from PowerCLI? So once again, I took my code that works just fine from a prompt into a more flexible and re-usable function.

Function Get-HelpUri {


[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a cmdlet name",

Process {
Write-Verbose "Processing $name"
Get-Help $name | Where {$_.RelatedLinks} |
Select Name,Synopsis,@{Name="URI";Expression={
#add the link tags as part of the output!
$link=$_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}
if ($link.uri) {
Write "$uri"
else {
#no link so write a null
write $Null
} #close process

} #close function

This will write a custom object with the cmdlet name, synopsis and URI property with the HTML code. I still need to convert to HTML and then fix the tags but I can verify it works.

PS C:\> get-command new-object | get-helpuri | ConvertTo-Html |Convert-HTMLEscape


Name Synopsis URI
New-Object Creates an instance of a Microsoft .NET Framework or COM object.

The icing is to include some style via a CSS file. Here’s a short script on how I might build a file for all of the cmdlets in my current session.

Write-Host "Building cmdlet help report" -ForegroundColor Green

#the file to create

#be sure to change the path to the CSS file if you want to use it

#optional image

#some pre content

Cmdlet Online Help


#some post content

Help for cmdlets found on $env:computername on $(Get-Date)

#a title for the report
$Title="Cmdlet Help"

<# Get all cmdlets in the current session, send them to the Get-HelpURI function to parse out help URLS, filter out those without a link, pass the remaining to the Convertto-HTML to generate HTML code which is piped to my function to replace < with > and the final HTML code is piped to
a file.
Get-Command -CommandType cmdlet | Get-HelpURI | Where {$_.URI} |
ConvertTo-Html -PreContent $PreContent -PostContent $postContent -Title $Title -cssUri $cssPath |
Convert-HTMLEscape | Out-File -FilePath $file -Encoding ASCII

Write-Host "Finished. See $file for the results" -ForegroundColor Green

And here’s the final result in Internet Explorer.

Ok, maybe you don’t have a compelling need for this exact script, but I hope you picked up on the importance of writing code for re-use and taking advantage of the pipeline.

If you’d like to try my code out for yourself, including the graphic and CSS file, download this zip file.

Thursday Treat – A PowerShell Word Find Game

Now for something completely different but hopefully a little fun. I’m a big fan of word games and puzzles. Tim Bolton has assembled a few PowerShell themed crosswords, which you can find on his blog. I’ve always like word finds so I put together a pretty simple one using 16 common PowerShell cmdlet names.

You can find cmdlet names forwards, backwards and diagonally. I used full cmdlet names like Where-Object, but omitted the dash, so you would be looking for WhereObject. By the way, that is not one of the names in the puzzle. If people like this I might create some more using a larger grid and perhaps extending the content to include aliases, concepts or anything else that seems fun.

To play, I guess you could print this page. Or you can download this PDF document. The puzzle is on Page 1 and the solution is on Page 2. Enjoy!

Friday the 13 Script Blocks

In celebration of Friday the 13th and to help ward off any triskaidekaphobia I thought I’d offer up 13 PowerShell scriptblocks. These are scriptblocks that might solve a legitimate business need like finding how long a server has been running to the more mercurial such as how many hours before I can go home.

A scriptblock is any small piece of PowerShell code enclosed in a set of curly braces. I say small, but there’s really no limit. You can even pass parameters to a script block. In this way they are like functions but without the function key word. Save the script block to a variable and when you want to execute, use the invoke operator, the & character. For example, here’s one of my luck 13 script blocks.
[cc lang=”powershell”]
#1. Get running services
$running={Param ([string]$computername=$env:computername) get-service -computername $computername |
where {$_.status -eq “Running”}}
To invoke it:
[cc lang=”powershell”]
PS C:\> &$running
Because this particular script block takes a computername as a parameter I could also do this:
[cc lang=”powershell”]
PS C:\> &$running Server01
I won’t post the complete script here, but these are the goodies:
#1. Get running services
#2. Get logical disk information
#3. Get day of the year
#4. Get top services by workingset size
#5. Get OS information
#6. Get system uptime
#7. get a random number between 1 and 13
#8. How old are you?
#9. get %TEMP% folder stats
#10. Get event log information
#11. Get local admin password age in days
#12. Get cmdlet summary
#13. How much time before I can go home?

I wrote many of the script blocks so that you could use them in your own functions and scripting and tried to make them re-usable as possible. They have no error handling and assume you have all the necessary permissions. Think of them as quick and dirty PowerShell snippets. The script file is simply defines the 13 script blocks. After each one is a commented example of how you might run it.

Again, I’m not promising anything life-changing but I hope you’ll have fun with them today. Download the script file 13ScriptBlocks.ps1.

Order Managing Active Directory with Windows PowerShell: TFM – Finally!

Yes, its finally true. You can finally get your hands on Managing Active Directory with Windows PowerShell: TFM. The book is being printed so you can get your copy today. You can order it today at in both print and ebook format. Or if you prefer the best of both worlds get both as a bundle.

Table of Contents

  1. PowerShell Crash Course
  2. PowerShell Extras
  3. Managing Local Computer Accounts
  4. Managing Local Groups
  5. Managing Active Directory with PowerShell Fundamentals
  6. Managing Active Directory Users
  7. Active Directory Password Management
  8. Managing Active Directory Contacts
  9. Managing Active Directory Groups
  10. Managing Active Directory Computer Accounts
  11. Managing Organizational Units and Containers
  12. Managing Group Policy
  13. Active Directory Security and Permissions
  14. Managing Active Directory with WMI and PowerShell
  15. Using the Active Directory PSDrive Provider
  16. Managing Active Directory Infrastructure
  17. (Appendix A) Managing Active Directory with PowerGUI

You can also download a sample chapter. This title is also available at

You don’t have to wait for Microsoft before you can begin managing Active Directory with PowerShell. You can start today.

Online PowerShell Training

Time is running out to register for SAPIEN Technologies’ online PowerShell Fundamentals training. The first class starts June 17. The class is essentially self-study with materials supplied by SAPIEN including an e-book version of Windows PowerShell: TFM 2nd ed. I’ll meet online twice a week via Webex with students to review the material. Students will also have access to a private class discussion forum at

Registration closes June 13th so hurry now and check out

The course material has some pretty specific requirements to ensure a high quality experience so please verify system compatibility before registering.