Microsoft publishes a page which contains all the keyboard shortcuts for Outlook (and other Office products, but I needed Outlook in this instance, so we'll stick with that). It's located here: https://support.microsoft.com/en-us/office/keyboard-shortcuts-for-outlook-3cdeb221-7ae5-4c1d-8c1d-9e63216c1efd. The problem is that it's just HTML, so while it's good for a quick reference, it's not something you can easily copy and paste into another format, like say Excel, to whip up a nice handy table you can print out or format some other way.
PowerShell to the rescue!
Taking a quick look at the page source shows that all the shortcuts are in HTML tables. So all we really need to do is some simple HTML parsing and we can extract all the data we need into a hashtable, then convert it to CSV or whatever else we want.
Long story short, here's the basic script.
$outlookShortcutsHelpDocURL = 'https://support.microsoft.com/en-us/office/keyboard-shortcuts-for-outlook-3cdeb221-7ae5-4c1d-8c1d-9e63216c1efd?ui=en-US&rs=en-US&ad=US'
$windowsShortcutsElementRegEx = '<div.*?id="PickTab-supTabControlContent-1".*?>.*?</div>.*?<div.*?id="PickTab-supTabControlContent'
$shortcutTableRegEx = '<table.*?>.*?</table>'
$shortcutItemRegEx = '<tr>.*?<td>(.*?)</td>.*?<td>(.*?)</td>.*?</tr>'
$regexMatchOptions = [System.Text.RegularExpressions.RegexOptions]::Singleline + [System.Text.RegularExpressions.RegexOptions]::IgnoreCase
# ---------------------------------------------------------------------------------
$page = Invoke-WebRequest -Uri $outlookShortcutsHelpDocURL -UseBasicParsing
$windowsShortcuts = [Regex]::Matches($page.Content, $windowsShortcutsElementRegEx, $regexMatchOptions)
$shortcutTables = [Regex]::Matches($windowsShortcuts[0], $shortcutTableRegEx, $regexMatchOptions)
$shortcuts = $shortcutTables | % {
[Regex]::Matches($_, $shortcutItemRegEx, $regexMatchOptions) | % {
if ($_.Captures -and $_.Captures.Groups -and $_.Captures.Groups.Count -ge 3) {
$shortcut = (($_.Captures.Groups[2].Value -ireplace '<.+?>','') -ireplace '\s+',' ').Trim()
$description = (($_.Captures.Groups[1].Value -ireplace '<.+?>','') -ireplace '\s+',' ').Trim()
@{$shortcut=$description}
}
}
}
$shortcuts
So what's going on above? Well, above the line (the #--------- part) are some variables I'm defining to hold the URL and Regular Expressions (RegEx) that are extracting the data from the HTML. I decided to do it this way so it's easier to edit in the future when Microsoft changes the page formatting. The next big part is the Invoke-WebRequest. That fetches the live version from Microsoft. Notice the "UseBasicParsing" flag. That tells PowerShell NOT to use the Internet Explorer parser. IE parsing is a bit of a mess nowadays and we don't need a full parser anyway since regular expressions will be doing all the heavy lifting.
There are three sequences of RegEx happening on the page output. First, using $shortcutTableRegEx, we're looking for the section that contains Windows shortcuts (Outlook for Windows Desktop). Second, we're extracting all the tables inside that section with $shortcutTableRegEx. Then finally we are getting individual shortcut items with $shortcutItemRegEx. Then we do a replace to remove any extra HTML or whitespace and then Trim() to remove leading or trailing whitespace. Finally, the item pair is output into a hashtable with the @{} section. When applied over the "for-each" loop, you get one big hashtable array, like below.
Now, while that's easy to read on the screen, it's not a great format for trying to export elsewhere, such as to CSV. PowerShell doesn't really handle converting arrays of hashtables very well for exports. While it unrolls nicely to the screen, it's actually a pretty complex object. So to export it, we need to help it along a bit by extracting the data from the embedded arrays in the array we created, cast that to a PSCustomObject, then send it to the export cmdlet.
$shortcuts | % { [PSCustomObject]@{"Shortcut"=[String]$_.Keys;"Description"=[String]$_.Values} } | Export-Csv shortcuts.csv -NoTypeInformation
You'll notice I'm actually still using a hashtable which is what is being cast to PSCustomObject. This hashtable doesn't format to the screen as nicely (by default), but does work well for exporting. The reason it doesn't look nice is because of how Format-Table works. If you sent this to Format-Table, it will try to show the first column's entire width. The columns are very wide, so it ends up just showing one column. It does this even with the AutoSize parameter set. We'd have to customize Format-Table's output to get it to look nice, which is too much work. ;-)
So anyway, since we generated a CSV now, this is what it looks like if we open the CSV in Excel.