The PowerShell Iron Scripter: My solution to prequel puzzle 3

I've been working through the Iron Scripter 2018 prequel puzzles which can be found on PowerShell.org's website. In puzzle 3, you're asked to create a "reusable PowerShell artifact". To me, that almost always means a PowerShell function. One requirement is to pull information from the PowerShell.org RSS feed. Invoke-RestMethod which was introduced in PowerShell version 3.0 is the easiest way to accomplish that task.

You're also asked to display the returned information in a way that allows the user to select an item from the feed. My first thought was to use Out-GridView to build a GUI based menu for selecting the item, but that won't work in PowerShell Core since it doesn't have the same GUI based commands as Windows PowerShell. This means I'll have to build a text based menu in PowerShell. Luckily, I've recently read The PowerShell Scripting and Toolmaking Book by Don Jones and Jeff Hicks which has some examples of building text based menus, although I designed mine a little differently than theirs. Consuming content from others often sparks your own creativity.

prequel3-ironscripter2a.jpg

The next part was actually the most challenging. Give the user the ability to display the selected item from the RSS feed in either their browser or in plain text.

prequel3-ironscripter3b.jpg

While the browser part was easy enough and the text part was also easy for Windows PowerShell, returning the content of the feed in text for PowerShell Core was more difficult. This was due to the ParsedHtml property not existing for Invoke-WebRequest in PowerShell Core.

One of the drawbacks to using Invoke-WebRequest in Windows PowerShell is that it's dependent on Internet Explorer, although that isn't the case in PowerShell Core running on a Windows system.

1Invoke-WebRequest -Uri https://powershell.org/

prequel3-ironscripter1a.jpg

Making it work for any RSS feed was a bonus and mine does work against a couple of different sites I tested it against (PowerShell.org and my mikefrobbins.com blog site). Your results may vary when using it for RSS feeds on other websites.

 1#Requires -Version 3.0
 2function Invoke-MrRssFeedMenu {
 3
 4<#
 5.SYNOPSIS
 6    Retrieves information from the specified RSS feed and displays it in a selectable menu.
 7
 8.DESCRIPTION
 9    Invoke-MrRssFeedMenu is an advanced function that retrieves information from the specified RSS feed, displays it in
10    a selectable menu and then prompts the user to return the information about the selected item in a browser or as text.
11
12.PARAMETER Uri
13    Specifies the Uniform Resource Identifier (URI) of the RSS feed to which the web request is sent. This parameter supports
14    HTTP, HTTPS, FTP, and FILE values. The default is the PowerShell.org RSS feed.
15
16.EXAMPLE
17     Invoke-MrRssFeedMenu
18
19.EXAMPLE
20     Invoke-MrRssFeedMenu -Uri http://mikefrobbins.com/feed/
21
22.INPUTS
23    None
24
25.NOTES
26    Author:  Mike F Robbins
27    Website: http://mikefrobbins.com
28    Twitter: @mikefrobbins
29#>
30
31    [CmdletBinding()]
32    param (
33        [ValidateNotNullOrEmpty()]
34        [string]$Uri = 'https://powershell.org/feed/'
35    )
36
37    $Results = Invoke-RestMethod -Uri $Uri
38
39    $MenuItems = for ($i = 1; $i -le $Results.Count; $i++) {
40        [pscustomobject]@{
41            Number = $i
42            Title = $Results[$i-1].title
43            PublicationDate = ([datetime]$Results[$i-1].pubDate).ToShortDateString()
44            Author = $Results[$i-1].creator.InnerText
45        }
46    }
47
48$PostMenu = @"
49    $($MenuItems | Out-String)
50    Enter the number of the article to view
51"@
52
53    do {
54        Clear-Host
55        [int]$Post = Read-Host -Prompt $PostMenu
56    }
57    while (
58        $Post -notin 1..$Results.Count
59    )
60
61    Clear-Host
62
63$OutputMenu = @"
64Number DisplayOption
65------ ----------------
66     1 Default Browser
67     2 Text
68     3 Quit
69
70Enter the number to display the article in the desired option
71"@
72
73    Clear-Host
74
75    do {
76        [int]$OutputType = Read-Host -Prompt $OutputMenu
77    }
78    while (
79        $OutputType -notin 1..3
80    )
81
82    switch ($OutputType) {
83        1 {
84            Start-Process -FilePath ($Results[$Post-1]).link
85            Clear-Host
86            break
87          }
88        2 {
89            Clear-Host
90            ($Results[$Post-1]).InnerText.ToString() -replace '\t.*|\n|<[^>]*>|/\s\s+/g|^\s+|\s+$'
91            break
92          }
93        3 {
94            break
95          }
96    }
97
98}

I use a regular expression to parse the text if PowerShell Core is used. That regular expression could probably use some more tweaking and I'll post an update if I decide to work on it more. The other things that could be added are error handling and verbose output.

prequel3-ironscripter4a.jpg

One of the rules I've broken in the PowerShell function shown in this blog article is that a function should do one thing and do it well. More about that rule can be read about in this Hey, Scripting Guy! Blog article. This function could be broken up into multiple functions to better adhere to that rule.

The function shown in this blog article can be downloaded from my IronScripter repository on GitHub. It can also be installed from the PowerShell Gallery.

See the scripting games category on this blog site to view my other Iron Scripter entries along with my previous scripting games entries. Other recommended reading: PowerShell Core 6.1 Web Cmdlets Roadmap blog article by Microsoft MVP Mark Kraus.

Learn to write award winning PowerShell functions and script modules from a former winner of the advanced category in the scripting games and a multiyear recipient of both Microsoft’s MVP and SAPIEN Technologies’ MVP award at the PowerShell + DevOps Global Summit 2018.


Update - February 4th, 2018

I modified the switch statement to use the same code regardless if Windows PowerShell or PowerShell Core is used.

µ