Download as pdf or txt
Download as pdf or txt
You are on page 1of 132

#PS7Now

A Blogger’s Guide to Getting Started with PowerShell 7

Jeff Hicks
This book is for sale at http://leanpub.com/ps7now

This version was published on 2020-03-26

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

© 2020 The DevOps Collective, Inc.


Contents

About This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i

About OnRamp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii

About The DevOps Collective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv

Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Getting Your Hands on PowerShell 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

PowerShell 7 Profile Paths and Locations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

PowerShell 7 Experimental Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Getting Started Using SSH with PowerShell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

PowerShell 7 Remoting Cleanup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

ForEach Parallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

PowerShell 7 Changes to JSON Cmdlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Staying Up to Date on PowerShell Releases with Update Notifications . . . . . . . . . . . . 51

PowerShell Markdown Cmdlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

Changes to Invoke-RestMethod in PowerShell 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

What’s New with Select-String in PowerShell7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

Running PowerShell 7 Commands Directly on Ansible Localhost . . . . . . . . . . . . . . . . 74

Defining and Changing Who You Are with PowerShell Classes and Update-List . . . . . . 77

Error Handling in PowerShell 7 with Get-Error and $Errorview . . . . . . . . . . . . . . . . . 81

PowerShell 7 Chain and Ternary Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

Exploring… Nothing? PowerShell 7’s Null Conditional Operators . . . . . . . . . . . . . . . . 90


CONTENTS

Using ANSI Escape Sequences in PowerShell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

PowerShell 7, VS Code, and the PowerShell VS Code Extension . . . . . . . . . . . . . . . . . 108

PowerShell 7 Cross-Platform Scripting Tips and Traps . . . . . . . . . . . . . . . . . . . . . . . 116

Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Release Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124


About This Book
This book is compilation of material published online centered
on the general release of PowerShell 7. For this project, a number
of PowerShell-focused bloggers and authors created content on
many of the new features of PowerShell 7. The intent was to raise awareness of the new release
and provide an incentive for existing Windows PowerShell administrators to make the move to
PowerShell 7. The content was released over the course of week in March 2020 using the #PSBlogWeek
and #PS7Now tags on Twitter. If you missed some or all of the content originally, this book is your
chance to discover what you missed.
The ebook is being freely offered but we hope you will consider making a small donation. All
proceeds benefit the OnRamp Scholarship fund maintained by The DevOps Collective, which is
the organization behind the PowerShell Summit. The authors have freely contributed their content.
Nobody is receiving any financial compensation. The content in this book may vary slightly from
the originally published articles. The original content has been edited for consistency, grammar and
spelling. Although be aware that not all authors are from North America!

Cover art graciously contributed by Jennifer Kanakos.

A Note On Code Listings


Due to page size limitations you may see some code samples in this book where the line ends in a
backslash. This is Leanpub’s line continuation character and is inserted when the line is too long
to fit the width of the code sample. We’ve tried to adjust code as much as possible to reduce these
breaks, but in some cases we felt it was easier to leave them in. Extended code samples are included
in an extras zip file which you should have obtained when getting this book. All code is offered as
is with no warranties or guarantees.

No code samples or snippets should be considered ready for


production. They are for educational purposes only.
About This Book ii

Questions and Comments


We are foregoing the usual book forum that can be setup with a Leanpub book. If you have questions,
or comments about any of the content in this book, feel free to reach out to the individual author
on Twitter. Check the Contributors page at the end of the book for a list of online links. You are
also encouraged to use the free forums at PowerShell.org for all your other PowerShell questions or
problems.
About OnRamp
OnRamp is designed for entry-level technology professionals who
have completed foundational certifications such as CompTIA A+
and Cisco IT Essentials. PowerShell + DevOps Global Summit’s¹
OnRamp track is a distinct ticket type that includes admission to
a separate track of content designed for entry-level technology
professionals. Less a conference and more of a guided, hands-on
class, OnRamp is taught by some the industry’s leading Power-
Shell instructors. It’s more than just an introduction to PowerShell
as a technology; OnRamp is also an introduction to the PowerShell
community and ecosystem. By blending classroom time with time
in Summit’s general sessions, keynotes, and social events, OnRamp attendees can supercharge their
entry into the broader world of DevOps and IT automation.
No prior PowerShell experience is required, although some basic knowledge of server administration
will be useful. A number of full-ride scholarships designed to help bring new and diverse young
professionals into the community and field are offered by the DevOps Collective. Your donation for
this book goes towards this scholarship fund.

Learn more about the OnRamp Scholarship at https://events.devopscollective.org/OnRamp/Scholarship/.

¹https://events.devopscollective.org/
About The DevOps Collective
Originally formed in 2012, The DevOps Collective, Inc.², is a
US 501 C(3) nonprofit dedicated to education and community in
the DevOps field. It specializes in technologies built around the
PowerShell Language Specification (PLS), covering all platforms
and operating systems. Aside from the PowerShell Summit³ and the PowerShell.org website, it offers
a number of free ebooks, free webinars, and more. Most PowerShell Summit sessions are recorded
and offered for free on YouTube⁴. The organization also provide assistance to local user groups and
enthusiasts who want to hold their own local events, such as a PowerShell Saturday or DevOps Day.

²https://devopscollective.org
³http://powershellsummit.org
⁴https://www.youtube.com/user/powershellorg
Foreword
“PowerShell 7 is an open-source, cross-platform, automation and configuration
tool enabling businesses to achieve greater agility in cloud, hybrid-cloud, and
on-premises through reliable and repeatable expert automation.”

This sentence would not exist if not for you. Your shared discoveries and contributions are the
foundation of PowerShell’s success. I’m grateful that my friend, and top industry expert Jeff Hicks
gathered a merry band of Super-Star PowerShell professionals together to share their PowerShell
experiences. Let them show you how to flip today’s challenges into tomorrow’s success with
PowerShell 7.
PowerShell loves technology. Open this book to explore the vast management and control you can
automate with your existing technology investments. Is scaling service in the cloud part on your
To-Do list? How about on-premises server management? Or do you have both in the form of a
hybrid-cloud? Explore what these thought-leaders, experts and enthusiasts are thinking about, how
they go about solving their daily challenges, and what failures impacted them along the way to
success.
When you’re finished with this book, help spread the word. Join with these authors and experts in
helping everyone be successful with PowerShell. Help your friends and co-workers understand the
benefits of adopting PowerShell 7 now!
Jason Helmick,
Program Manager | PowerShell Team
Getting Your Hands on PowerShell 7

by Josh King

PowerShell 7 is out now! The first step in being able to add it to our toolbox is to install it so let’s
check out our options.

It’s finally here!


It was April of 2019, just after the Generally Available release of PowerShell Core 6.2, when the
PowerShell team announced⁵ that the next release of would be known as PowerShell 7.
There were two things that surprised people in that announcement, the first being that the next
version number wouldn’t just increment up to 6.3. Secondly, where’d that quantifier “Core”
disappear to? Since then, we’ve had six preview releases and 2 release candidates. There’s been
a huge number of community contributions and epic work from the PowerShell team.
⁵https://devblogs.microsoft.com/powershell/the-next-release-of-powershell-powershell-7/
Getting Your Hands on PowerShell 7 3

Thanks to all the effort put in by so many people, Windows admins that have been bound to
Windows PowerShell 5.1 can finally make the move over to the open source version of their favorite
(I assume!) management tool. This move allows them to benefit from all the new features that have
been built in since development shifted from being focused solely on Windows. This should be
reasonably frictionless⁶ due to greater compatibility (compared to 6.2).
But before all of the new features are explored by others throughout this blog week, we’ll have a
look at getting it installed.

Install Manually
Our first option for installing PowerShell 7 is to grab the relevant installer from GitHub⁷. Chances
are if you’re installing on Windows you’ll want to grab the x86 or x64 MSI file. Download and run
that, click through the wizard and you’re ready to rock. If you’re wanting to do a manual install on
Linux or Mac… I’m not sure this is the best path to take. Instead, check out the docs⁸ about how to
install on your distribution of choice.

Note, the install does it’s best to remove any existing installs of PowerShell Core 6.x, but
will leave PowerShell 7 Preview installs untouched. You may want to go ahead and remove
the preview installs yourself if you were following them.

It’s Portable
I wanted to call out here that PowerShell 7 is technically portable. The GitHub releases page has
zipped versions of the Windows releases which you can extract and… put anywhere and then run
pwsh.exe. PowerShell 7 won’t be on your path by default if you go this route but it does give you
complete control.

Scripted Install
There are a pair of scripts available to automatically install the latest PowerShell version available.
On Windows, run the following command in a PowerShell console:

iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"


⁶Yes, there are some scripts that won’t work in PS7 or will require some work. Don’t feel like you have to transition completely and never
look back.
⁷https://github.com/PowerShell/PowerShell/releases/tag/v7.0.0
⁸https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-7
Getting Your Hands on PowerShell 7 4

In the interest of completeness, and because I hate leaving aliases there let’s just note the
two in that snippet:

• iex = Invoke-Expression
• irm = Invoke-RestMethod

This time, I can be a little more helpful for those on Linux. While it’s not the same script, it’s just as
simple to run:

wget https://aka.ms/install-powershell.sh; sudo bash install-powershell.sh -preview

Execute this in your terminal of choice, unlike the Windows option you don’t already need
PowerShell.

The Microsoft Store


The newest option that’s been (being?) introduced for PowerShell 7 is the ability to install it as an
application from the Microsoft store. To get it, search for “PowerShell” and install it. The app will
be from “Microsoft Corporation” and be under the developer tools category. Remember, there’s no
“Windows” or “Core” in the name anymore… just PowerShell. At the time of writing the live version
of PowerShell 7 isn’t yet available, but I can attest to the Preview working rather well.
Getting Your Hands on PowerShell 7 5

PowerShell Preview in the Microsoft Store

At least during the preview, the application was able to live alongside the MSI version, which in
turn can happily live alongside Windows PowerShell. In practice, I found it better to choose either
the store version or the MSI version as I wasn’t always 100% sure which I was opening (you can
tell, you just need to pay closer than normal attention.) The benefit to installing from the store is
that you’ll get automatic updates as they are released, though it’s possible they may be slower (due
to verification of the packages) than the general releases to GitHub. The preview versions did have
some issues related to resource virtualization. The hope is that this has been resolved, but we won’t
know for sure until the next version is up on the Microsoft Store.

One Final Option


There’s one last option I wanted to include, as I know someone⁹ would give me a hard time if I left
it out: Chocolatey¹⁰.
If you work on Windows and haven’t heard about and installed Chocolatey, fix that¹¹. It’s a tool that
allows you to install, update, and otherwise manage software on Windows much like our friends on
Linux can with their package managers. To install PowerShell 7 using Chocolatey¹², run:

⁹https://twitter.com/steviecoaster
¹⁰https://chocolatey.org/
¹¹https://chocolatey.org/install
¹²https://chocolatey.org/packages/powershell-core
Getting Your Hands on PowerShell 7 6

choco install powershell-core

You’ll note that the command mentions “PowerShell Core”, that’s a hangover from this package
being how the previous releases were made available and keeping that name allows existing users
to easily update. It’s possible that this will change to just powershell in the future or that the more
“correct” name is added in addition.

More to Come
Regardless of the method chosen, you’ll now be up and running on PowerShell 7.

This article originally appeared online at https://toastit.dev/2020/03/08/ps7now-installing/.


PowerShell 7 Profile Paths and
Locations

by Prateek Singh

The next major versions of PowerShell is out and there are some new features and significant changes
lets quickly go through them without going into the details, although many of these items are covered
elsewhere in this book.

• .NET Core 3.1 (LTS)


• ForEach-Object -Parallel
• Windows compatibility wrapper
• New version notification
• New error view (ConciseView) and Get-Error cmdlet
• Pipeline chain operators (&& and ||)
• Ternary operator (a ? b : c)
• Null assignment and coalescing operators (?? and ??=)
• Cross-platform Invoke-DscResource (experimental)
• Out-GridView, -ShowWindow and other legacy GUI cmdlets are back on Windows

Apart from these major feature releases there are some changes in directory paths as well if you are
coming from Windows PowerShell 5.1, like for the default locations where your $Profiles are stored
are changed. If you don’t know, PowerShell profiles are just like logon scripts, that can be used to
setup your PowerShell session specific to your needs like setting up Environment Variables, aliases
and loading functions into the session, by customizing your environment every time PowerShell.exe
or pwsh is launched, unless the -noprofile switch is explicitly specified.

Profile Files
PowerShell supports the following profiles are is shown in the precedence order, that means the first
profile has the highest precedence order to the profiles mentioned after that.
PowerShell 7 Profile Paths and Locations 8

Profile Path Scope


$Home[My]DocumentsPowerShellMicrosoft.PowerShell_profile.ps1 Current user, Current Host
$Home[My]DocumentsPowerShellProfile.ps1 Current User, All Hosts
$PsHomeMicrosoft.PowerShell_profile.ps1 All Users, Current Host
$PsHomeProfile.ps1 All Users, All Hosts

Here, $PSHome variable holds the installation directory of for PowerShell and $Home holds the current
user’s home directory. One important thing to note here is, since PowerShell 6 it is open-sourced
and cross-platform so it can run on Windows, Linux and MacOS, hence the values of $PSHome and
$Home variable will change on different operating systems.

Following changes were introduced in PowerShell Core 6, not in the recent release version
7 and it is good to know if you have started using PowerShell 7 and coming from Windows
PowerShell v5 background.

In Windows PowerShell 5.1, $PSHome points to C:\Windows\System32\WindowsPowerShell\v1.0.

Windows PowerShell Variables

Whereas, in PowerShell 7, $PsHome becomes C:\Program Files\PowerShell\7 and hence your


$Profile variable for All Users All Hosts points to a different path: C:\Program Files\PowerShell\7\profile.ps1

PowerShell 7 Variables

But on Linux systems, PowerShell 7 (pwsh) is installed at /opt/microsoft/powershell directory, so


your All Users All Hosts profile is located at /opt/microsoft/powershell/profile.ps1.
PowerShell 7 Profile Paths and Locations 9

PowerShell 7 on Linux Variables

The profiles above are shown in precedence order. The first profile in the list has the highest
precedence order. As you can see in the profiles above, we have two variables. The $PSHome and
$Home variables present the following values.

• $PSHome – Stores the installation directory for PowerShell


• $Home – Stores the current user’s home directory
• Other programs that host PowerShell can support their own.

Please Note
If you already had PowerShell Corev6.x installed on your machine and then if you install
PowerShell 7, it will overwrite the PowerShell Core 6.x directory and remove all the
unnecessary files, that is in place upgrade that removes the older version. But it can still
run side-by-side with Windows PowerShell 5.1.

Profile Variable
PowerShell provides an automatic variable called $Profile which stores profile paths for all the
profiles, by default it only shows the path of Current User, Current Host and if you want to view
all the other profile paths, you have to then inspect this variable a little more for NoteProperty where
you will observe following Properties and pointing to files for a specific scope listed in the following
table, and you can utilize these scopes to control how you want your session to be configured on
different hosts (Like, VSCode) and users:
Variable Scope
$Profile Current User, Current Host
$Profile.CurrentUserCurrentHost Current User, Current Host
$Profile.CurrentUserAllHosts Current User, All Hosts
$Profile.AllUsersCurrentHost All Users, Current Host
$Profile.AllUsersAllHosts All Users, All Hosts
PowerShell 7 Profile Paths and Locations 10

All Windows PowerShell 5.1 Profile Paths

All PowerShell 7 (Windows) Profile Paths


PowerShell 7 Profile Paths and Locations 11

All PowerShell 7 (Linux) Profile Paths

Profile Guidelines
Below I will provide some guidelines on for setting up your profiles, as provided by Microsoft.
If you use multiple host applications, put the items that you use in all the host applications into your
$Profile.CurrentUserAllHosts profile. Put items that are specific to a host application, such as a
command that sets the background color for a host application, in a profile that is specific to that
host application. If you are an administrator who is customizing PowerShell for many users, follow
these guidelines:

• Store the common items in the $profile.AllUsersAllHosts profile)


• Store items that are specific to a host application in $profile.AllUsersCurrentHost profiles
that are specific to the host application
• Store items for particular users in the user-specific profiles
• Be sure to check the host application documentation for any special implementation of
PowerShell profiles.

This article originally appeared online at https://ridicurious.com/2020/03/12/powershell-7-


profile-paths-and-locations/
PowerShell 7 Experimental Features

by Dave Carroll

Experimental Features Defined


After becoming open-source software, the PowerShell community requested a mechanism for users
to try out new features and provide early feedback to feature developers. This discussion took place
in PowerShell RFC0029¹³ which was finalized and implemented in PowerShell Core 6.1. New features
that are not production ready are deemed experimental in nature. Users can choose to opt-in for an
experimental feature on an individual basis. Administrators can choose to opt-in at the system level.

Please note that user configuration will take precedence over system configuration.

Using the built-in support for experimental features, developers can roll out an alternate command
or a parameter to their modules. Experimental features are not limited to the PowerShell engine
itself.

Experimental Feature Commands


Commands to discover, enable, and disable experimental features are provided to the user.

Get-ExperimentalFeature
The command Get-ExperimentalFeature will display a list of discovered experimental features.
These features can come from the PowerShell engine itself or from modules. You can see where
the experimental feature is defined by looking at the Source column in the command’s output.
Also, features can be specific to an operating system as the following illustrates. Notice the
PSUnixFileStat feature in the Linux output.
¹³https://github.com/PowerShell/PowerShell-RFC/blob/master/5-Final/RFC0029-Support-Experimental-Features.md
PowerShell 7 Experimental Features 13

Experimental Features on Windows

Get-ExperimentalFeature on Windows

Experimental Features on Linux

Get-ExperimentalFeature on Linux

Experimental feature discovery targets the paths in $env:PSModulePath.

Enable-ExperimentalFeature
The Enable-ExperimentalFeature command turns on one or more experimental features for the
current user or all users. Enabling a feature will add it to an array in the ExperimentalFeatures key
in the Powershell configuration file, powershell.config.json. If you do not specify a Scope, it will
default to CurrentUser.
For Windows, the user configuration file will be saved in the $HOME\Documents\PowerShell folder.
For Linux, the user configuration file will be saved in the $HOME\config\powershell folder.

On my system, I re-target my Documents folder to a separate volume. The PowerShell


configuration file is saved there and is not in the $HOME hierarchy.

You can turn on all experimental features in one line. In the following example, I’ve added a sanity
check by getting the content of the configuration file.
PowerShell 7 Experimental Features 14

Enable-ExperimentalFeature All on Windows

Restart Sessions
Take note of the warning message that serves as a reminder to restart the PowerShell session. In fact,
I believe you will need to close all console sessions (those of the same version and platform, that is)
before the change will take effect. Don’t forget the stop the terminal in Visual Studio Code.

Disable-ExperimentalFeature
The Disable-ExperimentalFeature command turns off the experimental feature. As with enabling,
when you disable one or more features, you must close all PowerShell sessions and start a new
session. Disabling the feature removes its entry from the enabled feature list in the appropriate
configuration file. The ExperimentalFeatures key will remain even if you disable all experimental
features.

PSUnixFileStat In Action
Now that we’ve talked about Experimental Features and how to enable/disable them, let’s take
one out for a spin. One of the features that Linux admins would probably appreciate is the
PSUnixFileStat feature. Let’s get some information about it.

PSUnixFileStat Information

The default output for Get-ChildItem looks the same on Windows or Linux.
PowerShell 7 Experimental Features 15

Get-ChildItem Before Enabling PSUnixFileStat

The output doesn’t help the Linux admin with permissions. Let’s enable the feature and correct that.

Enable-ExperimentalFeature PSUnixFileStat on Linux

I close all PowerShell sessions in my WSL instance and start a new one. And now to see the
difference.

Get-ChildItem After Enabling PSUnixFileStat

Those new UnixMode entries look much more useful (and are super cool).
This is PowerShell on Linux!

PSCommandNotFoundSuggestion In Action
Let’s take a quick look at another experimental feature.
PowerShell 7 Experimental Features 16

PSCommandNotFoundSuggestion Sample Output

I meant to type git, not get. With the PSCommandNotFoundSuggestion experimental feature
enabled, PowerShell can suggest commands when we have a typo or just space out.
Thanks, PowerShell!

Adding Support for an Experimental Feature to Your


Module
As I mentioned at the beginning of this article, experimental features are not limited to the
PowerShell engine. In fact, there are a couple delivered with PowerShell 7 within the modules
Microsoft.PowerShell.Utility and PSDesiredStateConfiguration. I wanted to provide the com-
munity a working demo of experimental features, but I couldn’t find any online.

My google-foo is strong, but it either failed me this time or there are no current examples in
the wild.

I wrote a very simple demo module that contains experimental features. You can find it at the bottom
of this article.

Experimental Features Demo Module

Module Manifest
In the Module Experimental Feature¹⁴ section of RFC0029, I found where experimental feature
support can be added to a module manifest. In the PrivateData.PSData section, there is a new
ExperimentalFeatures entry which allows an array of hashtables with Name and Description. This
metadata has been incorporated into the necessary components to update the PSModuleInfo object.
¹⁴https://github.com/PowerShell/PowerShell-RFC/blob/master/5-Final/RFC0029-Support-Experimental-Features.md#module-
experimental-feature
PowerShell 7 Experimental Features 17

Feature Naming
When I was creating the demo module, I originally created a feature name like PSDemoFeature. I
quickly discovered this was not the correct naming scheme for experimental features. It became
evident when I tested my demo module manifest.

PS> Test-ModuleManifest 'C:\Program Files\PowerShell\Modules\Demo


ExperimentalFeatures\DemoExperimentalFeatures.psd1'
Test-ModuleManifest: One or more invalid experimental feature names found:
PSDemoExpFeature. A module experimental feature name should follow this
convention: 'ModuleName.FeatureName'.

Be sure to use the proper naming scheme for your experimental features. The name must be in the
format of ModuleName.FeatureName.

The name of PowerShell engine experimental features is PSDescriptiveText. Once I realized


this, I removed the PS from my feature names to reduce any confusion.

Experimental Attribute
The about_Experimental_Feature¹⁵ documentation goes into detail on how to use the new Experimental
attribute.

[Experimental(NameOfExperimentalFeature, ExperimentAction)]

This attribute can be used for the function or any parameter. The ExperimentAction is an enum with
values of Hide or Show.

• Show will allow the experimental feature to be used when it’s enabled.
• Hide will prohibit the experimental feature to be used when it’s enabled.

They can be used to provide mutual exclusivity between different versions of a command or
parameter.

Additional Information
Refer to the about_Experimental_Feature (referenced above) documentation for examples of C# and
how to check if an experimental feature is enabled. The latter would be necessary when you don’t
need mutual exclusivity and when writing Pester tests for your code.

Demo Module with Experimental Features (Mutually Exclusive)


¹⁵https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_experimental_features?view=powershell-
7
PowerShell 7 Experimental Features 18

#DemoExperimentalFeatures.psd1

@{

RootModule = 'DemoExperimentalFeatures.psm1'

ModuleVersion = '0.7.0'

CompatiblePSEditions = 'Core'
GUID = 'a007643e-c876-4806-b6cb-367963716e98'
Author = 'Dave'
CompanyName = 'thedavecarroll'
Copyright = '(c) Dave. All rights reserved.'

PowerShellVersion = '7.0'

FunctionsToExport = 'Show-HelloWorld', 'Get-LoremIpsum'

PrivateData = @{
PSData = @{

ExperimentalFeatures = @(
@{
Name = 'DemoExperimentalFeatures.ExperimentalFunction'
Description = 'Demo of Experimental Functions'
},
@{
Name = 'DemoExperimentalFeatures.ExperimentalParameter'
Description = 'Demo of Experimental Parameter'
}
)
}
}

}
PowerShell 7 Experimental Features 19

#DemoExperimentalFeatures.psm1

function Show-HelloWorld {
[Experimental("DemoExperimentalFeatures.ExperimentalFunction", "Show")]
[CmdletBinding()]
param()

'PowerShell 7 is here!' | Write-Host -ForegroundColor Yellow


}

function Show-HelloWorld {
[Experimental("DemoExperimentalFeatures.ExperimentalFunction", "Hide")]
[CmdletBinding()]
param()

'PowerShell 7 is shipping soon!' | Write-Host -ForegroundColor Green


}

function Get-LoremIpsum {
[CmdletBinding()]
param(
[Experimental("DemoExperimentalFeatures.ExperimentalParameter", "Show")]
[switch]$Show,

[Experimental("DemoExperimentalFeatures.ExperimentalParameter", "Hide")]
[switch]$Display
)

$LoremIpsum = @"
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed varius mi erat,
in laoreet nibh eleifend eget. Phasellus odio diam, tincidunt rhoncus massa
in, feugiat iaculis mauris. Nulla ornare enim et semper tincidunt. Maecenas
ac tempor quam, in scelerisque lorem. Duis hendrerit urna sapien, ut
pellentesque odio placerat finibus. Mauris molestie nulla ac vestibulum
aliquet. Quisque ut lacus quis lorem venenatis elementum. Morbi hendrerit
odio at nibh faucibus finibus. Vivamus porta elit libero, et porttitor leo
malesuada et. Suspendisse luctus erat sed lectus ultricies laoreet.
"@

$fox = 'The quick brown fox jumps over the lazy dog'

if ($Display) {
Write-Output $LoremIpsum
PowerShell 7 Experimental Features 20

if ($Show) {
Write-Output $fox
}
}

This code is available in the Extras zip file.

Summary
I believe module developers will start delivering experimental features as they migrate to PowerShell
7, especially if their modules support mission critical automation and processes. Thank you for taking
the time to read this article and for being part of the PowerShell community. You are the reason we
do what we do!
If you haven’t already, begin your journey with PowerShell 7 now!

This article originally appeared online at https://powershell.anovelidea.org/powershell/ps7now-


experimental-features/
Getting Started Using SSH with
PowerShell

by Adam Bertram

Once upon a time, you were limited in the kind of computers you could connect to with Windows
PowerShell using PowerShell Remoting¹⁶. Since PowerShell Remoting only supported the WsMan
provider¹⁷, your chances of connecting to computers running Linux, for example, were nil.
Back in 2015 though, Microsoft introduced support for SSH¹⁸ in Windows bringing forth tons of
potential and finally allowing SSH with PowerShell. In this article, we’re going to dive in and see
how to set up Windows’ SSH client and how you can leverage SSH with PowerShell Remoting.

Before You Start


This article will be a walk-through tutorial. If you intend to follow along, be sure you meet the
following prerequisites:

• Windows 10 - All examples will use Windows 10 Build 1903. The example will be in the same
domain as the remote Windows Server.
• PowerShell v6+ installed on Windows 10 - If you don’t already have PowerShell (Core)
installed, find out how here¹⁹. All examples will use PowerShell 7 RC2.
• A remote SSH server - All examples will use a Windows Server SSH machine²⁰. If you’re
connecting to a Linux machine, you can skip the server setup steps.
• TCP port 22 available on the remote server

SSH Client (Windows 10) Setup


Before you open up a PowerShell console, you’ll first need to perform a few one-time tasks to make
sure Windows 10 can connect a remote SSH server.
¹⁶https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/running-remote-commands
¹⁷https://docs.microsoft.com/en-us/powershell/module/microsoft.wsman.management/about/about_wsman_provider
¹⁸https://www.thomasmaurer.ch/2015/06/windows-and-powershell-support-for-ssh/
¹⁹https://adamtheautomator.com/powershell-core-install/
²⁰https://techcommunity.microsoft.com/t5/itops-talk-blog/installing-and-configuring-openssh-on-windows-server-2019/ba-p/309540
Getting Started Using SSH with PowerShell 22

Add the OpenSSH Folder to the PATH environment variable


OpenSSH comes pre-installed on Windows 10 but sometimes needs a little help to work with
PowerShell Remoting. If you’ve already tried to use Enter-PSSession, for example, to connect to a
remote SSH system and came across the error below, you need to ensure the path to the OpenSSH
executable (ssh.exe) in the the PATH environment variable.

Enter-PSSession : An error has occurred which PowerShell cannot handle. A


remote session might have ended.

To add the OpenSSH folder path to the PATH environment variable, click on the Start button and
search for advanced system settings then click on View Advanced System Settings as shown below.

Advanced System Settings

When the System Properties window comes up, click on the Environment Variables button.
Getting Started Using SSH with PowerShell 23

Environmental Variables

In the Environment Variables window, under System variables, select Path and click Edit.
Getting Started Using SSH with PowerShell 24

Editing Variables

In the Edit Environment Variable window, click New then enter the path where the ssh.exe
executable resides. For this tutorial, the path is C:\Program Files\OpenSSH as shown below. Once
input, click OK twice to save the new value.
Getting Started Using SSH with PowerShell 25

Adding OpenSSH

Specify the SSH Subsystem


You’ll now need to specify the SSH subsystem for PowerShell to call on when connecting via SSH
on the remote Windows Server. If you don’t tell PowerShell what subsystem to use, you’ll inevitably
get the following error:
Enter-PSSession : The background process reported an error with the following message:
The SSH client session has ended with error message: subsystem request failed on channel
0.
Getting Started Using SSH with PowerShell 26

Note that if you’re not connecting to a Windows Server machine, you will not need to
perform these steps.

On the remote Windows Server, open the C:\ProgramData\ssh\ssh_config file. Scroll down to the
override default of no subsystems section of the ssh_config file. The section will look like the
screenshot below.

Override Default

Below the existing Subsystem line, add the PowerShell subsystem using the line below:

Subsystem powershell pwsh.exe -sshs -NoLogo -NoProfile

Now save the ssh_config file and restart the sshd Windows service on the remote Windows server
as shown below.

Get-Service -Name sshd | Restart-Service

Once the service restarts, you should now be able to connect via PowerShell Remoting and SSH.

Connecting via PowerShell Remoting and SSH


Now that the configuration is complete, it’s time to test out PowerShell Remoting. First, attempt to
enter an interactive session by using the Enter-PSSession cmdlet. Unlike what you may be used to,
you will not use the ComputerName parameter. Instead, you’ll use the HostName parameter to specify
the remote computer. By default, PowerShell Remoting still uses WsMan. To override the default,
use the SSHTransport parameter to indicate you’d like to connect via SSH. When you attempt to
connect for the first time with a username and password, you’ll need to create an SSH private key.
To do so, you’ll be prompted as you can see below. When you’re prompted, type ‘y’ to accept.

Enter-PSSession with SSH

You’ll then be prompted for the password of the user you’re currently logged in with.

Note that in this example the Windows 10 client and Windows Server SSH server are
in the same Active Directory domain. By default, PowerShell Remoting will attempt to
authenticate with the logged on user. You can also use the Credential parameter to specify
a different username and password.
Getting Started Using SSH with PowerShell 27

After providing the password, you should then be at a familiar PowerShell Remoting prompt.

A Connected SSH Remoting Session

Now exit out of the session with exit and then try out New-PSSession. Notice below using the same
HostName and SSHTransport parameters as before, the Transport now shows up as SSH. You are also
still able to use WsMan as the transport protocol too using the ComputerName parameter without
using the SSHTransport parameter.

PowerShell 7 Remoting Sessions

You aren’t required to use a username and password for SSH. You could instead use a private
key file²¹ and use the KeyFilePath parameter to provide the location.

Don’t forget to clean up your open sessions with Get-PSSession | Remove-PSSession!

Summary
You should now know how to get both Windows 10 and Windows Server set up to use SSH with
PowerShell Remoting. Once configured, the only difference between using WsMan as the transport
protocol and SSH is a couple of parameters! If you’d like to learn more about PowerShell and SSH,
be sure to check out PowerShell Remoting over SSH Microsoft documentation article²².

This article originally appeared online at https://adamtheautomator.com/ssh-with-power-


shell/

²¹https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/create-with-openssh/
²²https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/ssh-remoting-in-powershell-core?view=powershell-7
PowerShell 7 Remoting Cleanup

by Jeffery Hicks

Now that PowerShell 7 is here and hopefully installed on your Windows 10 desktop, you’re good to
go, right? I’d say you probably all set. However, if like me, you were running PowerShell 7 betas or
had PowerShell Core also installed, a little housekeeping might be in order. I don’t think what I’m
going to discuss has any serious implications. But I like to maintain a tidy system and I’m betting
many of you do as well.

PSSessionConfigurations For Everyone


On my Windows 10 desktop, I had been running PowerShell Core, the 6.x version of PowerShell, as
well as the PowerShell 7 betas and release candidates. I enabled remoting over WSMan with each.
Over time, this has left me with multiple PSSessionConfiguration settings.

Leftover Session Configurations

In PowerShell 7, you don’t see the PowerShell 5.1 sessions. I doubt there are any issues with simply
leaving these here, but it feels messy to me. At this point, I’ve already uninstalled PowerShell Core
6.x so I definitely don’t need the related sessions. Digging a little deeper, I can discover what these
sessions are pointing to.
PowerShell 7 Remoting Cleanup 29

Leftover Session Details

Testing Plugin Paths

This surprised me. But it was easily verified.


PowerShell 7 Remoting Cleanup 30

PowerShell Configuration Directory

Even though the PowerShell v6.x editions had long been uninstalled, these locations remained.
Along older PowerShell 7 bits.

Version Cleanup
When you install the final release of PowerShell 7 and enable remoting you will get folders labeled
7 and 7.0.0. The plugin files in each folder are identical. From what I have learned so far this is by
design. There is a generic PowerShell 7 folder and then a version specific folder, 7.0.0. Presumably,
at some point in the future I’ll install something like PowerShell 7.1.0 which will overwrite the 7.0.0
folder. I’m trying to learn more about this, but for now that doesn’t factor into my cleanup tasks.
My clean up will consist of removing the obsolete files and folders and unregistering the obsolete
PSSessionConfigurations.
PowerShell 7 Remoting Cleanup 31

#requires -version 7.0

[cmdletbinding(SupportsShouldProcess)]
Param()

#clean up folders
Get-ChildItem -path $env:windir\system32\powershell -directory |
Where-Object {$_.name -notmatch "^7(\.0\.0)?$"} | Remove-Item -Force -Recurse

#clean up sessions
Get-PSSessionConfiguration | Where-object {$_.name -notmatch "^Powershell\.7(\.0\.0)\
?$"} |
Unregister-PSSessionConfiguration -Force -NoServiceRestart

Write-Warning "You will need to restart the WinRM service for these changes to take \
effect"

The script file uses a regular expression pattern to keep the current PowerShell 7.0.0 files and sessions.
I also included support for -WhatIf.

Cleanup Script

I figured I was all set. But apparently not.


PowerShell 7 Remoting Cleanup 32

Cleanup Error

I suspect that I should have removed the session configuration before removing the files. So I have
revised my script.

[cmdletbinding(SupportsShouldProcess)]
Param()

#clean up sessions
$sessions = Get-PSSessionConfiguration | Where-Object {$_.name -notmatch "^Powershel\
l\.7(\.0\.0)?$"}
foreach ($session in $sessions) {
Try {
Unregister-PSSessionConfiguration -Name $session.name -Force -NoServiceResta\
rt -ErrorAction Stop
}
Catch {
#Fall back to this if the cmdlet fails
winrm delete http://schemas.microsoft.com/wbem/wsman/1/config/plugin?Name=$(\
$session.name)
}
}

#clean up folders
Get-Childitem -path $env:windir\system32\powershell -directory |
Where-object {$_.name -notmatch "^7(\.0\.0)?$"} | Remove-Item -Force -Recurse

In this version, if Unregister-PSSessionConfigurations fails, I’ll fall back to the winrm command
line tool. However, when I try again, I still get errors. In fact, I appear to have completely botched
things up. But that’s ok. That’s how I can learn more. Any perhaps some of you might encounter
equally trashed environments.
PowerShell 7 Remoting Cleanup 33

Digging Deeper
What I should have done, is backed up the files before I deleted them. Or created a system restore
point. You should keep that in mind. In my case, I’m going to need to go an alternate route
because even after restarting the WinRM service, My WSMan configuration is still looking for plugin
configurations. These are stored in the registry.

Registry related settings

I’m working under the assumption that if I remove the obsolete registry entries, I can get over the
Get-PSSessionConfiguration error.

$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin"
#get the PowerShell plugins
$ps = Get-ChildItem -path $regPath | Where {$_.PSChildName -match "^PowerShell"}
#filter the ones to remove
$remove = $ps | where-object {$_.pschildname -notmatch "^Powershell\.7(\.0\.0)?$"}
$remove | Remove-Item -Recurse -Force #-WhatIf

I tested with -WhatIf to verify I’m removing the correct entries. Once those entries are deleted, I
finally have the PSSessionConfigurations I want.
PowerShell 7 Remoting Cleanup 34

Get final session configurations

Summary
You don’t have to automate this type of cleanup. But if you do, it seems you should unregister the
PSSessionConfiguration and then delete any left over files or registry keys that remain. Personally,
I’m looking forward to someday using SSH for all my PowerShell remoting needs. But for now,
WSMan still plays a role and might need a little attention every now and then.

This article originally appeared online at https://jdhitsolutions.com/blog/powershell-


7/7347/powershell-7-remoting-cleanup/
ForEach Parallel

by Jonathan Medd

Prior to PowerShell version 7 there were a number of different methods for running PowerShell tasks
in parallel, for example creating your own runspaces manually or using a third-party module such
as Invoke-Parallel²³ or PoshRSJob²⁴ to make that a little more straightforward. Not to be confused
of course with the foreach -parallel²⁵ language construct, which exists, but is only available via
a Windows PowerShell workflow. For an excellent summary of the different options prior to v7,
I would highly recommend checking out Richard Siddaway’s talk²⁶ from PSDayUK 2019 which
provides some great insight.
In this article we’ll look at how things were prior to this new feature in PowerShell 7 and why you
might want to consider using the new Parallel feature to make things simpler.
First of all, the following example demonstrates how to use PowerShell runspaces to process some
data in parallel - this is an approach we could have taken prior to PowerShell 7.

# Modified Example From: https://blog.netnerds.net/2016/12/runspaces-simplified/

$users = Invoke-RestMethod -Uri 'https://api.github.com/users'

$pool = [RunspaceFactory]::CreateRunspacePool(1, 5)
$pool.ApartmentState = "MTA"
$pool.Open()
$runspaces = @()

$scriptblock = {
Param (
[string]$login,
[string]$id
)

Write-Output "GitHub User $login has ID $id"


²³https://github.com/RamblingCookieMonster/Invoke-Parallel
²⁴https://github.com/proxb/PoshRSJob
²⁵https://docs.microsoft.com/en-us/powershell/module/psworkflow/about/about_foreach-parallel?view=powershell-5.1
²⁶https://www.youtube.com/watch?v=coEaJsBdwM0
ForEach Parallel 36

$Seconds = (Get-Random -Minimum 1 -Maximum 10)


Start-Sleep -Seconds $Seconds
}

$users | ForEach-Object {

$runspace = [PowerShell]::Create()
$null = $runspace.AddScript($scriptblock)
$null = $runspace.AddArgument($_.login)
$null = $runspace.AddArgument($_.id)
$runspace.RunspacePool = $pool
$runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvok\
e() }
}

while ($runspaces.Status -ne $null){

$completed = $runspaces | Where-Object { $_.Status.IsCompleted -eq $true }

foreach ($runspace in $completed){

$runspace.Pipe.EndInvoke($runspace.Status)
$runspace.Status = $null
}
}
$pool.Close()
$pool.Dispose()

<# sample output


GitHub User defunkt has ID 2
GitHub User wycats has ID 4
GitHub User mojombo has ID 1
GitHub User ezmobius has ID 5
GitHub User evanphx has ID 7
GitHub User vanpelt has ID 17
GitHub User ivey has ID 6
GitHub User pjhyett has ID 3
>

You’ll observe that quite a lot of code was required to instantiate using the runspaces! In PowerShell
7 we can reduce the above example to the following:
ForEach Parallel 37

$users = Invoke-RestMethod -Uri 'https://api.github.com/users'

$users | ForEach-Object -Parallel {

Write-Output "GitHub User $($_.login) has ID $($_.id)"

$Seconds = (Get-Random -Minimum 1 -Maximum 10)


Start-Sleep -Seconds $Seconds
}

Of course, bear in mind the above are contrived examples and using the new -Parallel functionality
in v7 (or runspaces in earlier versions) may not always be the most effective approach. Typically
running in parallel is geared towards code which requires intensive processing over a significant time
period or contains elements of waiting which might cause bottlenecks if running in serial fashion.
If this is not the case, then it may prove counter intuitive to make use of parallel functionality since
there is overhead every time a runspace is created.

This article originally appeared online at https://www.jonathanmedd.net/2020/03/powershell-


7-new-feature-foreach-object-parallel.html
PowerShell 7 Changes to JSON
Cmdlets

by Dave Carroll

PowerShell 7 JSON Cmdlets

With the official release of PowerShell 7, we wanted to cover some of the changes that demonstrate
the efficacy of adopting the newest, fastest, and best PowerShell. In this article, we will be looking
at the JSON cmdlets - ConvertFrom-Json, ConvertTo-Json, and the new addition Test-Json.

Test-Json was technically introduced in PowerShell Core 6.2.

JSON Cmdlet Changes


The Convert*-Json cmdlets were introduced with Windows PowerShell 3.0 in late 2012. Since the
release of Windows PowerShell 5.1 in early 2017, there have been several improvements to the
cmdlets, including updates to the underlying dependencies.

Many of these improvements have been driven by community members just like you via the
PowerShell GitHub repository²⁷ through issues, comments, voting, and even pull requests.

Now, without further adieu, let’s check them out.


²⁷https://github.com/PowerShell/PowerShell
PowerShell 7 Changes to JSON Cmdlets 39

ConvertFrom-Json
Comparing the syntax for the ConvertFrom-Json cmdlets from Windows PowerShell 5.1 and
PowerShell 7, we can see that the new cmdlet has three new parameters.

PS> Get-Command -Name ConvertFrom-Json -Syntax

# Windows PowerShell 5.1


ConvertFrom-Json [-InputObject] <string> [<CommonParameters>]

# PowerShell 7
ConvertFrom-Json [-InputObject] <string> [-AsHashtable] [-Depth <int>] [-NoEnumerate\
] [<CommonParameters>]

We won’t be focusing on the existing parameters (which, in reality is just one), but we will examine
each of the new ones in greater detail in the next few sections.

-AsHashtable
Originally introduced in PowerShell Core 6.0 and updated in later releases, this switch parameter al-
lows the cmdlet to overcome a few limitations of outputting converted JSON to a [PsCustomObject].
Specifically, a [PsCustomObject] has the following limitations:

• Property names cannot be empty


• Property names are case insensitive
• Slower than a [Hashtable] to add new properties
• Slower than a [Hashtable] to search

What was the original behavior for the cmdlet in 5.1?

PS> $validJson = @'


{
"array": [
1,
2,
3
],
"boolean": true,
"null": null,
"number": 123,
"object": {
PowerShell 7 Changes to JSON Cmdlets 40

"a": "b",
"c": "d"
},
"string": "Hello World",
"String": "Party On!",
"" : "Empty Property Name1"
}
'@

PS> $validJson | ConvertFrom-Json


ConvertFrom-Json : Cannot convert the JSON string because a dictionary that was conv\
erted from the string contains the
duplicated keys 'string' and 'String'.
At line:1 char:14
+ $validJson | ConvertFrom-Json
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [ConvertFrom-Json],
InvalidOperationException
+ FullyQualifiedErrorId : DuplicateKeysInJsonString,Microsoft.PowerShell.
Commands.ConvertFromJsonCommand

Despite using valid JSON input, the older cmdlet would stop your code in its tracks.
No bueno.
Now, what about the new cmdlet behavior?

PS> $validJson | ConvertFrom-Json


ConvertFrom-Json: Cannot convert the JSON string because it contains keys with
different casing. Please use the -AsHashTable switch instead. The key that was
attempted to be added to the existing key 'string' was 'String'.

This returns a very succinct and descriptive error, due to the new default $ErrorView of ConciseView
in PowerShell 7. As suggested by the error message, the -AsHashtable switch will come to the rescue!
PowerShell 7 Changes to JSON Cmdlets 41

PS> $validJson | ConvertFrom-Json -AsHashtable

Name Value
---- -----
array {1, 2, 3}
null
String Party On!
Empty Property Name1
boolean True
string Hello World
object {a, c}
number 123

The addition of this parameter makes ConvertFrom-Json play nicer with valid JSON and will give
you a way to speed up your code when dealing with large datasets by manipulating the hashtable
instead.

If you want to see how the community contributed to this parameter, check out issues #3623²⁸
and #3159²⁹.

-Depth
Introduced in PowerShell Core 6.2, this parameter allows you to set the maximum depth of JSON
input. It was named to align with a similar parameter of ConvertTo-Json. In Windows PowerShell
5.1, if you attempted to convert a greater depth than 101, you would get a ConvertFrom-Json :
RecursionLimit exceeded. (606) error and a sea of red in your console. In PowerShell 7, Get-Help
-Name ConvertFrom-Json -Full reveals that -Depth parameter accepts type [Int32] and has a
default value of 1024. This is already a great improvement over the older cmdlet.

A discussion in issue #3182³⁰, which continued into pull request #8199³¹, focused on
increasing the default value. The decision was to add the parameter to allow the user to
exceed the default depth, up to [int]::MaxValue.

Now let’s see it in action.

²⁸https://github.com/PowerShell/PowerShell/issues/3159
²⁹https://github.com/PowerShell/PowerShell/issues/3623
³⁰https://github.com/PowerShell/PowerShell/issues/3182
³¹https://github.com/PowerShell/PowerShell/pull/8199
PowerShell 7 Changes to JSON Cmdlets 42

PS> $depth = 1025


PS> $val = ""
PS> $end="null"
PS> 1..$depth | % {
$val += '{"' + $_+'":'
$end += '}'
}

PS> $val + $end | ConvertFrom-Json

This fails with the below error.

ConvertFrom-Json: Conversion from JSON failed with error: The reader's MaxDepth
of 1024 has been exceeded.

But by specifying an appropriate -Depth, the command will convert the JSON input correctly.

PS> $val + $end | ConvertFrom-Json -Depth 1025

If you consistently deal with JSON having a depth larger than 1024, you should consider using
$PSDefaultParameterValues near the beginning of your scripts. Here is an example of doubling the
default maximum depth.

$PSDefaultParameterValues=@{"ConvertFrom-Json:Depth"=2048}

-NoEnumerate
The last parameter for ConvertFrom-Json we are going to examine is -NoEnumerate. From the
Microsoft documentation³²:

• Specifies that output is not enumerated.


• Setting this parameter causes arrays to be sent as a single object instead of sending every
element separately.
• This guarantees that JSON can be round-tripped via ConvertTo-Json.

Previous to PowerShell 7, the default behavior for the ConvertFrom-Json cmdlet was to not
enumerate arrays by default. This lead to confusion as it went against the behavior of how other
cmdlets sent multiple objects through the pipeline. Consider the following:

³²https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-json?view=powershell-7
PowerShell 7 Changes to JSON Cmdlets 43

# Windows PowerShell 5.1


PS> ('[1,2]' | ConvertFrom-Json | Measure-Object).Count
1

The array, i.e. collection, of two integers should be seen as having two members in the output, but
that is not the case.

After a discussion beginning in issue #3424³³, the PowerShell committee decided on a


breaking change³⁴ for the cmdlet to align it with the others. The new behavior is to unwrap
collections by default.

To handle the previous behavior, the -NoEnumerate switch was added to the cmdlet, which aligns
to the implementation in the Write-Output object. Let’s perform the same actions as the example
above:

# PowerShell 7
PS> ('[1,2]' | ConvertFrom-Json | Measure-Object).Count
2

PS> ('[1,2]' | ConvertFrom-Json -NoEnumerate | Measure-Object).Count


1

Incidentally, I believe the issue referenced above was the most discussed for the ConvertFrom-Json
cmdlet.
Your voice matters!

ConvertTo-Json
Now, let’s compare the syntax for the ConvertTo-Json cmdlets from Windows PowerShell 5.1 and
PowerShell 7.

³³https://github.com/PowerShell/PowerShell/issues/3424
³⁴https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/breaking-change-contract.md
PowerShell 7 Changes to JSON Cmdlets 44

PS> Get-Command -Name ConvertTo-Json -Syntax

# Windows PowerShell 5.1


ConvertTo-Json [-InputObject] <Object> [-Depth <int>] [-Compress]
[<CommonParameters>]

# PowerShell 7
ConvertTo-Json [-InputObject] <Object> [-Depth <int>] [-Compress]
[-EnumsAsStrings] [-AsArray] [-EscapeHandling <StringEscapeHandling>]
[<CommonParameters>]

This cmdlet also has three new parameters (though, I’m sure it was just a coincidence). Likewise, we
will examine each of these new parameters in the following sections.

-EnumsAsStrings
JSON is used heavily in serialization, which essentially is translating a complex object to a simple
object (typically a string representation) and vice versa. Serialization is used extensively in web
applications and APIs.

Enum Backgrounder
An enumerated type, or enum, is a data type that enables a variable to be a set of predefined constants.
The value of the enum is a zero-based index, beginning with the first item.
For example, if you wanted to define a selection of car types in a script, you could use the following
to create the enum then retrieve its value:

PS> enum CarTypes {


Compact
MidSize
Intermediate
SUV
Luxury
}

PS> [CarTypes]::SUV.value__
3

The -EnumsAsStrings parameter instructs the cmdlet to output enums as their string representations,
so as to ensure the data remains meaningful. Continuing with the Enum example above, the following
statements demonstrate this usefulness of this switch.
PowerShell 7 Changes to JSON Cmdlets 45

PS> [CarTypes]::SUV,[CarTypes]::Compact | ConvertTo-Json


[
3,
0
]

PS> [CarTypes]::SUV,[CarTypes]::Compact | ConvertTo-Json -EnumsAsStrings


[
"SUV",
"Compact"
]

-AsArray
The -AsArray switch, suggested in issue #6327³⁵, instructs the cmdlet to wrap the output object in
array brackets. This guarantees that the pipeline input can be treated as an array, whether it’s a
single item or not.

# single item
PS> "one" | ConvertTo-Json
"one"

PS> "one" | ConvertTo-Json -AsArray


[
"one"
]

-EscapeHandling
The last parameter for ConvertTo-Json that we will cover is -EscapeHandling which was introduced
in PowerShell Core 6.2.

Issue #7693³⁶ identifies unexpected behavior from Windows PowerShell 5.1 and PowerShell
Core 6 in how special characters are escaped.

While the default behavior remains unchanged, this parameter allows the user to properly escape
non-ASCII and HTML characters. Possible values with an example for each are:

³⁵https://github.com/PowerShell/PowerShell/issues/6327
³⁶https://github.com/PowerShell/PowerShell/issues/7693
PowerShell 7 Changes to JSON Cmdlets 46

# Default - Only control characters are escaped.


PS> @{ 'abc' = "'def'" } | ConvertTo-Json -EscapeHandling Default
{
"abc": "'def'"
}

# EscapeNonAscii - All non-ASCII and control characters are escaped.


PS> @{ 'newline' = "`n" } | ConvertTo-Json -EscapeHandling EscapeNonAscii
{
"newline": "\n"
}

# EscapeHtml - HTML (<, >, &, ', ") and control characters are escaped.
PS> @{ 'Html' = '<a href="https://powershell.anovelidea.org">Thanks for reading my
blog!</a>' } | ConvertTo-Json -EscapeHandling EscapeHtml
{
"Html": "\u003ca href=\u0022https://powershell.anovelidea.org\u0022\u003eThanks
for reading my blog!\u003c/a\u003e"
}

Introducing Test-Json
The last JSON cmdlet that we will examine is the Test-Json. It allows you to validate JSON input
against proper syntax and against a defined JSON Schema. Before we discuss the Test-Json cmdlet,
let’s take a short detour to gain a better understanding of JSON Schema.

JSON Schema
For some of you, this will be the first time that you’re hearing about JSON Schema.
In fact, I had worked with JSON for a while before realizing, just last year, that there is an IETF
JSON Schema draft³⁷. This draft serves to define the structure of a given JSON object type. Prior to
this, the contents of a JSON object were at the discretion of the developer or scripter. Within small
teams, there could be some differences between two objects that ultimately refer to the same type.
Let’s take an example of a Person object.

³⁷http://json-schema.org/specification.html
PowerShell 7 Changes to JSON Cmdlets 47

Name
Age
Date of Birth

This Person object is fairly simple. Perhaps too simple. Where do you put the first name? Or last
name? Nickname? Writing unit tests or even code against the moving target of the previous Person
object in this team would be tedious and prone to failures or bugs. Consider the following new
Person object JSON schema.

$personSchema = @'
{
"$id": "https://example.com/person.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"required": [ "firstName", "lastName", age ],
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"lastName": {
"type": "string",
"description": "The person's last name."
},
"nickName": {
"type": "string",
"description": "The person's nick name."
},
"age": {
"description": "Age in years which must be equal to or greater than eighteen.",
"type": "integer",
"minimum": 18
}
}
}
'@

This schema will ensure the team will use consistent property names and data types for each
property. Actually, there is an error I introduced purposefully that we will discover shortly.
PowerShell 7 Changes to JSON Cmdlets 48

Validate JSON Basic Syntax with Test-Json


As I mentioned previously, the Test-Json cmdlet has two primary functions. The first is to validate
the syntax of the JSON input. Before this cmdlet, the only PowerShell way to validate JSON was
to use ConvertFrom-Json | ConvertTo-Json in the pipeline. For reasons gleaned from the sections
above for both of these cmdlets, this method was often fallible.

PS> $personSchema | Test-Json


Test-Json: Cannot parse the JSON.
False

PS> (Get-Error).Exception.InnerException.Message
Unexpected character encountered while parsing value: a. Path 'required[1]', line 6,
position 41.

Ah! I completely forgot to enclose the required field age in double quotes.

# bad syntax
"required": [ "firstName", "lastName", age ],

# valid syntax
"required": [ "firstName", "lastName", "age" ],

After making the change above, let’s see the updated output.

PS> $personSchema | Test-Json


True

Much better.

Validate JSON Schema with Test-Json


Continuing with the Person schema that we defined above, let’s focus on the second function of
the Test-Json cmdlet. That is, we will test a JSON object against the Person schema that we have
defined.
PowerShell 7 Changes to JSON Cmdlets 49

PS> $self = @'


{
"firstName": "Dave",
"lastName": "Carroll"
}
'@

PS> $self | Test-Json -Schema $personSchema


Test-Json: PropertyRequired: #/age
False

Looks like I forgot to include my age in the object. Let’s correct that.

PS> $self = @'


{
"firstName": "Dave",
"lastName": "Carroll",
"age": 1000
}
'@

PS> $self | Test-Json -Schema $personSchema


True

Though the JSON object is validated correctly against the schema, an age of 1000 is highly unlikely.
We can adjust the schema to handle real world data.

"age": {
"description": "Age in years which must be equal to or greater than eighteen.",
"type": "integer",
"minimum": 18,
"maximum": 120
}

Let’s try that again.

PS> $self | Test-Json -Schema $personSchema


Test-Json: NumberTooBig: #/age
False

We don’t have to correct my age in the example. And that’s how you validate JSON objects in
PowerShell 7, both for syntax and against a predefined schema.
PowerShell 7 Changes to JSON Cmdlets 50

In my blog post on Writing Windows Events with Smart EventData³⁸, I mention using
EventData schema for each event type that you want to write. Using JSON Schema and
the Test-Json cmdlet would help your team with documentation and implementation of
consistent Smart EventData.

Community Input
If you’re interested in seeing how much the PowerShell community has shaped the present (and
future) of PowerShell, check out the PowerShell GitHub BI Community Dashboard³⁹ page with
with Pull Requests and Issues By Community and Microsoft.

Summary
As you can see, there have been a great number of improvements with just these cmdlets. Imagine
all of the other commands and their improvements. It’s a great time for scripters of all experience
levels.
Thank you for your interest in the JSON cmdlets in PowerShell 7. And thank you for being part
of the community. You are the reason we do what we do. I hope you’ve found this interesting or
informative.
Thanks for reading!
And, if you haven’t already, begin your journey with PowerShell 7 now!

This article originally appeared online at https://powershell.anovelidea.org/powershell/ps7now-


changes-to-json-cmdlets/

³⁸http://bit.ly/38hFiDZ
³⁹http://bit.ly/2SMofDf
Staying Up to Date on PowerShell
Releases with Update Notifications

by Josh King

If you’re not stalking the PowerShell team on Twitter, or obsessively refreshing the PowerShell repo
on GitHub, how are you meant to be aware of new releases?

During this week of blog posts about the latest PowerShell release⁴⁰, you’ll have read a lot about
many of the new features it delivers and with any luck you’ve been sold on it and installed⁴¹/updated
it. But now you’re itching for the next release and the next round of new features and bug fixes,
right? How on earth are you meant to know when those are out? Luckily, PowerShell 7 shipped with
an answer: Update Notifications⁴².
⁴⁰https://github.com/powershell/powershell/releases
⁴¹https://toastit.dev/2020/03/08/ps7now-installing/
⁴²https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_update_notifications?view=powershell-7
Staying Up to Date on PowerShell Releases with Update Notifications 52

Example update notification from RC3 to GA

Once in a given 24 hours window, PowerShell checks to see if there is a new version available.
This check happens three seconds (or more) after you’ve opened PowerShell for the first time in
that window to limit any impact on performance. Any subsequent time you open up a PowerShell
session you’ll see the notification itself.

What Notifications Will I See


By default, a generally available (GA) release will only be notified about the next GA release. Any
preview and release candidate will be notified about all preview and GA releases. What’s really cool,
especially if you were only allowed to install PowerShell 7 in your environment because it has Long
Term Support (LTS), is that you can specify that you only want to be notified about the next LTS
release and not the releases in between. Support is complicated, there’s a page⁴³ with info specifically
for PowerShell. The short and simplified version is that PowerShell 7’s LTS is locked to that of .NET
Core 3.1’s release on 3 December 2019. Support lasts through until 3 December 2022. “Current” (the
GA releases that aren’t LTS) releases are supported until three months after the release of the next
current release.
In order to set notification, you’ll want to configure an environment variable.

$Env:POWERSHELL_UPDATECHECK = 'LTS'

Note though that this only applies to your current session, so for that to work you’ll want to add it
to your profile⁴⁴, or make it a persistent variable⁴⁵.
⁴³https://docs.microsoft.com/en-nz/powershell/scripting/powershell-support-lifecycle?view=powershell-7#lifecycle-of-powershell-7
⁴⁴https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7
⁴⁵https://trevorsullivan.net/2016/07/25/powershell-environment-variables/
Staying Up to Date on PowerShell Releases with Update Notifications 53

How Do I Turn Them Off


Spoilsport, but I won’t begrudge you the option! Much like setting the environment variable for only
getting LTS notifications, you can also specify that you don’t want any. You do this by setting the
variable to “Off”.

$Env:POWERSHELL_UPDATECHECK = 'Off'

In the interest of completeness, you can also set this to “Default” to set everything back to how it
was out of the box.

$Env:POWERSHELL_UPDATECHECK = 'Default'

This has the same effect as the environment variable not being defined at all, so if you do persist it
and want to go back to default you can just remove the variable completely.

What’s Happening Under the Hood


There are three JSON endpoints out there, the one that is checked depends on the release used and
the previously discussed environment variable.

• LTS Only Preference = https://aka.ms/pwsh-buildinfo-lts


• Default Preference on GA Release (Stable) = https://aka.ms/pwsh-buildinfo-stable
• Default Preference on Preview Release = https://aka.ms/pwsh-buildinfo-preview

Note: Preview installs actually check both the stable and preview endpoints. You won’t see
GA info by querying the preview uri.

Because this data is available to anyone and everyone, you can really get your geek on and build your
own update notifications. Maybe display a toast notification⁴⁶ in the OS or send out an automatic
tweet⁴⁷?
I’ll leave that one to your imagination.
⁴⁶https://www.powershellgallery.com/packages/PoshNotify
⁴⁷https://www.powershellgallery.com/packages/PSTwitterAPI
Staying Up to Date on PowerShell Releases with Update Notifications 54

Credit
Hero image by Birmingham Museums Trust⁴⁸ on Unsplash⁴⁹

This article originally appeared online at https://toastit.dev/2020/03/13/ps7now-update-


notifications/

⁴⁸https://unsplash.com/@birminghammuseumstrust?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
⁴⁹https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
PowerShell Markdown Cmdlets

by Josh Duffney

Introduction
The vast majority of the technical documentation written today is written in Markdown. From
Jane’s dev blog to Microsoft’s PowerShell documentation, Markdown is behind it. Markdown is a
light weight markup language with plain-text-formatting syntax. Markup languages were designed
to be easy to write using a generic text editor and easy to read in its raw from without rendering.
Markdown’s success is largely in part because it does this very well. It is easy to read without being
rendered. It also does not require a bunch of opening and closing tags. Which are difficult to read
and time consuming to type. Markdown has an extensive list of features that allow you to; style font,
define headings, create tables, create hyperlinks, define code snippets, and much more. Recently, a
few new Markdown cmdlets were introduced to PowerShell. These cmdlets allow you to work with
Markdown from the terminal. They allow you to render the Markdown as HTML or to view it within
a terminal window using AsVT100 Encoding. In this blog post, you’ll learn how to use these cmdlets
to render and output Markdown using PowerShell.

Converting from Markdown to HTML or AsTV100


Encoded Strings
The ConvertFrom-Markdown cmdlet converts the contents of a string or file to a MarkdownInfo
object. Once converted (rendered) into a MarkdownInfo object it can be displayed in two ways,
as html or as a VT100-encoded string. If you choose to convert the Markdown to html, you can
either view the html code within the terminal to confirm it rendered properly. Choosing to convert
it to a VT100-encoded string specifies if the output should be encoded as a string with VT100 escape
codes. ANSI VT100 escape sequences are then used to provide special syntax highlighting within
the terminal. The value this cmdlet provides is the ability to generate rendered output. For example
when you provide a URL within a Markdown file ConvertFrom-Markdown will add the necessary
html syntax to make it a link within html. You do not have to type a paragraph tag or an anchor
tag around the url. The ConvertFrom-Markdown cmdlet will do that for you. CovertFrom-Markdown
uses markdig which is a Markdown processor for .Net. You can view many of the supported features
PowerShell Markdown Cmdlets 56

and examples on the markdig⁵⁰ GitHub page. Below are just a few of the things you can do with the
ConvertFrom-Markdown cmdlet to render Markdown to html or to a VT100-encoded string.

Auto Generate Links


'http://duffney.io/' | ConvertFrom-Markdown

ConvertFrom-Markdown

Creating a Task List


$taskList = @"
- [ ] Item1
- [x] Item2
- [ ] Item3
- Item4
"@
$taskList | ConvertFrom-Markdown

Emphasis
'**#PS7** >_ ~~Soon~~ ^Now!^ ' | ConvertFrom-Markdown -AsVT100EncodedString

Notice that some of the styling does not display when converted to a VT100 encoded string.
It will however, display properly when rendered in html and displayed in a web browser.

Markdown Emphasis

Learn more about the Markdown emphasis⁵¹ with markdig.

⁵⁰https://github.com/lunet-io/markdig
⁵¹https://github.com/lunet-io/markdig/blob/master/src/Markdig.Tests/Specs/EmphasisExtraSpecs.md
PowerShell Markdown Cmdlets 57

Displaying Markdown with PowerShell


Converting Markdown to html or to VT100-encoded strings is neat, but it doesn’t help give you an
idea of what the rendered Markdown will look like. That is where the Show-Markdown cmdlet comes
in. This cmdlet allows you to view the MarkdownInfo object through either a web browser or within
the terminal using ANSI escape sequences to provide color coding and other visual aids.

Show in a Web Browser


The Show-Markdown cmdlet has a -UseBrowser parameter that will display the html output from
ConvertFrom-Markdown in your web browser. This gives you the ability to see what the end result of
the Markdown will look like. Using a Markdown definition list as an example, you can create the
Markdown using a here-string in PowerShell and store it in a variable. You then pipe that stored
variable to the ConvertFrom-Markdown cmdlet which will convert the Markdown to html. After the
Markdown is converted to html you pipe that to the Show-Markdown cmdlet with the -UseBrowser
parameter. The end result will be the Markdown within the here string displayed as an html page
in your browser!

#requires -version 7.0

$defList = @"
PowerShell
: PowerShell is a task-based command-line shell and scripting language built on
.NET. PowerShell helps system administrators and power-users
rapidly automate tasks that manage operating systems (Linux, macOS, and
Windows) and processes.
> Supported and experimental platforms:

- Windows
- Linux
- macOS
- ARM (including Windows 10 ARM32/ARM64 and Raspbian)

``````powershell
Test
``````

[Getting Started with Windows PowerShell](https://docs.microsoft.com/en-us/power\


shell/scripting/getting-started/getting-started-with-windows-powershell?view=powersh\
ell-7)
: [Sample scripts for system administration](https://docs.microsoft.com/en-us/powe\
rshell/scripting/samples/sample-scripts-for-administration?view=powershell-7)
PowerShell Markdown Cmdlets 58

"@

$defList | ConvertFrom-Markdown | Show-Markdown -UseBrowser

Show in Browser

This code is available in the Extras zip file.

Show Markdown Within the Terminal


For those of us who prefer the command line over graphical interfaces there is also an option for
you. The Show-Markdown cmdlet also supports outputting Markdown in the terminal. It does this by
using ANSI escape sequences to add text style and coloring to the Markdown being displayed. You
can get the current ANSI escape sequences used to modify the display with the Get-MarkdownOption
cmdlet. You’ll notice different ANSI escape sequences are used for all six different header options.
In addition to the headers; code, links, images, bold and italics also have unique sequences. The
numbers represented in the sequence are arguments that are pasted to the ANSI function m.
PowerShell Markdown Cmdlets 59

Get-MarkdownOption

Header2 for example has a sequence of [4;93m. The number 4 indicates that the text will be
underlined. Following the 4 and after a semi colon is the number 93, which represents an orange
foreground color. The function m that is after 93 is fed those arguments resulting with all Header2
being displayed in an orange foreground and underlined.

'## Header2' | Show-Markdown

Header 2

Changing the Markdown Options


You have the ability to change the escape sequences being used to style the different properties
of the Markdown output. You can do that by directly using the Set-MarkdownOption cmdlet or by
storing the current settings in a variable and then passing that PSMarkdownOptionInfo object to
the Set-MarkdownOption cmdlet. Using some sample Markdown you can get a better idea of what
it will look like displayed in the terminal. When using a dark themed terminal the Code highlight
is washed out and difficult to see. Also italics isn’t italic. Both of these can be changed using the
Set-MarkdownOption cmdlet to change the escape sequence associated with that property.

The current Code property uses the ANSI escape sequence [107;95m, 107 is modifying the back-
ground to a bright white color and 95 is changing the foreground color to a light purple color. These
numbers are within a range defined in the SGR (Select Graphic Rendition) that set display attributes.
PowerShell Markdown Cmdlets 60

The range for bright background color is 100-107 and the range for bright foreground color is 90-97.
Using some PowerShell and an ANSI escape sequence you can output all the different combinations
and pick one that sticks out to you.

$sample = @"
# Heading1

## Heading2

### Heading3

``Get-Help Show-Markdown -Online``

[Show-Markdown PowerShell Docs](https://docs.microsoft.com/en-us/powershell/module/M\


icrosoft.PowerShell.Utility/Show-Markdown?view=powershell-7)

**Bold**

*Italics*
"@

$sample | Show-Markdown

Sample Markdown
PowerShell Markdown Cmdlets 61

# Display all bright foreground and background color combinations


$fgColors = '90','91','92','93','94','95','96','97'
$bgColors = '100','101','102','103','104','105','106','107'

foreach ($fgColor in $fgColors)


{
foreach ($bgColor in $bgColors){
Write-Output "`e[$($fgColor);$($bgColor)m[$bgcolor;$fgcolor`m Get-Help Show\
-Markdown -Online`e[0m"
}
}

ANSI BrightColors

Choosing the third sequence that was outputted from the above script you can now modify the
Code property and which will change the Markdown options and ultimately the colors you see
on the screen. The simplest way to do this is to use the Set-MarkdownOption cmdlet with the
-Code parameter. Giving that parameter the value of [102;90m will change the code sections being
displayed to a green background with a fadded black foreground color. You do not include any
escape characters as part of the sequence.
PowerShell Markdown Cmdlets 62

Set-MarkdownOption -Code '[102;90m'

'Get-Help Show-Markdown -Online' | Show-Markdown

Custom Code Property

Conclusion
It is now possible with PowerShell alone to generate html files from Markdown. That html code can
be used for blog posts, wiki documents, or other projects you might prefer to write in Markdown
first. While you might not want to completely switch your authoring workflow to the terminal,
perhaps a nice break from your daily IDE is all you need. In that case, you can still author and view
Markdown all within the comfort of your terminal of choice. It is also a great way to learn some
ANSI escape sequences. Not everything you learn as to be practical. Sometimes just learn for fun!
Shout out to @rjpleau who wrote about PowerShell and Markdown⁵² when it was introduced in
PowerShell 6.1.

This article originally appeared online at http://duffney.io/UsingPowerShellMarkdownCmdlets

⁵²https://ephos.github.io/posts/2018-8-1-PowerShell-Markdown#in-the-console
Changes to Invoke-RestMethod in
PowerShell 7

by Tommy Maynard

The Invoke-RestMethod cmdlet has been with us since PowerShell 3.0. For those of us paying
attention to PowerShell during the 2.0 to 3.0 transition, it was a huge release and the inclusion
of this new command was only one of the reasons why. From that point forward, we had a built-
in, PowerShell way to interact with RESTful Web Services. REST (Representational State Transfer)
web services accept HTTP(S) requests, and respond with structured data, often in the form of JSON.
This structured data is converted by the Invoke-RestMethod cmdlet into PowerShell objects. The
command was greatly improved in PowerShell 6⁵³, but today we’re going to focus on the newest
changes in PowerShell 7.
If you spent any time in the help file/online docs for Invoke-RestMethod (5.1⁵⁴ and 7.0⁵⁵), then
you’ve likely seen the below example. It shows the basic ability of the cmdlet, and returns
some worth wild results, too. The below results are created for us after issuing a specific,
Invoke-RestMethod command. You can see it below. We first assigned the $Uri variable an
exact, URI (Uniform Resource Identifier) value. This complete URI is the combination of the
base URI, https://blogs.msdn.microsoft.com, and the endpoint, /powershell/feed. We then used
the variable in the Invoke-RestMethod command, which makes an HTTPS request for the data,
receives a response, and finally, converts it into PowerShell objects, where it’s then filtered by our
Select-Object command.

$Uri = 'https://blogs.msdn.microsoft.com/powershell/feed/'
Invoke-RestMethod -Uri $Uri | Select-Object -Property Title,pubDate

title pubDate
----- -------
Secrets Management Module Vault Extensions Thu, 06 Feb 2020 22:59:33 +0000
Public Preview of PowerShell Support in Jupy... Thu, 06 Feb 2020 21:46:52 +0000
Secrets Management Development Release Thu, 06 Feb 2020 20:40:50 +0000
Announcing the PowerShell 7.0 Release Candid.. Tue, 17 Dec 2019 00:42:17 +0000
⁵³https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-6
⁵⁴https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-5.1
⁵⁵https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7
Changes to Invoke-RestMethod in PowerShell 7 64

Improvements in Windows PowerShell Container... Tue, 10 Dec 2019 00:32:57 +0000


PowerShell 7 Preview 6 Fri, 22 Nov 2019 01:20:37 +0000
PowerShell Extension Roadmap Mon, 04 Nov 2019 13:19:35 +0000
DSC Resource Kit Release October 2019 Wed, 30 Oct 2019 20:52:48 +0000
PowerShell 7 Preview 5 Wed, 23 Oct 2019 19:11:44 +0000
DSC Resource Kit Release September 2019 Thu, 19 Sep 2019 18:07:12 +0000

As we continue, do keep in mind that Invoke-RestMethod can do much more than what we’ve seen
so far, and what we’ll cover. It can do POST requests, handle pagination, handle multipart/form-data,
and it can pass in multiple headers if required. Don’t forget about authentication and credentials;
it can handle those, as well. In PowerShell 7, there’s a couple newer features we’ll cover today.
Before we do that, if you didn’t use this cmdlet in PowerShell 6, then do know that there were many
changes in that version. You can actually find the PowerShell 6 features in the Invoke-RestMethod
PowerShell 7 documentation.
Before we get into the new features, here’s another example of using Invoke-RestMethod. This article
has coincided well with some of my AWS research and testing with Lambda and API Gateway. The
below image shows some testing against a random number generator that has a REST API in front
of it. That’s why there’s various numbers in the results.

Testing Invoke-RestMethod

There are two new parameters for Invoke-RestMethod in PowerShell 7: StatusCodeVariable and
SkipHttpErrorCheck. The StatusCodeVariable parameter allows you to assign the status code,
returned by the request to the RESTful web service, to a variable. As the below example was
successful, it assigned 200, or an OK successful status response code, to the scv variable.
Changes to Invoke-RestMethod in PowerShell 7 65

Using the StatusCodeVariable

As mentioned, the other new parameter addition to this cmdlet is SkipHttpErrorCheck. This
parameter causes Invoke-RestMethod to ignore an error status. It treats them as though they
were successful requests. Without it however, we received the error, as can be seen in the first
Invoke-RestMethod invocation below. It’s not clear by the image, but the value in $Uri has been
changed between our successful examples, and this one.
The second invocation in the below image appeared that it was (somewhat) successful, but that
was thanks to SkipHttpErrorCheck. It wasn’t, and you can gather that from the message that was
returned, even though we’ve skipped the error. The question is, “how might you know for sure, if
there was an error and you skipped it?” Yep. The StatusCodeVariable parameter and the variable
to which you’ve assigned the status code. These two new parameters can be used in conjunction.
The final command in the below example, uses both of the new PowerShell 7 Invoke-RestMethod
parameters, as part of a single command.

Using SkipHttpErrorCheck

Invoke-RestMethod is a complex command that consists of many ways in which it can be used. A full
discussion on everything you can do with Invoke-RestMethod could fill up an entire PSBlogWeek
(or two), all by itself. Even so, with the release of PowerShell 7, it made sense that as a group, we
helped highlight this command and its two new parameters. It’s just a small offering of all the new
Changes to Invoke-RestMethod in PowerShell 7 66

additions in the newest version of PowerShell.


Welcome to PowerShell 7.0.

This article originally appeared online at https://tommymaynard.com/changes-to-invoke-


restmethod-in-powershell-7/
What’s New with Select-String in
PowerShell7

by Mike Kanakos

Select-String is one of those commands that either you use it often or not much at all. There doesn’t
seem to be a middle of the road for Select-String. I don’t get called upon to use it that often, but
when I do, it usually saves my bacon at work. The most typical use case for me is for searching flat
log files for patterns of text or for all entries of a particular phrase across a bunch of files. The release
of PowerShell 7 has brought many features and some great new cmdlets. Select-String received
some minor but powerful updates as well in the new release. The updates to Select-String are less
“WOW” and more like, “Oh yeah, that’s a useful addition.” For someone who uses Select-String all
the time, these updates are probably super helpful. But if you only occasionally use Select-String,
then you may not even notice the new bits available when using this cmdlet.
Select-String has four new parameters included with Powershell 7. Overall, the major functionality
of the cmdlet has changed little. These four new parameters add some useful upgrades though, so
let’s review how they work.

-Culture
The -Culture parameter is a new parameter that is used to detect specific characters for a language.
An example of this would be in the English language we have the letter i which can be lowercase i
and uppercase I. Most modern computer systems can pick up the difference between a lowercase and
uppercase letter. However, that’s not always the case for other languages that have special characters.
One example of this would be the Turkish alphabet which has an upper and lowercase I and then
also has an upper and lowercase I with two dots above it. That means the Turkish alphabet
has 4 iterations of the letter i and the English alphabet only has 2. If we were to search for those
letters using an English search utility, the search would not match correctly sometimes. This may
not seem like a big problem, but if you were trying to programmatically search Turkish text for
specific phrases, it could be a bit of a nightmare to deal with. Same goes for other languages that
have special characters. The culture parameter solves this issue by allowing the pattern matching
of Select-String to occur using the right language. It’s probably not a big change for most people,
but it is a nice addition.
What’s New with Select-String in PowerShell7 68

-NoEmphasis
The -NoEmphasis parameter turns off text highlighting of matched text. Now, you might to yourself,
“wait a minute, Select-String doesn’t highlight matches” and you would be right… except in
PowerShell 7 text highlighting is a now a thing, and it’s on by default! I’ll need to first show how text
highlighting works before I can show what the purpose of -noemphasis is. I absolutely LOVE the
new highlighting addition to Select-String and I think you will too! You can see the highlighting
change in action by doing a simple text search. Here’s a text string match on the word “the”.

$text | select-string -Pattern "the"

Text Highlight

However, that only matched on the first match. To match on all instances of the word “the”, you
need to add the -AllMatches parameter.

$text | select-string -Pattern "the" -AllMatches

Text Highlight All Matches

The highlighting can be useful when searching across multiple files at once. Here is a search for the
word “dummy” in a directory of log files.

Select-String -Path C:\temp\*.log -Pattern "dummy"

Highlight Multiple Files

The highlighting of the matched text is a small change that makes a big impact.
There are many ways to do the same searches with existing Select-String parameters like -CaseSensitive,
-NotMatch, -Quiet and others. I won’t review those parameters because they are not new, but keep
What’s New with Select-String in PowerShell7 69

in mind you can get creative with the parameters and the highlighting will reflect differently for all
those results.
Now I am sure you can imagine what the -NoEmphasis parameter does. If we perform the same
search and add that parameter, we get the same results without highlighting.

Select-String -Path C:\temp\*.log -Pattern "dummy" -noemphasis

Highlight Multiple Files with No Emphasis

-Raw
Another new parameter is the -Raw parameter. This parameter changes what the result type will be.
Remember that one of the great things about PowerShell is that nearly everything is an object.
This is true for Select-String results. If we perform one of our earlier searches and save the
results to a variable, we can then look at the variable type. Here we see that the object type is
Microsoft.PowerShell.Commands.MatchInfo.

PS C:\Scripts> $results = ($text | select-string -Pattern "the")


PS C:\Scripts> $results | gm
What’s New with Select-String in PowerShell7 70

MatchInfo Type

If you are new to PowerShell, gm is an alias for Get-Member.

The output result becomes a string when using the -raw switch. Notice also that all the members for
the string object are much different from the MatchInfo object.

PS C:\Scripts> $results2 = ($text | select-string -Pattern "the")


PS C:\Scripts> $results2 | gm
What’s New with Select-String in PowerShell7 71

String Type

-SimpleMatch
To this point, I have been showing off text searches. You can do much more with Select-String
because behind the scenes the cmdlet uses REGEX (regular expressions) by default to do its work. We
can see an example of the REGEX functionality below. In this search, we’re matching on a pattern
of \?. But since Select-String is using REGEX, this search is looking for just the ? character.

Select-String -Path "$PSHOME\en-US\*.txt" -Pattern '\?'

Regex Search Result

The -SimpleMatch parameter instructs PowerShell to not use REGEX for this search, instead just
do a “simplematch” of the text in the pattern. Using the same search as before, you’ll see how the
parameter affects the output. We have no match in the result because there is no instance of \? in
the files searched.
What’s New with Select-String in PowerShell7 72

Select-String -Path "$PSHOME\en-US\*.txt" -Pattern '\?' -simplematch

Simple Match Search Result

This is an important concept to understand because REGEX has special characters that LOOK like
regular characters but PRODUCE different results in searches. The -SimpleMatch parameter allows
for a way to do a search with the literate value of a character rather than REGEX value of that
character. Look at this simple example I have created to highlight the difference. In the search below,
we’re trying to find the . character, also known as a period. What this search LOOKS LIKE is, “look
at all .txt files in the specified directory and match on the ..

Select-String -Path "$PSHOME\en-US\*.txt" -Pattern '.'

However, that’s not what PowerShell interprets it as, because the . character has special meaning
in REGEX. The . character means match ALL characters (or in better terms match ANY character).
Below is the output.

Misleading REGEX Search

What the search returned is a match of ANY CHARACTER. I did not use the -AllMatches parameter,
so it literally matched on the FIRST CHARACTER of every line of text in the files that match the
search criteria. If we were actually trying to find periods in files, this search would have been a giant
fail. However, the -SimpleMatch parameter instructs PowerShell to ignore REGEX and do a simple
search instead.
What’s New with Select-String in PowerShell7 73

Also a side note here. Imagine if we performed this same search prior to PowerShell 7 and did not
have the highlighting feature? Imagine getting an output without seeing that the first character of
every line was the match and trying to troubleshoot that problem.

Select-String -Path "$PSHOME\en-US\*.txt" -Pattern '.' -SimpleSearch

Simple Search Result

Now we get an output, that is what we expected. PowerShell returned a list of all lines that contain a
period. You can now see how the -SimpleSearch parameter could be useful. These small changes to
the Select-String cmdlet are edge-use cases for most people, but the flexibility that these options
provide can be useful when those edge cases arise.

This article originally appeared online at https://www.networkadm.in/select-string-power-


shell7/.
Running PowerShell 7 Commands
Directly on Ansible Localhost

by Jonathan Medd

In a previous post⁵⁶ I have written about Running PowerShell Core Commands Directly on Ansible
Localhost using the version of PowerShell Core available at that time 6.2.3. With the recent release
of PowerShell 7 I will demonstrate how to achieve the same outcome with this newer version and
also an example taking advantage of one of the new features.
My awx_task container is based on Centos 8 and installation for PowerShell 7 is the same process⁵⁷
as for 6.2.3. I already had the Microsoft RedHat repository registered from installing 6.2.3, so it was
simply:

sudo yum install -y powershell

We can see that PowerShell 7 has been installed:


⁵⁶https://www.jonathanmedd.net/?p=5631
⁵⁷https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-7#centos-7
Running PowerShell 7 Commands Directly on Ansible Localhost 75

To illustrate functionality we’ll use the following Ansible playbook:

---
- hosts: all
tasks:

- name: Run a script


shell: "{{ playbook_dir }}/files/test_output4.ps1"
args:
executable: /usr/bin/pwsh
register: result1

- name: Process output


set_fact:
errorDetail: "{{ result1.stdout | from_json }}"

- debug:
msg: "Error Category: {{ errorDetail.Category }}, Error Activity: {{ errorDeta\
il.Activity }}, Error Reason: {{ errorDetail.Reason }} for Target {{ errorDetail.Tar\
getName }}"

- name: Invoke-RestMethod via shell module


Running PowerShell 7 Commands Directly on Ansible Localhost 76

shell: Invoke-RestMethod -Uri 'https://httpbin.org/get' | ConvertTo-Json


args:
executable: /usr/bin/pwsh
register: apiRequest

- name: Process output


set_fact:
apiOutput: "{{ apiRequest.stdout | from_json }}"

- debug:
msg: "The User-Agent used to make the API request was {{ apiOutput.headers['Us\
er-Agent'] }}"

In the first half of the playbook we’ll execute the below PowerShell script stored locally on the
Ansible host. On line 1 we purposefully run a command that will generate an error. We then use a
new cmdlet in PowerShell 7, Get-Error, to return some specific error details.

Get-ChildItem test

$ErrorDetail = Get-Error

Write-Output ($ErrorDetail.CategoryInfo | ConvertTo-Json)

When this section of the playbook is executed in AWX we observe the selected info returned from
Get-Error that we wish to debug via Ansible.

Task debug

In the second half of the playbook we use Invoke-RestMethod to make an API call against a test URL.
Observe the debug log from the Invoke-RestMethod call confirms that we are using PowerShell 7:

PowerShell confirmation

This article originally appeared online at https://www.jonathanmedd.net/2020/03/running-


powershell-7-commands-directly-on-ansible-localhost.html
Defining and Changing Who You Are
with PowerShell Classes and
Update-List

by Dan Franciscus

Folks, I will not lie to you. When I received my topic assignment and saw Update-List next to my
name, I had absolutely no idea what cmdlet that was. I have never used it and never heard of it,
which makes learning and writing about it challenging and fun. Update-List is a pretty cool way
to add, remove and replace values in a collection, which I find particularly useful when working
within the property of a class. Without further ado, lets get do it.

Defining Yourself
To best illustrate Update-List, let’s first set up a simple class. This class is about Dads. Well, who
are Dads? Dads have a first name, last name, an age, some hobbies, a mood, and kids of course.

class Dad {
[string]$FirstName
[string]$LastName
[string]$Mood
[System.Collections.Generic.List[string]]$Hobbies
[System.Collections.Generic.List[string]]$Kids
[int]$Age
}

You will notice that I chose to use a generic list for my hobbies and kids. You will see later that this
is where Update-List comes in handy. With Update-List, we can easily change the values in these
properties. Whenever I have had to write PowerShell scripts that need to change the values of an
array, a generic list is what I have used in the past. Now that we have defined what a “Dad” is, lets
instantiate one of these bad boys.
Defining and Changing Who You Are with PowerShell Classes and Update-List 78

$Dan = [Dad]::New()

Neat. Now lets give $Dan some values:

$Dan.FirstName = 'Dan'
$Dan.LastName = 'Franciscus'
$Dan.Hobbies = 'golf','dancing'
$Dan.Kids = 'Cecelia','Benjamin'
$Dan.Mood = 'angry'
$Dan.Age = 38

There we go. I am a Dad that has a first name, last name, hobbies, kids, age and a mood (angry).

FirstName : Dan
LastName : Franciscus
Mood : angry
Hobbies : {golf, dancing}
Kids : {Cecelia, Benjamin}

Now we know who I am. I have defined myself pretty well I would say.

Changing Who You Are


Lets face it, people change. We are not static beings that keep the same state forever. So what can do
I do to change who I am? We can use Update-List of course. We know that I likes golf and dancing
as hobbies, but you know what? I have just turned 40 (hypothetically) and realize it hurts like heck
when I try to tango. So first we will just change the value of my age.

$Dan.Age = $Dan.Age + 2
40

Now, lets get rid of dancing as a hobby since I am getting old.


Defining and Changing Who You Are with PowerShell Classes and Update-List 79

$Dan | Update-list -Property hobbies -Remove 'dancing'

FirstName : Dan
LastName : Franciscus
Mood : angry
Hobbies : {golf}
Kids : {Cecelia, Benjamin}
Age : 40

Next, lets use Update-List to add two values to my hobbies, beer and couching. If you are wondering
what “couching” is, this term refers to sitting on the couch as long as you can without performing
any productive task whatsoever.

$Dan | Update-list -Property hobbies -Add 'beer','couching'

FirstName : Dan
LastName : Franciscus
Mood : angry
Hobbies : {golf, beer, couching}
Kids : {Cecelia, Benjamin}
Age : 40

After a while of couching, I realize its time to do choose something more productive to do with my
time. I think I will take up golf but still enjoy my alone time with a cocktail every now and then.
Here we completely replace the values in the hobbies property with golf and cocktails, subsequently
removing beer.

$Dan | Update-List -Property hobbies -Replace 'golf','cocktails'

FirstName : Dan
LastName : Franciscus
Mood : angry
Hobbies : {golf, cocktails}
Kids : {Cecelia, Benjamin}
Age : 40

Hmm, you know what? My kids have been driving me insane. Let’s replace them as well:
Defining and Changing Who You Are with PowerShell Classes and Update-List 80

$Dan | Update-List -Property kids -Replace 'Marsha','Greg'

FirstName : Dan
LastName : Franciscus
Mood : angry
Hobbies : {golf, cocktails}
Kids : {Marsha, Greg}
Age : 40

Look at me, I have changed so much of who I am today!


As you can see, Update-List is a great way to manage values when working with a generic list
within classes.

This article originally appeared online at https://winsysblog.com/2020/03/defining-and-


changing-who-you-are-with-powershell-classes-and-update-list.html
Error Handling in PowerShell 7 with
Get-Error and $Errorview

by Mike Kanakos

By now, I am sure you’ve heard the news: PowerShell 7 is no longer a beta product and the latest
release has reached GA (generally available) status. In short, this means that Microsoft now considers
PowerShell 7 production ready! From this point forward you should begin to use PowerShell 7 as
your daily driver for all things PowerShell. Today I’d like to show you one aspect of PowerShell that
is greatly improved: Error Handling and Error Messages.

Error Views
Before I can jump into the new features; let’s review what’s was already available in PowerShell
prior to the v7 release. Let’s run some code and generate an error.

[int]$number = Read-Host "What is the latest version of PowerShell ?"

In the above code, I am prompting the user to answer a question and saving the answer to a variable
called $number. I am expecting the answer to be a number so I cast the variable as an INT, meaning
that the variable will only accept numbers as being valid. Anything else will return an error. When
I run the code I get prompted to answer the question. For our purposes, I will input a letter instead
of a number causing an error to be thrown. The error returned contains a bunch of information, but
notice it has a standard format you have probably seen many times prior.
The layout of this message is called the “normal view”. This layout should look pretty familiar; it’s
the default error view in PowerShell 5 and earlier. However, there is another way this error could
have been displayed.
Error Handling in PowerShell 7 with Get-Error and $Errorview 82

PowerShell 5 Error Message

Let’s re-run the code again, but this time set a view called “CategoryView” for a variable called
$errorview and see what happens.

$errorview = "categoryview"
[int]$number = Read-Host "what is the latest version of PowerShell ?"

PowerShell 5 Error Message using Category View

In our example above, running the same code with this other error view produces a different error
message. This view is called the Category View and it can be set manually to override the normal
view which we saw in the previous example. Here’s a better example of the category view showing
an error that describes the problem.

$errorview = "categoryview"
test-connection -ComputerName "notrealcomputer" -count 1

Test-Connection error using Category View

So what does this have to do with PowerShell 7? PowerShell 7 contains the same two views but now
includes a third view called ConciseView. The same code in PowerShell 7 produces a short but useful
error message.
Error Handling in PowerShell 7 with Get-Error and $Errorview 83

PowerShell 7 error message

One of the first things you will notice using PowerShell 7 is that error messages are much improved
and more succinct. This error message for Test-Connection is short but adequately describes the
issue.

PowerShell7 Test-Connection Error

In this code sample, I have purposely misspelled the Select-Object cmdlet. Again we see that the
message clearly states the problem.

PowerShell 7 Select Object typo

ConciseView is the default view in PowerShell. Nothing needs to be configured to get these shorter,
clearer messages. All errors produced will be easier to understand and point more directly to what
caused the error in your code. If you prefer one of the other views, you can switch at anytime by
updating the value of $Errorview and then PowerShell will produce errors similar to the message
you have seen in previous versions.

Get-Error
PowerShell 7 also includes a new cmdlet named Get-Error. The cmdlet retrieves and displays
extended error information for recent error messages from the current PowerShell session. Extended
error information is ALL the error information that is recorded when an error is thrown. The views
I discussed earlier are only presenting a subset of the error data available.
In the screen capture below, I generate an error by calling an invalid CIM namespace. Afterwards, I
then run Get-Error to see the extended information available for that error. You’ll notice that there
is quite a bit of information available in addition to the original error message that was displayed
at the first error.
Error Handling in PowerShell 7 with Get-Error and $Errorview 84

Get-Error Extended Information

Get-Error not only displays extended error information on the most recent error message, it also
can display all the errors that occurred in a session. In my current session I have generated quite a
few errors that are still in memory. I can use Get-Error to get the entire list and then retrieve the
information as needed. For this example, I am going to use Out-Gridview to illustrate how many
errors I have in memory.

Get-Error -newest 20 | Out-Gridview


Error Handling in PowerShell 7 with Get-Error and $Errorview 85

Get-Error list of errors in current session

All the information from past errors can be recalled and reviewed as long as the session is not cleared.
That information can be useful when trying to pinpoint errors.
These new views and cmdlets provide more useful data when errors occur and more ways to go back
and look at past errors. We also can dive in deep and look at the extended data for an extra deep
view of what happened when an error occurred. These new tools are just a scratch of the surface of
the new features available in PowerShell 7!

This post originally appeared online at https://www.networkadm.in/error-handling-in-


powershell-7-with-get-error-and-errorview
PowerShell 7 Chain and Ternary
Operators

by Thomas Lee

Introduction and Background


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: https://www.youtube.com/watch?v=TytIqm_d7uE
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 modeled on C# - Jeffrey
Snover⁵⁸ has often said that PowerShell is on the glide scope to C#. Several operators, present in C#
(and Bash), are now a part of PowerShell 7.
The Pipeline Chain operators (|| and &&) enable you to to allow conditional execution of commands
depending on whether the previous command succeeded for failed. You use the Ternary operators (?
and :) to create strings based on boolean values. These are popular among C# developers and Bash
users and have long been requested within PowerShell. These operators add great new functionality
to PowerShell 7. They are nice when used carefully, but introduce the risk of reduced clarity of
production code.

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 these operators you could have used
If/Else to do this.
⁵⁸https://twitter.com/jsnover
PowerShell 7 Chain and Ternary Operators 87

What Is It
There are two new chain operators: && and ||. These operators come originally from Posix. POSIX
shells call this as AND-OR lists. The idea is that depending on whether command is successful, you
can do different things.

What Is It Used For


If a pipeline successful, you can then run some other pipeline. But if the first pipeline is unsuccessful,
you can run a different pipeline. Unlike other operators, && and || operate on pipelines, rather than
on expressions like + or -and.

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

If the keys are generated successfully using ssh-keygen, then the command gets the contents of a
file and 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

What Is It
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 use this operator mainly to create a string depending on the value of a boolean variable or
expression.

Example
You could create a string that displayed whether an AD user account was enabled based on the
user’s Enabled property.
PowerShell 7 Chain and Ternary Operators 88

$UEMsg1 = "This user IS enabled in AD"


$UEMsg2 = "This user IS NOT enabled in AD"
$UserEnabled = (Get-ADUser -Identity $UserName).Enabled
$UserEnabledStr = $UserEnabled ? $UEMsg1 : $UEMsg2
$UserEnabledStr
This user IS enabled in AD

Or

$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 maybe at the console. An example of this, if you
look at the chain operator example, is getting something from a file to the clipboard after running
some pipeline. 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}
}
# then
Get-Content -Raw ~\.ssh\id_rsa.pub | clip

If I was running this from the console, I’d just run ssh-keygen. If it ran ok, then I’d type the second
line. Typing that longer line is almost certain to introduce typos, especially given my lousy typing. I
guess I find doing things step by step is easiest - both to write and to understand months later when
the code blows up!
I also feel 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 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.
PowerShell 7 Chain and Ternary Operators 89

This article originally appeared online at https://tfl09.blogspot.com/2020/03/introduction-


and-background-welcome-to.html
Exploring… Nothing? PowerShell 7’s
Null Conditional Operators

by Josh King

How do you deal with potentially getting nothing back from a cmdlet or function? PowerShell 7
introduced a set of new operators to make working with null values easier.

Have you ever found yourself with a variable that may or may not actually exist? Maybe you were
assigning the output of a cmdlet to it, but it’s possible that the resource you were looking for doesn’t
exist so the cmdlet didn’t return anything. In this situation, the variable is “null” or empty/non-
existent. For a deep dive on the general concept of null in PowerShell I highly recommend checking
out Kevin Marquette⁵⁹’s “Everything You Wanted to Know⁶⁰” post.
But back to the original question. Traditionally, if you wanted to check if your variable was null or
not you might include something like this:

⁵⁹https://twitter.com/KevinMarquette
⁶⁰https://powershellexplained.com/2018-12-23-Powershell-null-everything-you-wanted-to-know
Exploring… Nothing? PowerShell 7’s Null Conditional Operators 91

$TheThing = $null

if ($null -eq $TheThing) {


"Nope, it wasn't there!"
} else {
$TheThing
}

In this example, we first assign null to the $TheThing variable, we’re doing this for the sake of the
demo to ensure you get the expected result when you try it at home. We then check to see if that
variable is null. If it is null then we take some action (in this case outputting a string) and if it
isn’t then we return the value it contains. You could simplify your if statement from ($null -eq
$TheThing) to (-not $TheThing), but regardless of how you go about the comparison you’re still
using a full if/else statement.

Null Coalescing Operator


The newly released PowerShell 7 introduces a new option for us on this front. We now have a ??
null coalescing operator, which is a feature of more “traditional” programming languages. The best
way to describe this is that your output will be the value on the left hand side of the operator if it
isn’t null, but if it is null then it will output whatever is on the right of the operator.

<left> ?? <right>

Our previous example re-written to use the null coalescing operator looks like this:

$TheThing = $null

$TheThing ?? "Nope, it wasn't there!"

That’s dramatically shorter isn’t it?

Try It Yourself
If you haven’t already, let’s run through those examples and see the output from them. The first
“old” example was this:
Exploring… Nothing? PowerShell 7’s Null Conditional Operators 92

$Thing = $null

if ($null -eq $Thing) {


'The thing is null'
} else {
$Thing
}

This will output:

The thing is null

Now let’s give $Thing a value:

$Thing = 'The thing has a value!'

if ($null -eq $Thing) {


'The thing is null'
} else {
$Thing
}

The output now contains the value we assigned to $Thing:

The thing has a value!

Now try those two examples again, but using the null coalescing operator.

$Thing = $null
$Thing ?? 'The thing is null'

And this:

$Thing = 'The thing has a value!'


$Thing ?? 'The thing is null'

Stretching Things
It’s not just strings that you can put on either side of this operator. You can actually run commands.
A caveat here, as far as I’m aware from my experiments, is that you’re limited to one pipeline
(meaning you can use | characters, but not ;) and in my testing the expression needs to be wrapped
in brackets.
Exploring… Nothing? PowerShell 7’s Null Conditional Operators 93

$Thing ?? (Get-Date | Select-Object *)

You can even put an expression like this on the left. Just be aware that the expression on
the right will only be evaluated if the left side was null. That’s a very specific condition,
remember that things that return a Boolean value aren’t returning null ($false is a value,
therefore it’s not null.)

This is all well and good, but what if we want to actually assign a value to our variable if it’s null?

Null Conditional Assignment Operator


A very near relative to the ?? coalescing operator is the ??= conditional assignment operator. With
this operator, the variable on the left will keep its current value if it is not null, but will be assigned
the value on the right if it is null. Let’s walk through a full example of this using just the new
operators from PowerShell 7 (no need for if/else here!). First we’ll make sure our variable is null:

$Thing = $null

Then we’ll make sure it’s actually null with the coalescing operator:

$Thing ?? "Yup, it's null!"

Yup, it's null!

Now, let’s use the conditional assignment operator to give our variable a value:

$Thing ??= 'This is a value!'

There’s no output on this. To see that something has happened, we’ll use the coalescing operator
again:

$Thing ?? "Yup, it's null!"

This is a value!

Excellent, our value was assigned. For a little homework, try running the following commands to
double check that the conditional assignment operator won’t overwrite and existing value:
Exploring… Nothing? PowerShell 7’s Null Conditional Operators 94

$Thing ??= 'Yet another value...'


$Thing

Bonus: Accessing Properties and Methods on Null


Variables
One of the main reasons I actually find myself checking whether a variable is null or not is to avoid
errors when trying to dig into it, such as pulling out properties, calling methods, or indexing an item
in a collection. Doing any of these actions on a null variable will result in errors. We can avoid these
errors by throwing question marks at the problem (don’t worry, this statement will make sense in a
moment.)

Note: This is an experimental feature, and will need to be enabled prior to use.

Enable-ExperimentalFeature -Name PSNullConditionalOperators

Methods and Properties


Let’s say we’re wanting to stop the BITS service using its Stop() method, but we’ve forgotten to
actually get the service in a variable prior to trying.

$BitsService.Stop()

Error from method on null variable

In PowerShell 7 we can instead do the following:


Exploring… Nothing? PowerShell 7’s Null Conditional Operators 95

${BitsService}?.Stop()

No error on method due to PS7

What you’ll note here is that the name of the variable has been wrapped in curly braces, this is
needed because variable names can end with a question mark and that happens to be what we need
to add before the period when calling a method or property. The previous example didn’t actually
do anything because that variable was still null. If it did contain a service object, the service would
have been stopped. Adding the question mark specifies that you’re expecting that the variable could
be null and that that is OK… for whatever reason.

Note: Trying to get properties from a null variable won’t actually generate an error, even
before PowerShell 7. Using this syntax just makes then intentions of your script clearer.

Indexing Collections
Let’s say you’re getting a list of users from Active Directory, but the Organizational Unit you’re
using may or may not actually contain users. You want to index into the potential collection of
users, but if none were returned your variable will be null… and you’ll get an error

$Users[0]

Error when indexing into a null collection

Once again, we can add wrap the variable’s name in curly braces and add a question mark to avoid
the error:
Exploring… Nothing? PowerShell 7’s Null Conditional Operators 96

${Users}?[0]

Suppressed error using conditional access question mark

These can be chained together, so you can index into a collection and also call a method or get a
property, you just need to continue to use question marks.

${Users}?[0]?.SamAccountName

Final Thoughts
It’s important to remember, as with most of the new PowerShell 7 features, that using them
automatically makes your scripts incompatible with older PowerShell versions. There’s nothing
wrong with this, just consider the audience of your scripts and whether you want to force them
to upgrade (even though we all know that they should!). Personally, I’ll be using some of these new
operators when working via an interactive shell, but not in scripts that I’m saving for later re-use.
This is the same stance I take on aliases⁶¹.
The main value I see here is breaking down a barrier that may have previously stopped existing
developers from adding PowerShell to their toolbox.

Credit
Hero image by Valentin Lacoste⁶² on Unsplash⁶³

This article originally appeared online at https://toastit.dev/2020/03/11/ps7now-null-condi-


tional/

⁶¹https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_aliases?view=powershell-7
⁶²https://unsplash.com/@valentinlacoste?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
⁶³https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
Using ANSI Escape Sequences in
PowerShell

by Josh Duffney

Introduction
When using ANSI within PowerShell there are two main parts; the escape character and the
escape sequence. The escape character is used to indicate the beginning of a sequence and changes
the meaning of the characters that follow the escape character. Most commonly escape characters
are used to specify a virtual terminal sequence (ANSI escape sequence) that modifies the terminal.
Escape characters are a standard of in-band signaling that control the cursor location, color, font
styling, and other options within terminals and terminal emulators. ANSI escape sequences are
often used with modifying command line prompt displays!
Windows PowerShell doesn’t have a built-in escape special character. Because of that you’d have
to use "$([char]27)" to output a ASCII character representing an escape character. However,
PowerShell now includes a special character for escape⁶⁴ `e . To use the escape character you
start a string with the escape character `e followed by an opening square bracket `e[. Inside the
square bracket is where you place the escape sequence. That escape sequence will determine how
the terminal interpret the characters and acts accordingly.
The best way to understand ANSI escape sequences is to break it down into its different parts. Using
some ASCII art as an example you can break down the sequence `"`e[5;36m$asciiArt`e[0m" into its
different parts. The sequence starts with the control sequence introducer `e[. The `e is the escape
character and [ is the introducer. What follows is the sequence. Each of the numbers within this
sequence represent an argument. The number 5 represents an argument that makes the text within
the sequence blink. Each argument must be separated by a semi colon, which is why you see a
semi colon between 5 and 36. Values 30-37 represent different foreground colors. In this example 36
represents a foreground color of cyan.
Next in the sequence is the letter m which represents a function. The function is called SGR (“Select
Graphics Rendition”) and accepts several arguments which were define earlier in the sequence. What
⁶⁴https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-
7#escape-e
Using ANSI Escape Sequences in PowerShell 98

follows after the function is the text that will be displayed. In this example that is a here-string stored
in a the variable $acsiiArt that contains the ASCII art for #PS7Now. At the very end of the sequence
`e[0m is calling the SGR function again, but this time it is using the argument 0 to reset and turn
off all the attributes defined in the first sequence. Putting the sequence back together again and
running it within a terminal will result in the ASCII art #PSNow being displayed with a cyan font
and flashing text. Now that you have a good understanding of ANSI escape sequences, let’s take a
look at what else can be done with them and have some fun!

$asciiArt = @"
__ __ ____ ___________ __
__/ // /_/ __ \ ___/__ / | / /___ _ __
/_ _ __/ /_/ \__ \ / / |/ / __ \ | /| / /
/_ _ __/ ____/__/ / / / /| / /_/ / |/ |/ /
/_//_/ /_/ /____/ /_/_/ |_/\____/|__/|__/

"@

Write-Output "`e[5;36m$asciiArt`e[0m";

PS7Now ASCII Art

Blinking is determined by your terminal, yours might not support it. And of course you
won’t see it in a static image like this.

Text Styling
ANSI escape sequences support a few different text styling options; bold, underline, and invert. Text
styling with ANSI escape sequences are one of the more basic modifications you can make to the
terminal’s text. Each of the styles has a reset argument that will turn off the attribute it enabled.
This is important to know, because if you do not reset the attribute all future inputs will have that
style applied.
Using ANSI Escape Sequences in PowerShell 99

Style Sequence Reset Sequence


Bold ‘e[1m | ‘e[22m
Underlined ‘e[4m | ‘e[24m
Inverted ‘e[7m | ‘e[27m
Reset all ‘e[0m

Bold
$text = '#PS7Now';
Write-Output "`e[1m$text";

Bold Styling

Underline
$text = '#PS7Now';
Write-Output "`e[4m$text";

Underline Styling

Invert
$text = '#PS7Now';
Write-Output "`e[7m$text";

Inverted Style
Using ANSI Escape Sequences in PowerShell 100

Resets
All of the previous examples do not include a reset sequence. If you do not include a reset sequence
the ANSI attributes you applied will continue to apply to all future text your code outputs to the
screen. In PowerShell the command or script scope limit its impact, but it does effect the output of
your code. Running the below code snippet, you’ll notice that the text following #PS7Now is still
bold and inverted. It will also change the first new line return of your terminal prompt.

$text = '#PS7Now'
Write-Output "`e[1;7m$text, ANSI not reset, text is still bold and inverted"

No Reset

When using ANSI reset sequences you have two options. You can either reset the attribute
individually or reset all of them. Using the table above you know that the bold argument is 1 and the
invert argument is 7. Combining them you can create the escape sequence `e[1;7m. This sequence
will style the text bold and invert the foreground and background colors. To reset just the invert you
would use the sequence `e[27m. The text after that sequence would remain bold, but not inverted.
To reset just the bold attribute you would use another sequence `e[22m. That would remove all the
attributes applied by the first sequence and the text would no longer be inverted or bold. Another
option available if you do not need to keep any of the attributes enable is to reset all of them at once.
The sequence to reset all attributes is `e[0m.

$text = '#PS7Now'
#reset styles individually
Write-Output "`e[1;7m$text `e[27m Not Inverted `e[22mNot Bold or Inverted";

Individual Reset

#reset all
Write-Output "`e[1;7m$text`e[0m Not Bold or Inverted";
Using ANSI Escape Sequences in PowerShell 101

Reset All

Moving the Cursor


ANSI sequences can do more than just modify the style of text. ANSI also supports cursor movement.
While I’m not sure of the practical usage of this in a PowerShell script, it is fun to play around with.
Using `e[1m$pwd`e[2A;sleep 5 as an example sequence you can see the curious position change.
As you learned perviously the first sequence `e[1m$pwd is bolding the font and then outputting the
variable $pwd. The second sequence `e[2A is what is moving the cursor position. The number 2 is
defining how many positions to move the cursor and A is an ANSI function for cursor up. Normally
this would happen so fast you wouldn’t be able to see it. To take care of that the sleep 5 is pausing
the output for 5 seconds so you can see the cursor move.

"`e[1m$pwd`e[2A";sleep 5

For a gif animation go to http://duffney.io/images/posts/ansiEscapeSequences/movecursor.gif⁶⁵.

Learn more about ANSI cursor positioning at http://ascii-table.com/ansi-escape-se-


quences.php⁶⁶

Viewport Positioning “Scrolling”


Using the S ANSI sequence you can also use scrolling to add padding to the text that is output. The
sequence `e[3S$text`e[S3 will fill new lines in from the bottom of the screen. The value 3 is the
number of lines that will be filled in. There is a scroll down sequence as well. You can read more
about viewport positioning here⁶⁷.

⁶⁵http://duffney.io/images/posts/ansiEscapeSequences/movecursor.gif
⁶⁶http://ascii-table.com/ansi-escape-sequences.php
⁶⁷https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#viewport-positioning
Using ANSI Escape Sequences in PowerShell 102

#Scroll Up (add 3 lines of space top & bottom)


$text = '#PS7Now'
Write-Output "`e[3S$text`e[3S"

Again, you’ll need to go online to http://duffney.io/images/posts/ansiEscapeSequences/scrolling.gif⁶⁸


to see a gif animation of this in action.

Cursor Positioning & Text Modification


ANSI escape sequences can also modify the cursor position. While the possibilities are endless, one
way it can be used is to save and restore cursor location. To save the current cursor position you’ll
use the sequence s after the control sequence introducer. This will not modify the output of the
string in anyway, it simply saves the position for restoring later. After the variable $text outputs
#PS7Now you’ll use the restore sequence which is u. This will place the cursor at the # character.
With the cursor at that location you can use a text modification to delete the # at the beginning. The
ANSI sequence for deleting a character is P. The 1 before the P is the number of characters that will
be deleted. Running the below line of code will result in the # character being deleted. Simple, silly,
but yet fun!

$text = '#PS7Now'
Write-Output "`e[s$text`e[u`e[1P"

Text Modification

A few other text modification sequences worth mentioning are the erase in display and erase in line.
While I struggle to find practical use for the at the moment. I can see them being useful as part of
an April fools joke. Say you have a co-worker who is somewhat obsessed with their prompt display.
You could add `e[s`e[u`e[K to the prompt function. The s and u save and restore cursor position
as you’ve already learned. K in the erase in line sequence that will replace all text on the line with
space characters. Which will effectively destroy their prompt function.

Erase Line
⁶⁸http://duffney.io/images/posts/ansiEscapeSequences/scrolling.gif
Using ANSI Escape Sequences in PowerShell 103

$text = '#PS7Now'
Write-Output "`e[s`e[36m$test`e[u`e[K`e[0m"

Erase Line

Another fun sequence might be to erase the entire display. The sequence for that is <n>J, where <n>
is the number of space characters that will replace the display.

Erase Display
$text = '#PS7Now'
Write-Output "`e[2J`e[36m$text`e[0m"

Try this for yourself or view http://duffney.io/images/posts/ansiEscapeSequences/clearDisplay.gif⁶⁹.

Read more about Text Modification⁷⁰, Cursor Positioning⁷¹ and ANSI Escape sequence⁷².

⁶⁹http://duffney.io/images/posts/ansiEscapeSequences/clearDisplay.gif
⁷⁰https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#text-modification
⁷¹https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#cursor-positioning
⁷²http://ascii-table.com/ansi-escape-sequences.php
Using ANSI Escape Sequences in PowerShell 104

Basic Foreground & Background Colors


The original specifications of ANSI only had 8 colors that could be used to set the foreground and
background colors. The SGR (“Select Graphics Rendition”) parameters 30-37 selected the foreground
color, while 40-47 selected the background. You can use these color sequences with the m SGR
function to modify the foreground and background colors of the text. Remember to reset your
sequences.

Color Foreground Code Background Code


Black 30 40
Red 31 41
Green 32 42
Yellow 33 43
Blue 34 44
Magenta 35 45
Cyan 36 46
White 37 47

$fgColors = '30','31','32','33','34','35','36','37'
$bgColors = '40','41','42','43','44','45','46','47'

foreach ($fgColor in $fgColors)


{
$bgColor = $bgColors | Get-Random
Write-Output "`e[$($fgColor)m#PS7`e[0m`e[30;$($bgColor)m Now `e[0m`e[7;$($fgColo\
r);$($bgColor)m >_ `e[0m"
}
Using ANSI Escape Sequences in PowerShell 105

Basic Background Colors

8-bit 256-Color Foreground & Background


If 8 colors isn’t enough, which it might not be. You can also use the 8-bit sequence which provides you
with a range of 265 colors. The ANSI sequence for using 8-bit color is `e[<Foreground or Background
Code>;5;(n) the (n) represent the 8 bit color code. Foreground is indicated by 38 and background
is 48. An example sequence might look like this, `e[38;5;220m#PSNow. This sequence changes the
foreground color to a dark yellow. The 256 color range has different sections; 0-7 are standard colors,
8-15 are high intensity colors, 16-231 are a 6x6x6 color cube, and 232-255 are grayscale colors.

• ESC[ 38;5;�n� m Select foreground color


• ESC[ 48;5;�n� m Select background color
• 0-7: standard colors
• 8-15: high intensity colors
• 16-231: 6 × 6 × 6 cube (216 colors)
• 232-255: grayscale

Read more about ANSI escape code colors⁷³.

You probably haven’t memorized the color codes for all the 256 colors that are available. That’s
okay, you can output them in the terminal for reference. The following script block will output all
the colors for both the foreground and background colors along with their code.
⁷³https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
Using ANSI Escape Sequences in PowerShell 106

# 256-Color Foreground & Background Charts


$esc=$([char]27)
echo "`n$esc[1;4m256-Color Foreground & Background Charts$esc[0m"
foreach ($fgbg in 38,48) { # foreground/background switch
foreach ($color in 0..255) { # color range
#Display the colors
$field = "$color".PadLeft(4) # pad the chart boxes with spaces
Write-Host -NoNewLine "$esc[$fgbg;5;${color}m$field $esc[0m"
#Display 6 colors per line
if ( (($color+1)%6) -eq 4 ) { echo "`r" }
}
echo `n
}

Putting the sequence to use, you can create some very interesting text. Below are some examples
outputting #PS7Now and some ANSI art for the PowerShell icon.

"`e[38;5;27m#PS7`e[1mNow `e[22;38;5;15;48;5;27m >_ `e[0m"

Blue PS Ansi Art

"`e[38;5;248m#PS7`e[1mNow `e[22;38;5;15;48;5;237m >_ `e[0m"

Grey PS Ansi Art

Prompt Modification with ANSI


Now you are armed with enough knowledge to dive into the world of hardcore prompt customiza-
tions! Instead of covering that within the confines of this post, I will instead refer you to several
other sources for inspiration.
Using ANSI Escape Sequences in PowerShell 107

• Basic To Boss: Customizing Your PowerShell Prompt by Thomas Rayner⁷⁴


• Prompt Example from Thomas Rayner⁷⁵
• How to make a pretty prompt in Windows Terminal with Powerline, Nerd Fonts, Cascadia
Code, WSL, and oh-my-posh⁷⁶
• Anatomy of a Prompt (PowerShell) by Brad Wilson⁷⁷
• Customize Your PowerShell Prompt with Nerd Fonts & ANSI Escape Sequences by Trevor
Sullivan⁷⁸

Sources
Before writing this blog post I knew nothing of ANSI escape sequences, let alone the ANSI escape
character. I must give credit where it is due and list all the sources I used to gain an understanding
of ANSI escape sequences.

• The (Mostly) Dependency Free PowerShell Prompt - Part 1⁷⁹


• ANSI escape code⁸⁰
• Bash tips: Colors and formatting (ANSI/VT100 Control sequences)⁸¹
• Tip_Colors_and_Formatting⁸²
• Console Virtual Terminal Sequences⁸³
• How To Use ANSI/VT100 Formatting in PowerShell⁸⁴
• ANSI Escape sequences⁸⁵
• Build your own Command Line with ANSI escape codes⁸⁶

This article originally appeared online at http://duffney.io/usingANSIescapeSequencesPowerShell⁸⁷

⁷⁴https://www.youtube.com/watch?v=SdQYooRg7Cw&feature=youtu.be&t=
⁷⁵https://github.com/thomasrayner/dev-workstation/blob/master/prompt.ps1
⁷⁶https://www.hanselman.com/blog/HowToMakeAPrettyPromptInWindowsTerminalWithPowerlineNerdFontsCascadiaCodeWSLAndOhmyposh.
aspx
⁷⁷https://bradwilson.io/blog/prompt/powershell
⁷⁸https://www.youtube.com/watch?v=DhzR7mbFE9I&feature=youtu.be
⁷⁹https://ephos.github.io/posts/2019-6-24-PowerShell-Prompt-1
⁸⁰https://en.wikipedia.org/wiki/ANSI_escape_code
⁸¹https://misc.flogisoft.com/bash/tip_colors_and_formatting#terminals_compatibility
⁸²https://misc.flogisoft.com/bash/tip_colors_and_formatting
⁸³https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#screen-colors
⁸⁴https://powershell.org/forums/topic/how-to-use-ansi-vt100-formatting-in-powershell-ooh-pretty-colors/
⁸⁵http://ascii-table.com/ansi-escape-sequences.php
⁸⁶http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
⁸⁷http://duffney.io/usingANSIescapeSequencesPowerShell
PowerShell 7, VS Code, and the
PowerShell VS Code Extension

by Thomas Lee

Introduction and Background


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 whatever you call it - it’s feature complete and to replacing the ISE is a newer 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:
https://code.visualstudio.com/docs⁸⁸.

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 advent of PowerShell 7 and the
incredible velocity behind the VS Code product changes that view. I now use VS Code for my day
to day work. I still use the ISE, but pretty much only to install 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 the filestore.
• Side by Side edit windows - makes comparing files, and leveraging code from one into another
so much simpler.
⁸⁸https://code.visualstudio.com/docs
PowerShell 7, VS Code, and the PowerShell VS Code Extension 109

• Built-in spell check - yes it’s an extension, but typos in comments are less likely.
• The extensibility and customisability - you really can have it your way.
• PS Script analyzer is built in - so I get hints about poor quality code as I type.

For more details on VS Code, see: https://code.visualstudio.com/docs⁸⁹.

VS Code Extensions
VS Code was built to be extended. An extension adds functionality, such as spell checking or
markdown checking. I 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.
To customise the colour scheme of VS Code - you can find a ton 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: https://marketplace.visualstudio.com/VSCode⁹⁰.
One 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 It
To install VS Code, well - there’s a PowerShell script for that. Naturally! It is called Install-VSCode
and you download it from the PS Gallery. The script 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


# NB: this may generate errors, which can generally be ignored
$Extensions = "Streetsidesoftware.code-spell-checker",
"yzhang.markdown-all-in-one",
"davidanson.vscode-markdownlint",
⁸⁹https://code.visualstudio.com/docs
⁹⁰https://marketplace.visualstudio.com/VSCode
PowerShell 7, VS Code, and the PowerShell VS Code Extension 110

"vsls-contrib.gistfs"
# 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 may be 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. It seems these warning messages have gone away with
the latest builds of VS Code. This snippet takes around 30-40 seconds and rewards you, at the end,
with VS Code opening up 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!
Here is a side-by-side picture of VS Code with the PowerShell ISE and the Windows PowerShell ISE.
I have grown to prefer VS Code!

VSCode

For more details on setting up VS Code, see https://code.visualstudio.com/docs/setup/setup-


overview⁹¹.

⁹¹https://code.visualstudio.com/docs/setup/setup-overview
PowerShell 7, VS Code, and the PowerShell VS Code Extension 111

Managing Extensions
You can manage and configure VS Code extensions inside VS Code. 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. One VS
Code is installed, you can manage extensions (from PowerShell) like this:

# Sets the root path for extensions


code --extensions-dir <dir>

# Lists the installed extensions.


code --list-extensions

# Uninstalls an extension.
code --uninstall-extension (<extension-id> | <extension-vsix-path>)

VS Code PowerShell Extension


As I mentioned earlier, one extension most ISE users are going to want to get is the PowerShell
extension.The PowerShell extension adds great language support and great features including:

• Syntax highlighting
• Tab completion
• Code snippets
• IntelliSense for cmdlets and more
• 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 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.

For more details on the extension, see https://code.visualstudio.com/docs/languages/powershell⁹².

⁹²https://code.visualstudio.com/docs/languages/powershell
PowerShell 7, VS Code, and the PowerShell VS Code Extension 112

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 C:\Users\<user>\AppData\Roaming\Code\User\settings.json.
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 = 'https://github.com/microsoft/cascadia-code/releases'
$CascadiaRelease = Invoke-WebRequest -Uri $CascadiaRelURL # Get all of them
$CascadiaPath = "https://github.com" + ($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)
$FontShellNamespace.CopyHere($cascadiaFilePL, 0x10)

# Install the font using Shell.Application COM object


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

$Destination = (New-Object -ComObject Shell.Application).Namespace(0x14)


$Destination.CopyHere($DLFile,0x10)

# Create a short cut to VSCode


$SourceFileLocation = "$env:ProgramFiles\Microsoft VS Code\Code.exe"
$ShortcutLocation = "C:\foo\vscode.lnk"
# Create a new wscript.shell object
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation
#Save the Shortcut to the TargetPath
$Shortcut.Save()

# Create a short cut to PowerShell 7


$SourceFileLocation = "$env:ProgramFiles\PowerShell\7-Preview\pwsh.exe"
$ShortcutLocation = 'C:\Foo\pwsh.lnk'

# Create a new wscript.shell object


$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutLocation)
$Shortcut.TargetPath = $SourceFileLocation

# Save the Shortcut to the TargetPath


$Shortcut.Save()
$XML = @'
<?xml version="1.0" encoding="utf-8"?>
<LayoutModificationTemplate
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
Version="1">
<CustomTaskbarLayoutCollection>
<defaultlayout:TaskbarLayout>
<taskbar:TaskbarPinList>
<taskbar:DesktopApp DesktopApplicationLinkPath="c:\foo\vscode.lnk" />
<taskbar:DesktopApp DesktopApplicationLinkPath="c:\foo\pwsh.lnk" />
</taskbar:TaskbarPinList>
</defaultlayout:TaskbarLayout>
</CustomTaskbarLayoutCollection>
</LayoutModificationTemplate>
'@
$XML | Out-File -FilePath C:\Foo\Layout.xml
PowerShell 7, VS Code, and the PowerShell VS Code Extension 114

# Import a startlayout.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-preview\\pwsh.exe"
$JHT += @{
"terminal.integrated.shell.windows" = "$PWSH"
}
$Path = $Env:APPDATA
$CP = '\Code\User\Settings.json'
$Settings = Join-Path $Path -ChildPath $CP
$JHT |
ConvertTo-Json |
Out-File -FilePath $Settings

This code is available in the Extras zip file.

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

Summary
PowerShell 7, by the time you read this article 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
PowerShell 7, VS Code, and the PowerShell VS Code Extension 115

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?

This article originally appeared online at https://tfl09.blogspot.com/2020/03/powershell-7-


vs-code-and-powershell-7.html
PowerShell 7 Cross-Platform Scripting
Tips and Traps

by Jeffery Hicks

One of the reasons you want to adopt PowerShell 7 on your desktop, is that it can be used cross-
platform. Theoretically, you can write a PowerShell script or function that works on Windows,
Linux, and Mac. However, this is not without challenges. In some ways, it feels like we are
back to the early days of PowerShell scripting where we are all trying to figure out what to do.
Based on my experiences, let me share some thoughts on what I think you should keep in mind.
Remember, I am focusing on PowerShell code you will write in script files and modules. Using
PowerShell interactively and cross-platform has its own set of rules. Although let me add that the
more experience you have working interactively cross-platform, the easier it will be to write cross-
platform code.

No Aliases
A long established community best practice in PowerShell scripting is not using command and
parameter aliases. In a cross-platform world, this is even more critical. You may have been in the
habit of using Sort in your code in place of Sort-Object. I know I have. I didn’t mind bending the
no alias rule a bit because there was nothing cryptic about Sort. But in the Linux world, sort is
a native command. There is no PowerShell alias. If your code uses sort, on Linux it will call the
native command which will most likely break your code. You need to get in the habit now of using
full cmdlet and parameter names. In Visual Studio Code with the PowerShell extension, it is very
easy to convert all aliases. In the command palette use PowerShell: Expand Alias or the keyboard
shortcut Shift+Alt+E.

No CIM
Another key thing to watch for is the lack of WMI and CIM on non-Windows platforms. This
probably doesn’t come as much of a surprise. CIM support is part of the Windows Management
Framework. The name gives it away. Any script that is using a CIM cmdlet will fail on a non-
Windows system. This also includes commands like Get-Volume, that are using CIM under the hood.
If the command you are using has a -CimSession parameter, it won’t work cross-platform.
PowerShell 7 Cross-Platform Scripting Tips and Traps 117

Testing for Version and Edition


What this means is that you will need to include additional error handling in your commands. You
will need to check items like $PSVersionTable.

PSVersionTable on Linux

Here’s what I have for PowerShell 7 on Windows.

PSVersionTable on Windows

And Windows PowerShell.


PowerShell 7 Cross-Platform Scripting Tips and Traps 118

PSVersionTable on Windows PowerShell

By the way, you can use $PSEdition on PowerShell 7 and Windows PowerShell to get the same
value you see here. In addition to testing values from $PSVersionTable, there are a number of new
variables that might come in handy.
Is Variables⁹³
These variables do not exist in Windows PowerShell. Here’s one way you might use them.

if ($psedition -eq 'core' -AND $IsWindows) {


"PS7 on Windows"
}
elseif ($psedition -eq 'core' -AND (-Not $IsWindows) ) {
"PS7 on Linux OR MacOS"
}
elseif ($PSEdition -eq "Desktop" -AND ($psversiontable.PSVersion.Major -eq 5 -AND $p\
sversiontable.PSVersion.Minor -eq 1)) {
"Must be Windows PowerShell 5.1"
}
else {
"All other Windows PowerShell versions"
}

Testing is important and can be tricky. For example, even though you don’t have Get-Ciminstance
in PowerShell 7 on Linux, you do on Windows. You will need to be familiar with Get-Command and
Get-Module and be able to run them on non-Windows platforms. Here’s a code snippet you might
use to validate a requirement for your code.
⁹³images/hicks-scripting-4.png
PowerShell 7 Cross-Platform Scripting Tips and Traps 119

Try {
Get-Command -name Get-Volume -erroraction stop
}
Catch {
Write-Warning "Command not available on this system."
#bail out of the command
return
}

If you’ve ignored learning how to properly use Try/Catch, now would be a good time. Another
option you might consider adding is the use of Dynamic Parameters. This is a complex topic that I
don’t want to get into here. But you could define some parameters of your command dynamically,
depending on the PowerShell flavor.

Require Minimum Version


If you are writing stand-alone scripts that might be run on different PowerShell versions, you need
to insert a #requires statement at the beginning of your script.

# requires -version 7.0

This is very important if you are using any of the new features in PowerShell 7 such as the new
parallel parameter with ForEach-Object. Read the about_requires help topic for more detail.

Scripting with Remoting


The scripting element you might incorporate is PowerShell remoting. I often use this feature to make
my command easily scale. What you have to take into account is how your command will be used
with remote machines. You might need to define separate parameter sets with clear documentation
that one set is used for Windows machines and another set for Linux. Or Windows PowerShell vs
PowerShell 7.
I love that PowerShell 7 remoting can now use ssh. If you enable and configure it on your remote
machines, you can then use ssh remoting in your code. Or, if you will have a mix of remote
machines, perhaps you write your code to only accept PSSessions. The user of your code would
be responsible for creating PSSessions. Some sessions could use WinRM and some could be using
SSH. Your command can be designed to take an array of PSSessions from the pipeline and internally
you could pass them to Invoke-Command. Of course, you still need to take other error handling,
validation and testing into account. But this could be a simple way to manage multiple remote
systems, regardless of whether they are running Windows or Linux.
PowerShell 7 Cross-Platform Scripting Tips and Traps 120

Your Plan
What does all of this mean for you? First, I’d say you need to become comfortable with PowerShell
7 and all the new features it brings to the party. Assuming you are going to be running PowerShell
7 on your desktop, you can begin testing if the code still works. I have yet to find any scripts or
functions that I wrote for Windows PowerShell fail to run locally under PowerShell 7.
Next, assuming you have a need for PowerShell 7 on remote servers, then you can begin assessing
what you need to manage and how you will go about it. I always stress to students that you have to
think about who is going to run your code. Now, you also have to think about where. Even if most
of your scripting work is for Windows PowerShell, if you begin to pay attention to PowerShell 7
scripting requirements, I think the quality of your code overall will improve.

This article originally appeared online at https://jdhitsolutions.com/blog/scripting/7361/powershell-


7-cross-platform-scripting-tips-and-traps/
Contributors
The following people were kind enough to contribute their time and energy towards this project.
Not only did they create and publish original content on their respective blogs or web sites, but they
also took the extra step to provide their content for this book.

Adam Bertram
Adam Bertram is a 20+ year veteran of IT and an experienced online business professional. He’s
a consultant, Microsoft MVP, blogger, trainer, published author and content marketer for multiple
technology companies.

Dan Franciscus
Dan Franciscus is a veteran IT professional specializing in PowerShell, Active Directory, Windows
Server, and generally automating stuff. His current role focuses on end-user digital experience using
data science techniques.

Dave Carroll
Dave Carroll has spent more than two decades in IT in various capacities with significant focus on
systems administration. For most of this time, he developed automation through multiple scripting
languages, with a strong focus on PowerShell over the last 10 years. Currently, he is an Identity and
Access Management Application Developer for a top university.
You can find more of Dave’s content online on Github at https://github.com/thedavecarroll.

Jeff Hicks
Jeffery Hicks is an IT veteran with almost 30 years of experience, much of it spent as an
IT infrastructure consultant specializing in Microsoft server technologies with an emphasis on
automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award. He works
today as an independent author, teacher and consultant. Jeff has taught and presented on PowerShell
and the benefits of automation to IT Pros worldwide. He has authored and co-authored a number of
books, writes for numerous online sites, a Pluralsight author, and a frequent speaker at technology
conferences and user groups.
Contributors 122

Jonathan Medd
Jonathan is a Cloud Automation Engineer at Atos. A PowerShell MVP between 2010 - 2019 and
VMware vExpert between 2011 and 2020, he is the co-author of VMware vSphere PowerCLI
Reference 1st and 2nd editions and co-organiser of both PSDayUK, a PowerShell one day conference
in the UK, and PowerShell Southampton.

Josh Duffney
Josh Duffney is a DevOps engineer with 10 years of systems administration and engineering expe-
rience. Josh is a Pluralsight author of several courses on the topic of automation and infrastructure
development and blogs at duffney.io. Josh enjoys being an active member of the PowerShell and
DevOps community where he gets to share his knowledge but more importantly enjoys learning
from others in the industry. Outside of his professional work Josh is a practitioner of digital
minimalism who strives to find balance in a digital world. Josh also spends his time weight lifting
and training in the art of Brazilian jiu-jitsu.

Josh King
Josh King is a Microsoft MVP and Systems Administrator at Tribe, an IT services organization
in New Zealand. Josh predominantly works within Windows and VMware environments. He is
also a contributor at TechSnips⁹⁴, an e-learning tech screencast platform. Josh has a passion for
automation and his primary tool of choice is PowerShell. He also has an unhealthy obsession with
toast notifications and how they can be leveraged for script to operator communication.

Mike Kanakos
Mike Kanakos is a 20 year IT infrastructure pro currently working as a Senior Systems Engineer
and Identity Access Management specialist. Mike is also the co-leader of the Research Triangle
PowerShell users group⁹⁵, community lead for PowerShell.org, active blogger and public speaker.
He focuses on teaching PowerShell fundamentals and sharing tools with the community to make a
sysadmin’s life easier.

Prateek Singh
Prateek Singh is an Infrastructure Developer working at LinkedIn⁹⁶, an avid PowerShell blogger,
and a community contributor. In 2017 and 2018, his blog RidiCurious.com as among the “Top 50
⁹⁴https://techsnips.io/
⁹⁵https://rtpsug.com/
⁹⁶https://www.linkedin.com/in/prateeksingh1590/
Contributors 123

PowerShell blogs in the world”. Find his work open-sourced at GitHub⁹⁷, and at Leanpub⁹⁸.

Thomas Lee
PowerShell/Lync Geek, Grateful Dead/Jerry Live Recording Enthusiast and living in the UK.

Tommy Maynard
Tommy Maynard is a Senior Systems Administrator with a passion for PowerShell. He has nearly 20
years of experience in Information Technology, and PowerShell has so far, been the most rewarding
part of his overall career. Luckily for him, PowerShell works alongside the technologies he has long
supported. It often even works with the new ones, too. His goal is to help educate and inspire people
that work in his industry to embrace PowerShell, and in general, automation. Tommy lives in Tucson,
Arizona with his wife Jenn and two kids, Carson and Arianna.

#PS7Now Contributors Online

Author Twitter Blog


Josh King https://twitter.com/WindosNZ https://toastit.dev/
Josh Duffney https://twitter.com/joshduffney http://duffney.io/
Adam Bertram https://twitter.com/adbertram https://adamtheautomator.com/
Mike Kanakos https://twitter.com/MikeKanakos https://www.networkadm.in/
Jonathan Medd https://twitter.com/jonathanmedd https://www.jonathanmedd.net/
Thomas Lee https://twitter.com/doctordns https://tfl09.blogspot.com/
Prateek Singh https://twitter.com/singhprateik https://ridicurious.com
Dave Carroll https://twitter.com/thedavecarroll https://powershell.anovelidea.org/
Dan Franciscus https://twitter.com/dan_franciscus https://winsysblog.com/
Jeff Hicks https://twitter.com/jeffhicks https://jdhitsolutions.com/
Tommy Maynard https://twitter.com/thetommymaynard https://tommymaynard.com/
⁹⁷https://github.com/PrateekKumarSingh
⁹⁸https://leanpub.com/b/books
Release Notes
26 March, 2020
• Initial publication to Leanpub.com.

You might also like