Monday, October 25, 2021

Saving PowerPoint Deck as a PDF - with PowerShell 7

 This week, I got into a twitter discussion about a presenter posting their slides for delegates. My personal preference is to always share the slides. Often, especially when I was working for an employer, I'd share a PDF of the file rather than the slides. Some decks had had a LOT of effort put in (some by inhouse graphics guys) and giving those animations away was sub-optimal for IP reasons. 

My approach was to post whatever material was appropriate to a DropBox share - and let people have that. I created a unique Bit.Ly short URL, and put that on the first few and final slides. Since the shortend URL never changed, I could put whatever content was relevant - letting attendees know this would all disappear in a few week's time.

What I used to do was to run a short little PowerShell script, during the final development of the deck. If, like SO many presenters, I made a change in the speaker's lounge just before going on - I could run the script just before unplugging and heading to the talk. I could just share and manage the content.

Converting your PowerPoint deck to a PDF is straightforward - I blogged about how to convert a Powerpoint deck into a PDF using PowerShell a long time ago.  The script uses the PowerPoint objects that are part of Office and which Office setup conveniently stores in the .NET Framework's Global Assembly Cache (the GAC). 

After the Twitter conversation, I dusted off this script and to my surprise, it did not work. And the reason is that PowerShell 7 uses .NET and not The .NET Framework. The former does not make use of the GAC, so when I tried to load the appropriate assembly I got this error:

PS C:\Foo> Add-Type -AssemblyName

Add-Type: Cannot find path 'C:\Foo\' because it does not exist.

After a few minutes contemplation I realised what the issue was. And by using Void Tools most excellent SearchEvereything product, I found the assembly at the snappy location of: C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\\Microsoft.Office.Interop.PowerPoint.dll.

So now I know the location, I could do this to save a single file:

# Define the files
$PPTFile = 'C:\foo\test.pptx'
$PDFFile = 'D:\Dropbox\AAA-TFL Presententations LIVE\test.pdf'
# Add the DLL
$DLLFile = 'C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\' +
# Add the type
Add-Type -Path $DLLFile
# Create save as PDF option
$SaveOption = [Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType]::ppSaveAsPDF
# Open the PowerPoint Presentation
$PPT = New-Object -ComObject "PowerPoint.Application"
$Presentation = $PPT.Presentations.Open($PPTFile)
# Save it as a PDF and close the presentation
# Close PPT and kill the process to be sure
Get-Process 'POWERPNT' | Stop-Process

This fragment adds in the necessary DLL, opens the presentation from wherever I have it stored today, and then saves the file as PDF into the DropBox folder. 

I kind of thought that the Quit() method would kill PowerPoint but in my testing, the process hung around hence killing the process at the end of the fragment. Depending on how adventurous you wish to be, you can always wrap this code to get all the PPT files in one folder and save them away. 

Tuesday, October 19, 2021

Patching and PowerShell 7

 Many of you reading that blog know I'm a big supporter of PowerShell 7. I hope that many of you share my enthusiasm. After writing several books on PowerShell, and two on PowerShell 7, I find it a pretty good product.

One of the challenges which PowerShell brings to the enterprise is updates. Like most software products, there are bugs and vulnerabilities in PowerShell. I was reminded again of this fact reading an article from my Powershell Paper.Li paper. Today the top article comes from Bleeping Computer:

The team do a great job in updating the code as soon as the vulnerabilities are found. In some (many) cases, the issue is not in PowerShell itself, but in one of the components, such as .NET. Once the team releases an update, you need to ensure that the update is rolled out everywhere you use PowerShell 7.

The method you use to keep PowerShell 7 up to date depends on how you installed PowerShell 7 in the first place. And here you have (at least) 3 options:

  • Use Install-PowerShell.ps1 - this is a script you download from GitHub which enables you to install the latest production version of PowerShell, the latest next version Preview and (for the very brave) today's daily builds. If you use this, then you must manually update the software yourself. I love this as I am in control!
  • Use the Microsoft store - you get the released version directly from the store. This should automatically keep your version up to date as the PowerShell team. At present, there is no Store application for Windows Server. You can also get the Preview version from the store (although I do not know how that plays if you also have the released version installed). 
  • Use a third-party repository - you could download PowerShell directly from Github and install it (and manually update it as new versions get pushed out). You can also use Chocolatey although technically that is not supported.
So you have options. Personally, I update the daily build once or twice a week and update the preview and production versions once I get a warning about an updated version the next time I start PowerShell.  

So no matter how you install PowerShell 7, make sure you have a patch strategy in place. And if you have read this far, make sure you have installed 7.1.5 (or 7.0.8 if you are still on 7.0).

Sunday, August 08, 2021

A Great Book On Windows Terminal


Review of Will Fuqua’s book “Windows Terminal Tips, Tricks, and Productivity Hacks”


I have had the pleasure of reading through this recently published book (Smile.Amazon.Com). This is a timely book, full of tips and tricks for using the new Windows Terminal.

Ever since Microsoft got into the OS business, we’ve had a console or shell built into the OS. The earliest,, gave way to cmd.exe with NT 3.1. And later came Windows PowerShell, the PowerShell ISE and of course PowerShell 7 with VS Code. And now we have Windows Terminal.

As the book clarifies, there really are two components at work when you use tools like PowerShell. The first is the shell, with the second being the terminal. As the book makes clear, the terminal is, essentially, “what you see” when you use a command-line tool. The terminal renders any text, draws any UI, and accepts kb/mouse input. The terminal sends input to the shell for the actual processing. The shell then processes the input and returns the results back to the terminal to display.

When you use cmd.exe or PowerShell, conhost.exe is the terminal with the command prompt or Windows Powershell/PowerShell 7 as the shell.  The actual shell does not have a UI as such – it gets input from and sends output to the terminal. It is important to separate the two.

Conhost.exe is really pretty primitive – it works and does the job but could do so much more. Which is a thought that has led to the development of a new, open-source, cross-platform terminal supporting just about any shell, including the shells in Linux distributions you can run under the Windows Subsystem for Linux (WSL2).  The new windows terminal is not just a re-write of conhost.exe, but is so much more!

As a PowerShell user, I traditionally used PowerShell via either the command line (i.e. pwsh.exe, cmd.exe, powershell.exe) or most often via VS Code (and some ISE).  The tool I chose at any given time reflected what I was about to do. I use a GUI to develop and test scripts. And sometimes to run code. On the other hand, when I only need to run code ( for example, my Get-TodayInHistory.ps1 script ( that helps me pick music to play today), I chose the console as my terminal.

The book begins with a look at both what the terminal is and how you install it, followed by a great chapter on the UI. If you are going to be using the new terminal, Chapter 2 has several important key sequences you need to work into your muscle memory.

In the second section, the book looks at how you can configure Windows Terminal and the shells you use. The book contains lots of great tips for using the terminal and PowerShell and WSL2 Ubuntu via Windows Terminal.

For the hardcore developer, the final section in the book looks at how you can use Windows Terminal in development. The book looks at using GIT and GitHub and building web applications (with React) and  REST APIs. The book finishes with a look at connecting to a remote host and managing hosts in the cloud by using Azure Cloud Shell or Google Cloud shell in the Windows Terminal.

If you are a casual terminal user, Windows Terminal does everything you used to use consoles for – and a lot more. Windows Terminal is a great tool if you are a WSL2 user, perhaps creating APIs or web apps. For both audiences (and everyone in between), this book provides great guidance, tips/tricks, and best practices.



Monday, June 21, 2021

My Next Book is Announced!

I am pleased that my publisher, Packt, have announced my next book on PowerShell. The new book is entitled Windows Server Automation with PowerShell Cookbook and it should be released in July.

You can pre-order it from Amazon - in the UK, use the Smile.Amazon.Co.UK site. In the US, use the Smile.Amazon.Com site

I am looking for a few good books reviewers - you get a copy of the book in exchange for a review. If you are interested, send me some email at DoctorDNS@Gmail.Com.

Tuesday, June 01, 2021

Resolving Hyper-V issues with WIn10 Insiders builds

 I have been a member of the Windows Insiders group since 2014, and have enjoyed testing out the updates and looking at some of the new features coming to a new Win10 release in due course. A very few Insiders builds were bad - and just did not work. But that was rare and for the most part, the new builds work well.

I recently encountered an issue whereby after the upgrade, no Hyper-V VM would start. The error log shows an error message about MCompute faults - which looks like this in the event log.

Faulting application name: vmcompute.exe, version: 10.0.21354.1, time stamp: 0x1aa49b37

Faulting module name: vmcompute.exe, version: 10.0.21354.1, time stamp: 0x1aa49b37

Exception code: 0xc0000005

Fault offset: 0x00000000001c065a

Faulting process ID: 0x23a8

Faulting application start time: 0x01d72d3e4d15903d

Faulting application path: C:\WINDOWS\system32\vmcompute.exe

Faulting module path: C:\WINDOWS\system32\vmcompute.exe

Report ID: f8375064-c2fc-4a89-a8f1-af662c551008

Faulting package full name: 

Faulting package-relative application ID:

I reported this error multiple times, but to no avail. Every week, Windows Update would offer a new build that would fail to restart Hyper-V.. I'd revert to a working build, report the issue - then wait a week and repeat. Despite multiple reports, I never got any response from MSFT. 

I decided to take a near-nuclear option - which worked! In hopes that someone else who encounters this issue and, using their search engine, finds this post - here is my solution:

  1. Start, then shut down all VMs.
  2. Took an inventory of the VMs.
  3. Remove all the un-needed VM snapshots - there were too many and all are now gone! 
  4. Export all the VMs to a backup drive (removing the snapshots helped reduce the time).
  5. Remove the Windows 10 Hyper-V feature - all of it.
  6. Reboot.
  7. Accept the latest Insiders update and let it install.
  8. Once the upgrade is complete, add the Hyper-V feature back in.
  9. Reboot.
  10. Open up the Hyper-V console and install the necessary VM switches (two in my case).
  11. Import the VMs specifying the location on the VM location on the host's disk of the VM (and not the backup!).
  12. For some VMs< i need to specify where the virtual hard disks were stored (I use a non-standard location).
  13. Start the VMs and enjoy.
Here is what I see now!

Once you are up and running, make sure to remove the backups.

From start to finish, it took me a couple of days elapsed time (the backup to an external USB2 drive was slow!). 

In step 2, I took inventory (including identifying the snapshots and working out how much data had to be backed up. One useful step wasn running this query and saving the output. 
PS C:\Foo> Get-VM | Format-Table -Property Name, Path

Name           Path
----           ----
***Cookham1    D:\VMs\Cookham1
CH1            D:\V8\CH1
DC1            d:\v8\DC1
DC2            d:\v8\DC2
HV1            D:\V8\HV1
HV2            d:\v8\HV2
Hyper-V Server D:\V7\HyperVServer\Hyper-V Server
Old - Bridge   D:\V7\Bridge
PSRV           D:\V8\PSRV
SMTP-2019      D:\V8\SMTP-2019\SMTP-2019
SRV1           D:\V8\SRV1
SRV2           D:\V8\SRV2
SS1            D:\V8\SS1

After all the updating, in step 11, I pointed the MMC's import wizard at the path noted above and most of the VMs came in fine. For the "V8" VMs, I had to specify the same path to let the wizard know where each of the disks was stored. 

Hope you never encounter this issue but if you do the is a workaround. 

Monday, January 11, 2021

Packt Publishers Sale on Through Jan 13 2021

In my morning email was a message from the publishers of two (and soon three) books. My books are on PowerShell, but as many of you know, they publish books, ebooks, and videos across the IT Spectrum. In my experience, some of their books are better than others. I'd like to think that some books, including mine, are in the 'good' category - but as ever Caveat Emptor.

Their reason for writing to me is that they currently have a sale on at the moment. This sale, which is on through Jan 13th 2021 offers certain WIndows/Linux books and ebooks for sale. 

I apologise for the short notice, but I only heard about this from a mail I got this morning.

Mastering Windows Server 2019 -

Mastering Windows Security and Hardening -

Windows Server 2019 Administration fundamentals, Second Edition -

Mastering Linux Security and Hardening -

Windows Subsystem for Linux 2 (WSL 2) Tips, Tricks, and Techniques -

And what list of great books on offer would be incomplete without my latest book: 

Windows Server 2019 Automation with PowerShell Cookbook - Third Edition -

Sunday, January 10, 2021

My Wiley PowerShell Book - Issues with the code samples

I recently posted ( about my latest PowerShell book, published by Wiley. I am proud of this book for many reasons. Most of the writing was done after the start of the Covid pandemic. Frankly, the writing helped keep me sane (well saner). 

Writing a book involves a lot of people. I write stuff  and then a good team of others help to turn this into a finished product. In particular, I was very happy with the excellent editing done by the Wiley team. The development process was laborious and my editors were a pedantic nightmare - but all in the Best Possible Way! I appreciated the almost anal nitpicking done and the total focus on quality. Everyone involved has my gratitude for their excellent work.


Today I received a mail from a wonderful reader in Italy. He complained that cutting and pasting from the e-book failed. He provided me with some examples. Yes, he is right that what he showed me is NOT what I wrote and submitted.

My apologies for this to him, and everyone reading my book. I can assure you that I did everything I could to avoid this very issue. Jim, my TE, was totally focused on helping me to avoid issues like this - every missing capital letter, every missing parameter name, and every other minor error was flagged and resolved. I did what I could, and am sorry for the resulting issues.

With that said, one of the things I insisted on was that I could publish these scripts on GitHub. You do not have to buy my book (although I hope you will) to use the scripts. You canfindt the scripts at GitHub account -

So if you want to try out the book and the scripts contained in it, please do NOT cut/paste from an electronic version - use the scripts on Github. If you find errors there, PLEASE file an issue at I will do my best to fix anything that might be broken and republish the updated script.  You are welcome to fork this repo and I'll accept PRs that fix errors in the scripts.

Thanks for everyone who either both my book or uses my scripts.

Saturday, December 19, 2020

My Latest PowerShell Book - Copies Available


After a long development process, my latest PowerShell book has been published by Wiley

This book began before the Covid pandemic began and progress as a result of the virus was a lot slower than I would have liked, but thanks to some great people we got it done and this week I got my review copies.

As I announced on my Twitter (@DoctorDNS) feed, I have some copies of this book to give away. I am more than happy sign and send off copies of the physical book. I also want to find some reviewers and can provide them with an electronic copy for review if you contact me offline. 

I love the PowerShell community and nothing would please me more than to give a copy to anyone who asks. But that is not possible. So I am happy to give away some free copies, but to make it a bit more interesting, there is a little catch! :-)

 I will give away five signed copies of the physical book to the first FIVE people who ask and who donate to the Epilepsy Society ( My daughter has severe epilepsy and I support this charity. So to get a copy, confirm a donation and EMAIL me. My email addresses DoctorDNS@Gmail.Com.

Owing to Covid restrictions announced today in the UK, physical copies may be delayed until I can get out to the Post Office. 

Friday, December 11, 2020

How To: Change the File Time for a Windows File

When using PowerShell in Windows, The Get-ChildItem (and other cmdlets) return objects of the type System.IO.FileInfo for files in the NTFS File system. Each of the returned objects contains six date/time objects:
  • CreationTime and CreationTimeUtc - the date/time that the file was created (in local time and UTC)
  • LastAccessTime and LastAccessTimeUtc - the date/time that the file was last accessed
  • LastWriteTime and LastWriteTimeUtc - the date/time that the file was last written
These properties allow you to check a file (or for that matter a folder) for when it was created and when it was last written to and read from. This is very useful to help IT Professionals to discover files that are underused and may be candidates for deletion. To cater for files being accessed in different time zones, the UTC variants provide time-zone proof times for comparison.

One challenge is that you may wish to change these times. There is no cmdlet that can change these times directly. Fortunately, there is a simple bit of PowerShell magic. Assume you have a file for which you wish to change date/times. 

Here is you can do it:

PSH [C:\Foo]: Remove-Item -Path C:\Foo\ -ea 0  
PSH [C:\Foo]: "FOO" | Out-File -Path C:\Foo\
PSH [C:\Foo]: #    Find file and display
PSH [C:\Foo]: $File = Get-ChildItem -Path c:\foo\
PSH [C:\Foo]: $File| Format-List -Property name,*time*

Name              :
CreationTime      : 11/12/2020 10:09:57
LastAccessTime    : 11/12/2020 10:09:57
LastAccessTimeUtc : 11/12/2020 10:09:57
LastWriteTime     : 11/12/2020 10:09:57
LastWriteTimeUtc  : 11/12/2020 10:09:57

PSH [C:\Foo]: #    Get new date
PSH [C:\Foo]: $NewDate = Get-Date -Date '14/08/1950'
PSH [C:\Foo]: #    Change the date
PSH [C:\Foo]: $File.CreationTime      = $NewDate
PSH [C:\Foo]: $File.CreationTimeUTC   = $NewDate
PSH [C:\Foo]: $File.LastAccessTime    = $NewDate
PSH [C:\Foo]: $File.LastACcessTimeUTC = $NewDate
PSH [C:\Foo]: $File.LastWriteTime     = $NewDate
PSH [C:\Foo]: $File.LastWriteTimeUTC  = $NewDate
PSH [C:\Foo]: #    Recheck file to see changed date/time
PSH [C:\Foo]: $File = Get-ChildItem -Path C:\Foo\
PSH [C:\Foo]: $File| Format-List -Property Name,*Time*

Name              :
CreationTime      : 14/08/1950 01:00:00
CreationTimeUtc   : 14/08/1950 00:00:00
LastAccessTime    : 14/08/1950 01:00:00
LastAccessTimeUtc : 14/08/1950 00:00:00
LastWriteTime     : 14/08/1950 01:00:00
LastWriteTimeUtc  : 14/08/1950 00:00:00

Thursday, November 26, 2020

Creating a PowerShell Cmdlet Using C# and DotNet.eXE

As part of a book I am writing, I wanted to show how simple it could be to create your own cmdlet in C#. This was to be a part of an advanced look at the things you can do with PowerShell 7 and DotNet Core. 

Most IT professionals know about the .NET objects produced by cmdlets, how to discover details about those objects, and how to reach into the .NET BCLs to do things that cmdlets can't. You can also extend PowerShell using a .NET Language such as C# to create a class. You use Add-Type to import the class into your PowerShell session and use it just like any other .NET class. Creating a cmdlet turned out to be more complex.

The first question any sane person might ask is; WHY? Why would anyone want to write a cmdlet or create a class using C#? The obvious reason, per Jimmy The Swede, is because you can. As an IT professional, knowing how is just another tool on your tool belt. But aside from that, there really are some good reasons. Using C# is easier when you need to perform asynchronous operations or multi-threading, or if you wish to use Language-Integrated Query (LINQ). IN those cases, C# is a lot easier than trying to use PowerShell in those scenarios. Another use case is taking some ASP.NET code, such as a page which creates a list of out of stock items and re-purposing it for administrative use.  Admittedly, there are not a lot of use cases in the wild - but as a PowerShell person, you should know!

If you are a hardcore Windows developer, you probably have Visual Studio and know how to use it. But in this article, I'm going to use a different approach which is to use the .NET SDK and the dotnet.exe command. You can do most of this from the command line, although there seems no simple way to install the SDK. So here goes

1. Install the .Net Core SDK.  

This is almost the hardest part of the job. You need to install the Software Development Kit in order to get the tools you need to create the cmdlet. This involves using the GUI.

Navigate to the Dotnet download page:

From here, click on the download button. This leads to the downloading page:

The download takes a few seconds/minutes to download. Eventually, you can see in the bottom left corner that the download is complete and you can now click on the open file as you can see here:

Clicking on the open file link runs the installer to install the .NET Core SDK on your computer. Eventually, you see this:

Click on Close and you now have the SDK loaded on your system. From here it's fairly simple - here's the script:

# 2. Create the cmdlet folder
New-Item -Path C:\Foo\Cmdlet -ItemType Directory -Force
Set-Location C:\Foo\Cmdlet

# 3. Creating a class library project
dotnet new classlib --name SendGreetings

# 4. Viewing contents of new folder
Set-Location -Path .\SendGreetings

# 5. Creating global.json
dotnet new globaljson

# 6. Adding PowerShell package
dotnet add package PowerShellStandard.Library

# 7. Create the cmdlet source file
$Cmdlet = @"
using System.Management.Automation;  // Windows PowerShell assembly.
namespace Reskit
  // Declare the class as a cmdlet
  // Specify verb and noun = Send-Greeting
  [Cmdlet(VerbsCommunications.Send, "Greeting")]
  public class SendGreetingCommand : PSCmdlet
    // Declare the parameters for the cmdlet.
    public string Name
      get { return name; }
      set { name = value; }
    private string name;
    // Override the ProcessRecord method to process the
    // supplied name and write a geeting to the user by 
    // calling the WriteObject method.
    protected override void ProcessRecord()
      WriteObject("Hello " + name + " - have a nice day!");
$Cmdlet | Out-File .\SendGreetingCommand.cs

# 8. remove class file 
Remove-Item -Path .\Class1.cs

# 9. Build the cmdlet
dotnet build 

# 10. Importing the DLL holding the cmdlet
$DLLPath = '.\bin\Debug\net5.0\SendGreetings.dll'
Import-Module -Name $DLLPath
at that point, you can use the Send-Greeting command, like this:

Thanks to James O'Neil who got me started on this solution. Thanks also to Thomas Rayner's excellent PowerShell summit talk at

Thursday, November 12, 2020

PowerShell 7.1 ships. And it's in the Store

 Yesterday, the PowerShell team announced that they had shipped PowerShell 7,1. This is the next version of the open-source, cross-platform version of PowerShell. This release is not a Long Term Support release, meaning support is limited. PowerShell 7.0 and the future 7,2 are LTS releases. The release was announced on a blog post by Joey Aiello Program Manager, PowerShell.

PowerShell 7.1 is focused on resolving community issues. You can read the release notes here: While this is not an LTS release, the team have made some great strides forward in terms of the overall platform.

From an engineering point of view, the release of PowerShell moves to .NET 5.0. This major update to the .NET Core framework brings a lot of benefits for developers, including some performance improvements which you can read about at You may find that in complex scripts, this translates to shorter run times. 

One thing which is noteworthy is that PowerShell is now in the Windows store:

And for the adventurous, a preview of 7.2 is meant to drop soon.

Friday, October 02, 2020

Open Sourcing of PowerShell Documents is Awesome

Ever since the very first version of Windows PowerShell, the built-in help material has been awesome. Each cmdlet carefully documented with good examples to illustrate the most common use cases. Good English writing as well as best-practice PowerShell.

But every now and then there is an error. Some simple, some more complex. Some occasionally have some less than great grammar or miss out a useful example of the cmdlet. In the old, old days, you could file an issue somewhere that no one ever seemed to read. And then wait a year or longer before the next version of Windows PowerShell. It used to drive me nuts. I did rant a bit at the time (sorry Erin).

Then someone decided to Open Source the documentation and move it all to What a great idea. At first, it was a bit clumsy, as everyone involved slowly moved into the wonders of managing a complex repository at GitHub. The process was, again at first, a tad convoluted and frankly slow. BUT it worked. 

I recall the first time I saw an error. It was in class at Lab Center in Sweden. I was showing the delegates how to read the help text. A cmdlet parameters description said it did not take wild cards, but it did. Sok, in class, I filed an issue, edited the document, and issued a pull request. It was really cool although it took weeks to action.

Today, I saw an issue that arrived in my email in basket. You can read the issue on That message hit my email box at 7.23 this morning. I read it just after 10/ A few minutes later, I had edited the document and created a pull request. A 13:30 or thereabouts, I got an email to say that the issue was now closed as the PR had been accepted. 

So 6 hours from the time I got the email till the issue was resolved. It is going to take a few more days for the update to hit the web site, but I can live with that. 

TL;DR to me is that with the open sourcing of PowerShell documentation, errors need not persist. You can fix them easily. Or just report and let the community do the work. 

Monday, May 11, 2020

Draw.IO Integration with VS Code

Over the weekend, I saw a tweet announcing a new extension for VS Code. The extension "Draw.IO Integration "enables you to create good looking diagrams directly inside VS Code. Insane, I thought, then I downloaded it. I think a youthful view is that this is DOPE!

You install the extension directly from inside VS Code, create and open a new *.Drawio document and away you go. If you have used virtually any diagramming tool, the interface is immediately accessible.

Here is a screenshot of it working on my workstation:

As you can see, VS Code, up and running with my main workspace loaded and there in the module is a diagram too. It feels a LOT like Visio without (at least so far) the flakiness of Visio of late. The diagrams are not the nice polished ones you might be able to create using tools like PowerPoint, Visio, etc. But simple and BUILT-In.

When you are finished with the diagram, you can export it in various formats. The diagram above, rendered to PNG, looks like this;

There are many templates and, I'm sure, every possibility of more being added by the community over time.

Wednesday, April 01, 2020

Setting up an SMTP Relay using SendGrid

I have written several books - some of which have made use of SMTP to send mail. I am currently working on a Storage chapter for my next book. This chapter makes use of File Services Resource Manager (FSRM) to manage file storage. One neat feature of FSRM is its ability to send an email when events occur.

For many, the issue of how to test this has been a challenge, especially if you do not have a happy SMTP server you can use. A neat tool I have used is to use IIS and the SMTP email relay service offered by SendGrid. You can read more about the company here:

Setting up both IIS and the email relay service which SendGrid provides are straightforward to set up. In this blog post, I'll show you how to do this. 

There are 4 simple steps:
1. Setup a SendGrid account Ttheir free service allows you to send 100 emails a day, which should be more than enough for testing.
2. Create a SendGrid API key  The API key is, in essence, your user id and password for the site (so be careful). Make sure you copy/paste the key carefully!
3. Install IIS and configure an email relay using your SendGrid account The GUI is pretty easy to use. There's probably a way using PowerShell, but I've not worked it out yet.
4. Testing your relay And for that, there IS a PowerShell script.

Lets's look at these steps

Setup a SendGrid account

The first step in creating your SMTP relay is to sign up for a (free) account with SendGrid. Like this
1. Navigate to SendGrid.Com (http://Signup.SendGrid.Com) - Use your browser to go to SendGrid's sign up page and fill out your details:

Make sure you create a very strong password. then click ok OK. 

2. This takes you to a page for your personal details Obviously, you should use your real name:

Once you set these details, your account is setup. You should get email from SendGrid asking you to confirm your account. Please do NOT forget to do that.

Create a SendGrid API key

3. Once you have created your SendGrid account, navigate to the Email Integration Guide from SendGrid's Home page:

4. This brings you to the configuration page, where you click to set up your SMTP relay:

5. This takes you to a page where you specify an API Key name and click Generate:

Note that you can use any API Key name from this screen. The API Key name is not used as part of the relay security but is just FYI.

6. After you click on Create Key, SendGrid displays the key and provides some details on how to configure your client:
Please note that in this screenshot, I have obscured MY API key. Since that key is both a user name AND a password for your (or in this case my) account, you need to keep it safe. I recommend at this point cut/past the key form the web browser and save it to a text file. You can not retrieve the plaintext key again so be careful.

Now that you have your account setup and an API key generated, you now need to configure IIS to use this.

Install IIS and configure an Email Relay

The next step is to install IIS and configure the IIS SMTP relay. I assume you are using WIndow Sever 2019. If you need to, you can always download an eval version to use. That gives you 6 months use before you need to activate (or just install and configure a new server). 

7. Install IIS in Windows Server
You use the Add-WindowsFeature command to add IIS to your Windows Server host (e.g. on SRV1.Reskit.Org), like this:

For some reason, this needs a reboot - so go do it!

8. Configure IIS Relay on SRV1
The final step is to set up the SMTP Relay on SRV1. You use the IIS 6 MMC console for this. So start by bringing up the SMTP server tool either from the Start menu (Start/Windows Administrative Tools\Internet Information Server (IIS)  6 Manager or run the application:
C:\Windows\system32\inetsrv\InetMgr6.exe.And select the IIS SMTP Server:

Now bring up the SMTP Virtual Server properties dialog by right-clicking on the SMTP Virtual Server and selecting Properties then click on Access. And select Relay - here you configure which systems can connect. For testing, I set "All except the list below" and leave the list blank, like this:

Click OK.

Next click on the Delivery tab in the SMTP Virtual Server Properties:

From here, click on outbound security and set the user name and password. From this dialog box, select Basic Authentication then enters the user name and password. In this case, the user id is 'apikey' and the password is your API key you set up earlier. Configuring it looks like this:

After clicking OK, click on the Advanced Delivery button to bring up the Advanced Delivery dialog from which you enter the SendGrid server details like this:

And after clicking OK, your relay should be all set up and ready to go. You must need to test it.

Testing your relay

Once you have the relay working on SRV1, you can test it using this little script:

# Test-SendGrid.ps1 - script to test send grid

# Setup key variables
$To         = ''
$Fr         = ''
$Subj       = 'Test Email via SendGrid'
$body       = "testing`ntesting`nTESTING"
$SmtpSrv    = 'SRV1.Reskit.Org'

# And send the email
Send-Mailmessage -To $To -From $Fr -Subj $Subj -Body $body -SmtpServer $SmtpSrv

After sending the mail, you can check in your email client to ensure the mail is sent. If you don't get it, as ever, check your junk mail folder. If that doesn't work do it over and do so very carefully! My personal experience is that API keys can be a challenge. You may need to generate another key.

After sending the mail, you should check in your email client to ensure the mail has been sent and you have received it (it may be in your Junk folder). If you do not get the mail, you need to do some SMTP relay troubleshooting. 


SendGrid provides a simple SMTP EMail Relay Service which you can use to test SMTP enabled applications or services like FSRM.

Thanks to those nice people at Sendgrid for a most useful service!

Thursday, March 12, 2020

PowerShell 7, VS Code, and the PowerShell 7 ISE Extension

Introduction and Background

Welcome to this post, a part of this #PS7NOW blog series - I hope you are enjoying them.  Before getting to my topic, I assume that you know what PowerShell 7 is and are familiar with Windows PowerShell. 

The Windows PowerShell ISE, a tool I used extensively ever since early beta versions in Windows PowerShell V2, does NOT feature in PowerShell 7.  You might say, it's pining for the fjords (although it's still shipped in Windows and is likely to be supported for years and years). But however you describe it - it's feature complete and is not going to support PowerSell 7.  Replacing the ISE is a new product: Visual Studio Code, or VS Code.

VS Code is light-weight, cross-platform (ie Linux, Mac, Windows), and open-source code editing tool. Which sounds like no big deal, but if you are an ISE user and are utilising Windows PowerShell, VS Code is in your future. For more details on VS Code, see

VS Code

I must confess, the early versions of VS Code I used were under-whelming, and I much preferred the ISE. And given that PowerShell Core was also in the early days, sticking with the ISE and Windows PowerShell for day to day activities made sense.

But for me, the release of PowerShell 7 and the incredible velocity behind the both PowerShell 7 and VS Code product have changed my view. I now use VS Code for my day to day work. I still use the ISE, but only to install  PowerShell 7 and VS Code.

Some features I like in VS Code include:-
  • Workspaces - this is a set of related folders/files which makes it easy to keep things together even when they are spread out in filestore.
  • Side by Side edit windows - makes comparing files, and leveraging code from one into another so much simpler. 
  • Built-in spell check - yes it's an extension, but typos in comments are less likely. 
  • The extensibility and customizability - you really can have it your way.
  • PS Script analyzer is built in - so I get hints about poor code as I type.
And, and, and...

For more details on VS Code, see:

VS Code Extensions

VS Code was built to be extended. An extension adds functionality, such as spell-checking or markdown checking. I originally authored this blog post using Markdown, with the Markdown All In One Extension. If I am to author in Markdown, VS Code is my go-to tool. 

I am working on a book and use Github Gists. To assist in managing my Gists, I also use the GistPad Extension.  It makes handling Gists so much easier. The integration between VS Code and GitHub, via the extension, is really useful.

To customise the colour scheme of VS Code - you can find many extensions providing additional themes. And as a hint, some of these themes are better than others! For details on the available extensions (and there are a lot) see

And of course, a great extension anyone using ISE is going to want to get is the PowerShell ISE extension that makes VS code feel more like the ISE, at least colour wise.

Installing VS Code

To install VS Code, well - there's a PowerShell script for that too. Naturally! It is called Install-VSCode and you download it from the PS Gallery. That script when you run it, downloads and installs the latest version of VS Code and provides you with flexibility over exactly what to install.

You can find any number of cute 1-liners, but here's a more workman-like, step by step, and hopefully clearer installation snippet: 

# Get and save Install-VSCode installation script
#    Assumes C:\Foo exists
Set-Location -Path C:\Foo
Save-Script -Name Install-VSCode -Path C:\Foo

# Create a list of extensions to add when installing
$Extensions =  'Streetsidesoftware.code-spell-checker',

# Now install VS Code
$InstallHT = @{
  BuildEdition         = 'Stable-System'
  AdditionalExtensions = $Extensions
  LaunchWhenDone       = $true
}.\Install-VSCode.ps1 @InstallHT

The install script is able to load different versions (Stable-System, Stable-User, Insider-System, Insider-User). Different builds provide more recent features but maybe less well tested and less reliable. I use Stable-System and have not had any issues whatsoever (aside from being able to get PSReadline to behave - but that is a rant for another day)

When you run this snippet, for example in the Windows PowerShell or the ISE, you may see some warning messages when VS Code adds the extensions. You can ignore these errors. FWIW It seems these warning messages have gone away with the latest builds of VS Code so you may not see these today.

This snippet takes around 30-40 seconds and rewards you, in the end, with VS Code open and ready for use.

You may have noticed reading that snippet that it did not explicitly mention the PowerShell extensions. The good news is that the script installs this extension by default.
It sure seems like a good idea to me! However, the ISE theme is not used by default - but there are scripts to fix that too.

Here are a two screenshots of VS code (with the ISE Theme) and the Windows PowerShell ISE.

For more details on setting up VS Code, see:

Managing VS Code Extensions

You can manage and configure VS Code extensions inside VS Code or externally. In early versions of VS Code, you had to hand configure a JSON file to change settings, but today, there's a gui for that. And once you install VS Code, you can manage extensions (from PowerShell) like this:

#    Sets the root path for extensions
code --extensions-dir

#    Lists the installed extensions.
code --list-extensions

#    Uninstalls an extension.
code --uninstall-extension ( | )

VS Code PowerShell Extension

As I mentioned earlier, one extension most ISE users are going to want to get is the Powershell ISE extension. The PowerShell extension adds great language support and great features including: 
  • PowerShell Syntax highlighting
  • Tab completion
  • Code snippets
  • IntelliSense for cmdlets, parameters, and more
  • The rule-based analysis provided by PowerShell Script Analyzer
  • Definition tracking and a "Go to definition" for cmdlets and variables
  • Find references of commands and variables
  • Document and Workspace symbol discovery
  • Run the selected section of PowerShell code using F8
  • Launch online help for the symbol under the cursor using Ctrl + F1
  • Local script debugging and basic interactive console support
  • A colour scheme that looks familiar.
In my experience, VS Code is just different enough from the ISE to make those first few hours a tad painful.  But quickly, very quickly, VS Code begins to make the ISE look quite dated. I love having PS Script Analyzer run as I am entering code - it helps me to write better code And the side by side editing has made my book-writing task a lot simpler. 

For more details on the extension, see

Configuring the PowerShell Extension

You can update and configure extensions from within VS Code itself. In early versions of VS Code, any configuration had to be done by hand-editing a JSON file. Later versions added a configuration GUI meaning you can do most configuration simply using the GUI.
But you can also directly edit the **settings.json** file to update the configuration.

The VS Code user settings file is contained in the file:

My current settings.json file looks like this:

  "workbench.colorTheme": "PowerShell ISE",
  "window.zoomLevel": 1,
  "editor.fontFamily": "'Cascadia Code',Consolas,'Courier New'",
  "editor.tabCompletion": "on",
  "workbench.editor.highlightModifiedTabs": true,
  "powershell.codeFormatting.useCorrectCasing": true,
  "files.autoSave": "onWindowChange",
  "files.defaultLanguage": "powershell"

A neat feature of VS Code - if you update that file and save it, VS Code uses the newly created configuration automatically.

In the earlier snippet, you installed VS Code.
At the end of the configuration, you could do this to further configure VS Code:

# Download Cascadia Code font from GitHub
$CascadiaFont    = 'Cascadia.ttf'    # font name
$CascadiaRelURL  = ''
$CascadiaRelease = Invoke-WebRequest -Uri $CascadiaRelURL # Get all of them
$CascadiaPath    = "" + ($CascadiaRelease.Links.href | 
                      Where-Object { $_ -match "($cascadiaFont)" } | 
                        Select-Object -First 1)
$CascadiaFile   = "C:\Foo\$CascadiaFont"

# Download Cascadia Code font file
Invoke-WebRequest -Uri $CascadiaPath -OutFile $CascadiaFile

# Install Cascadia Code
$FontShellApp = New-Object -Com Shell.Application
$FontShellNamespace = $FontShellApp.Namespace(0x14)
$FontShellNamespace.CopyHere($CascadiaFile, 0x10)

# Install the font using Shell.Application COM object
$Destination = (New-Object -ComObject Shell.Application).Namespace(0x14)

# Create a short cut to VSCode
$SourceFileLocation  = "$env:ProgramFiles\Microsoft VS Code\Code.exe"
$ShortcutLocation    = "C:\foo\vscode.lnk"

# Create a  new object
$WScriptShell        = New-Object -ComObject WScript.Shell
$Shortcut            = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation

#Save the Shortcut to the TargetPath

# Create a short cut to PowerShell 7
$SourceFileLocation  = "$env:ProgramFiles\PowerShell\7-Preview\pwsh.exe"
$ShortcutLocation    = 'C:\Foo\pwsh.lnk'

# Create a  new object
$WScriptShell        = New-Object -ComObject WScript.Shell
$Shortcut            = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation

# Save the Shortcut to the TargetPath
$XML = @'
$XML | Out-File -FilePath C:\Foo\Layout.xml

# Import a startlayut.XML file
Import-StartLayout -LayoutPath C:\Foo\Layout.xml -MountPath C:\

# Update Local User Settings for VS Code
#    This step in particular needs to be run in PowerShell 7!
$JSON = @'
  "editor.fontFamily": "'Cascadia Code',Consolas,'Courier New'",
  "editor.tabCompletion": "on",
  "files.autoSave": "onWindowChange",
  "files.defaultLanguage": "powershell",
  "powershell.codeFormatting.useCorrectCasing": true,
  "window.zoomLevel": 1,
  "workbench.editor.highlightModifiedTabs": true,
  "workbench.colorTheme": "PowerShell ISE",
$JHT = ConvertFrom-Json -InputObject $JSON -AsHashtable
$PWSH = "C:\\Program Files\\PowerShell\\7\\pwsh.exe"
$JHT += @{
  "" = "$PWSH"
$Path = $Env:APPDATA
$CP   = '\Code\User\Settings.json'
$Settings = Join-Path  $Path -ChildPath $CP
$JHT |
  ConvertTo-Json  |
    Out-File -FilePath $Settings

This snippet downloads and installs a new font, Cascadia Code and creates two new shortcuts for your toolbar. The snippet also updates the settings.json file with certain useful settings.


PowerShell 7, has shipped. If you are a Windows PowerShell, and particularly a fan of the ISE, VS Code is a tool to take on board. To assist you, the PowerShell extension makes VS Code easier to adopt. And the extensions available take VS Code to the next level.

TL;DR: PowerShell 7 with VS Code with PowerShell 7 rocks.
The PowerShell Extension to VS Code just rocks more!

What are you waiting for?