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.

Invoke-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.

#Requires -Version 3.0
function Invoke-MrRssFeedMenu {

<#
.SYNOPSIS
    Retrieves information from the specified RSS feed and displays it in a selectable menu.

.DESCRIPTION
    Invoke-MrRssFeedMenu is an advanced function that retrieves information from the specified RSS feed, displays it in
    a selectable menu and then prompts the user to return the information about the selected item in a browser or as text.

.PARAMETER Uri
    Specifies the Uniform Resource Identifier (URI) of the RSS feed to which the web request is sent. This parameter supports
    HTTP, HTTPS, FTP, and FILE values. The default is the PowerShell.org RSS feed.

.EXAMPLE
     Invoke-MrRssFeedMenu

.EXAMPLE
     Invoke-MrRssFeedMenu -Uri http://mikefrobbins.com/feed/

.INPUTS
    None

.NOTES
    Author:  Mike F Robbins
    Website: http://mikefrobbins.com
    Twitter: @mikefrobbins
#>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [string]$Uri = 'https://powershell.org/feed/'
    )

    $Results = Invoke-RestMethod -Uri $Uri

    $MenuItems = for ($i = 1; $i -le $Results.Count; $i++) {
        [pscustomobject]@{
            Number = $i
            Title = $Results[$i-1].title
            PublicationDate = ([datetime]$Results[$i-1].pubDate).ToShortDateString()
            Author = $Results[$i-1].creator.InnerText
        }
    }

$PostMenu = @"
    $($MenuItems | Out-String)
    Enter the number of the article to view
"@

    do {
        Clear-Host
        [int]$Post = Read-Host -Prompt $PostMenu
    }
    while (
        $Post -notin 1..$Results.Count
    )

    Clear-Host

$OutputMenu = @"
Number DisplayOption
------ ----------------
     1 Default Browser
     2 Text
     3 Quit

Enter the number to display the article in the desired option
"@

    Clear-Host

    do {
        [int]$OutputType = Read-Host -Prompt $OutputMenu
    }
    while (
        $OutputType -notin 1..3
    )

    switch ($OutputType) {
        1 {
            Start-Process -FilePath ($Results[$Post-1]).link
            Clear-Host
            break
          }
        2 {
            Clear-Host
            ($Results[$Post-1]).InnerText.ToString() -replace '\t.*|\n|<[^>]*>|/\s\s+/g|^\s+|\s+$'
            break
          }
        3 {
            break
          }
    }

}

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.

µ