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?

Tuesday, March 10, 2020

PowerShell 7 Chain and Ternary Operators

Introduction and Background

Welcome to this post as part of this PowerShell 7's #PSBlogWeek!   I hope you are enjoying the many posts.

As I started to think about this topic, an old Grateful Dead song kept running through my mind "Operator, Can you help m?  Help me if you please...For a live version, listen to

So here is some information about a couple of the great new features in PowerShell 7, in particular, the Pipeline Chain Operators and the Ternary Operators.

In the days of Windows PowerShell, extending the PowerShell language was done by the Microsoft Windows PowerShell team.  With the move to open source, more developers can, and have, made it possible to do a lot more in PowerShell 7. PowerShell's language was modelled on C# - Jeff Snover has often said that PowerShell is on the glide scope to C#.

With PowerShell 7 comes two new operator sets: The Pipeline Chain Operators and the Ternary operators.

The Pipeline Chain operators (|| and &&) enable you to allow conditional execution of commands depending on whether the previous command succeeded for failed. You use the Ternary operators (? and :) as a short-hand way of implementing if/else type statements. These are popular among C# developers and Bash users and have long been requested within PowerShell.

These operators add new functionality to PowerShell 7. They are nice when used carefully, but can reduce the clarity of production code. Let's look at them in more detail. 

PIpeline Chain Operators

The pipeline Chain operators enable conditional execution of commands depending on whether a previous command succeeded or failed. There are two pipeline chain operators: && and ||These operators were added to PowerShell 7 Preview 5.  Prior to PowerShell 7, you could have used If/Else to do the same thing,

These operators come originally from Posix. POSIX shells call this as AND-OR lists. The idea is that depending on whether a command is successful, you can do different things.

What is it used for?

If a pipeline is successful, this operator allows you to run some other pipeline. But if the first pipeline is unsuccessful, you can run a different pipeline. 

For example:

# Create an SSH key pair - if successful copy 
# the public key to clipboard
ssh-keygen -t rsa -b 2048 && Get-Content -Raw ~\.ssh\ || clip

If the keys are generated successfully using SSH-KEYGEN (and content returned from Get-Content), then the command copies it to the clipboard. Without these operators you would have used if/else and/or try/catch - the chain operators make things a bit shorter.

Ternary Operators

The ternary operator evaluates a Boolean expression and returns the result of one of the two expressions, depending on whether the Boolean expression evaluates to true or false.
This sounds more complex than it is (see the example below!) These operators were added to PowerShell 7 Preview 4. 

What Is It Used For?
You typically use this operator mainly to create a string depending on the value of a boolean variable or expression. For example, you could create a string that displayed whether an AD user account was enabled based on the user's Enabled property or display whether a user is using PowerShell on a Mac. Like this:

# Is User Enabled?
# Create 2 strings
$UEMsg1         = "This user IS enabled in AD"
$UEMsg2         = "This user IS NOT enabled in AD"
# Get Details
$UserEnabled    = (Get-ADUser -Identity $UserName).Enabled
# Set Enabled/Disable String
$UserEnabledStr = $UserEnabled ? $UEMsg1 : $UEMsg2
# What does this show for an enabled user:
This user IS enabled in AD
# Anotehr example
$IsMacOS ? 'Yes' : 'No'

You Can but Should You?

I like these new operators but am not likely to use them in code I write.  Except maybe to demonstrate them.  I really do not, yet, see a great use case, except at the console. As an example of this, look at the chain operator example above. That snippet executes a command, and if successful copies a file to the clipboard. 

Personally, I'd have written it more like this:

# Create an SSH key pair - if successful, copy
# the public key to clipboard
try {
  ssh-keygen -t rsa -b 2048
Catch {
    # handle terminating error - left as an exercise for the reader
#  then    
Get-Content -Raw ~\.ssh\ | clip

If I was running this from the console, I'd just run ssh-keygen. If it ran ok, then I'd type Get-Content and pipe the output to the clipboard. Typing longer lines of code is almost certain to introduce typos, especially given my lousy typing. I find doing things step by step is easiest - both to write and to understand months later when the code needs modification.

These operators have the potential to reduce the clarity of production code. Unless you know these operators, their meaning is not easy to discern. Operators like -Contains, -Eq, and -Match are both named so as to give at least some clue to their use. The '?' character an alias for Where-Object and the ':' used in PSDrive letters leading to overloaded operators.  And that can diminish the readability of production code. I am sure mileage varies - and would love to hear comments as well as seeing more great use cases.


TL;DR: Great new operators that bring requested C# Features to PowerShell - Just use them wisely.