Category Archives: Powershell

Preview updating PowerShell 7.2 with Microsoft Update

This post was originally published on this site

Updating PowerShell 7 with Microsoft Update

Today, we’re happy to announce that we’re taking the first steps to making PowerShell 7 easier than ever to update on Windows 10 and Server. In the past, Windows users were notified in their console that a new version of PowerShell 7 is available, but they still had to hop over to our GitHub release page to download and install it, or rely on a separate package management tool like the Windows Package Manager, Chocolatey, or Scoop. But with Microsoft Update, you’ll get the latest PowerShell 7 updates directly in your traditional Windows Update (WU) management flow, whether that’s with Windows Update for Business, WSUS, SCCM, or the interactive WU dialog in Settings. With today’s announcement, you’ll soon be able to try this new update process for yourself
on the latest PowerShell 7.2 previews.

How updates will work

Because of the large changes and validation required to get this to work, we will publish updates only for future releases. We have already been working on a release that updates 7.2 preview 5 or newer to 7.2 preview 7. We will begin the Microsoft Update publishing process once we release an update to GitHub.

How you can opt-in and help test the upgrade

First, you’ll need to have Windows 10 RS3 (10.0.16299) or newer installed, as well as PowerShell 7.2 preview.5 or preview.6 installed. You’ll also need to ensure that your machine is set up to receive Microsoft Update updates. (On Windows 10, this is done by going to Settings -> Windows Update -> Advanced options and checking “Receive updates for other Microsoft products when you update Windows.” Next, you’ll need to make sure not to update to the latest 7.2 preview.7 or greater using the MSI. Finally, you’ll need to add a specific registry key to opt-in to Microsoft Update usage for PS7. Running the following script from an elevated PowerShell session will setup the registry for this scenario:

$pwshRegPath = "HKLM:SOFTWAREMicrosoftPowerShellCore"
if (!(Test-Path -Path $pwshRegPath)) {
    throw "PowerShell 7 is not installed"

Set-ItemProperty -Path $pwshRegPath -Name UseMU -Value 1 -Type DWord

About a week after PowerShell 7.2 preview.7 update is released, @PowerShell_Team will tweet that the Microsoft Update release is available. At this point, you should be prompted to update PowerShell 7.2-preview in your standard Windows Update workflow.

Test new installs of PowerShell 7.2 preview

If you don’t already have PowerShell 7.2 preview installed, you can still help us try out a new install method! Again, you’ll need to have Windows 10 RS3 (10.0.16299) or newer installed and Microsoft Update enabled. Then, run the following script from an elevated PowerShell session to setup the registry in such a way that Microsoft Update will install and update the latest version of PowerShell 7 preview. Running the following script from an elevated PowerShell session, will setup the registry for this scenario:

$pwshRegPath = "HKLM:SOFTWAREMicrosoftPowerShellCore"
$previewPath = Join-Path -Path $pwshRegPath -ChildPath "InstalledVersions39243d76-adaf-42b1-94fb-16ecf83237c8"
if (!(Test-Path -Path $previewPath)) {
    $null = New-Item -Path $previewPath -ItemType Directory -Force

Set-ItemProperty -Path $pwshRegPath -Name UseMU -Value 1 -Type DWord
Set-ItemProperty -Path $previewPath -Name Install -Value 1 -Type DWord

Note: due to an issue with the installer, make sure to uninstall any previously installed version of the PowerShell Preview MSI.

Also, as noted in the previous section, this will not work until @PowerShell_Team tweets that the 7.2 preview.7 MU release is live.

If you want to disable either scenario

If you hit an issue that is bad enough you want to disable MU-based install or updates, run the following script from an elevated PowerShell session:

$pwshRegPath = "HKLM:SOFTWAREMicrosoftPowerShellCore"
$previewPath = Join-Path -Path $pwshRegPath -ChildPath "InstalledVersions39243d76-adaf-42b1-94fb-16ecf83237c8"
if (!(Test-Path -Path $previewPath)) {
    throw "PowerShell 7 Preview is not installed"

Set-ItemProperty -Path $pwshRegPath -Name UseMU -Value 0 -Type DWord
Set-ItemProperty -Path $previewPath -Name Install -Value 0 -Type DWord

Final words

Going forward we are working to remove the need to manually add the UseMU registry value.

If you try either scenario and it works, please upvote the Microsoft Update discussion. If you have any issue, please file an issue and link it in the discussion above.

Thanks for all your help,

Travis Plunk

The post Preview updating PowerShell 7.2 with Microsoft Update appeared first on PowerShell Team.

PowerShell for Visual Studio Code May 2021 Update

This post was originally published on this site

We are excited to announce that an update to our PowerShell extension is now available on the Visual Studio Code Marketplace. This blog will explain what is new in this release as well as what you can expect from the extension in the coming months.

What’s new in the PowerShell Extension release

This incremental release incorporates a major update to OmniSharp, the Language Server Protocol library we use (like many other VSCode extensions). Behind the scenes there has also been a concerted effort to improve our build and release processes, with the intention of accelerating our release cadence.

Some highlights of the release include:

Since we have disabled semantic highlighting by default now, if you wish to re-enable it, use:

"[powershell]": {
    "editor.semanticHighlighting.enabled": true

The intention of this change is to reduce issues while the semantic highlighting backend is improved.

We now also remove - and $ from the word separators by default for PowerShell files. To add them back, use:

"[powershell]": {
    "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}|;:'",.<>/?"

The intenion of this change is to increase predictability, as double-clicking PowerShell variables now selects the same portion that the extension highlights (with the exception of scoped variables due to the continued inclusion of : as a word separator).

For the full list of updates please refer to the changelog.

More about the OmniSharp update

The most significant change is the update to OmniSharp v0.19.2, from the previous version v0.18.3, released in November 2020. OmniSharp is the underlying Language Server Protocol (LSP) and Debug Adapter Protocol (DAP) server library, and as such is our biggest dependency. This update brings us to the LSP 3.16 and DAP 1.48.x specifications, enabling us to start incorporating all the latest LSP changes, and it includes numerous bug fixes and enhancements resulting in a faster and more stable server and extension experience.

After our initial upgrade to OmniSharp v0.19.0, as early adopters we encountered two major bugs in the library. Rob and Andy spent a month identifying and solving a serialization bug in the Debug Adapter Protocol which would have broken the extension’s debugger, and a race condition which temporarily impacted startup reliability in the preview extension. As our fixes went upstream, we improved the OmniSharp library not only for the PowerShell extension, but also for several other Visual Studio Code extensions similarily relying on OmniSharp.

What’s next for the extensions

Over the coming months we plan to continue work on the Editor Services pipeline stability (intellisense, formatting, etc.), work to maintain compatibility with changes to Visual Studio Code, and improve testing infrastructure (to allow for more community contribution and more predictability with preview releases). You can track the progress on all of these projects, and others on our roadmap, in our GitHub repository.

Getting support and giving feedback

If you encounter any issues with the PowerShell extension in Visual Studio Code or have feature requests, the best place to get support is through our GitHub repository.

Sydney Smith, Andy Schwartzmeyer, Rob Holt

PowerShell Team

The post PowerShell for Visual Studio Code May 2021 Update appeared first on PowerShell Team.

SecretManagement Module v1.1.0 preview update

This post was originally published on this site

Microsoft.PowerShell.SecretManagement 1.1.0-preview

The next 1.1.0 version of SecretManagement has a number of minor fixes, but also two significant changes, one of which is a potentially breaking change for extension authors.
For more information on the changes in this release, see the CHANGELOG document.
This blog discusses the primary changes, why one is potentially breaking, and is especially relevant for extension vault owners who may want to test their vault and make any changes while these updates are in a preview state.

SecretManagement extension vault modules hosting change

SecretManagement extension vaults are PowerShell modules that conform to a special format.
Currently, when SecretManagement loads an extension vault module for use, it loads the module into the current user session.
However, this method of hosting extension vault modules prevented SecretManagement from running in ConstrainedLanguage (CL) mode.
To fix this problem, v1.1.0 of SecretManagement now hosts extension vaults in a separate runspace session.
Changing how the extension module is hosted, has the potential to break existing extension vaults.

What are the effects of the hosting change?

This change mostly affects extension vault authors because they can no longer assume their loaded vault module has access to current user session state.
Generally, extension vaults should never rely on shared user session state.
But if an extension did have this dependency, it would no longer work with this next version of SecretManagement.
We tested some extension vault modules on the PowerShellGallery, but found only one module that was affected by this change (SecretManagement.KeePass).

SecretManagement.KeePass extension vault

The SecretManagement.KeePass extension vault currently relies on its module being loaded in the user session in order to support its vault unlock operation via the Unlock-KeePassSecretVault command.
With the new SecretManagement version, the Unlock-KeePassSecretVault cmdlet is no longer effective, and the user is always prompted for a password when required by the vault.
This can be problematic for scripts being run unattended that do not support user interaction.

The KeePass vault needed to use this user shared state approach due to a deficiency in SecretManagement.
SecretManagement did not support a vault unlock operation, and required vaults that needed it to implement their own.
So one of the changes in this release is to add a new extension vault function, Unlock-SecretVault, that allows extension vaults to provide this functionality directly through SecretManagement.
With this new function, the KeePass vault no longer needs to leverage shared state with the user session.

New Unlock-SecretVault command

SecretManagement now supports a new Unlock-SecretVault command.
Extension vaults that require unlocking can optionally support this by implementing the Unlock-SecretVault function in their extension.
SecretManagement.KeePass extension vault module will be updated to use this new function, mitigating the problem.
For more information see SecretManagement Readme and Design documentation.

Other differences you may notice

Since the extension vaults are no longer loaded in the user session, you will no longer see them listed when you run the Get-Module command, which lists the currently loaded modules in your session.
This is arguably a good thing, since users generally don’t want or need to know about extension vault modules, and don’t need to see them in their current session.
Seeing extension vault modules unexpectedly loaded in their sessions may be confusing to users.

Some extension vault modules, such as Microsoft.PowerShell.SecretStore and SecretManagement.KeePass, include additional commands for the user.
The Microsoft.PowerShell.SecretStore vault provides additional commands for configuring the vault.
The SecretManagement.KeepPass vault provides additional commands for unlocking and registering the vault.
These commands will continue to work.
When these commands are run, the extension vault module will be visible from Get-Module because the commands are run in the current user session.
But that is the only time the extension vault modules will be loaded in the user session.

What is a runspace?

A PowerShell runspace encompasses the session context in which PowerShell scripts run, and can be thought of as an individual PowerShell session.
The PowerShell command shell usually has just a single session, but can support any number of sessions via multiple runspaces.
The runspace isolates multiple running scripts from each other within a single process.

What is CL mode?

Constrained Language is a PowerShell language mode that restricts language elements which can be used to invoke arbitrary APIs.
It is commonly used within a system wide application control policy, such as Windows AppLocker or Windows Defender Application Control, that restricts what applications are available and what scripts are trusted on the system.
Untrusted scripts run in ConstrainedLanguage while trusted scripts run in FullLanguage mode.

Feedback and Support

Community feedback has been essential to the iterative development of these modules.
Thank you to everyone who has contributed issues, and feedback thus far!
To file issues or get support for the SecretManagement interface or vault development experience please use the SecretManagement repository.
For issues which pertain specifically to the SecretStore and its cmdlet interface please use the SecretStore repository.

The post SecretManagement Module v1.1.0 preview update appeared first on PowerShell Team.

Announcing PlatyPS 2.0.0-Preview1

This post was originally published on this site

PlatyPS is the primary tool for creating the PowerShell help displayed using Get-Help.

What is PlatyPS

PowerShell external help files have been authored by hand or using complex tool chains and stored as
MAML XML for use as console help.
MAML is cumbersome to edit by
hand, and common tools and editors don’t support it for complex scenarios like they do with
markdown. Markdown is adopted widely by
Open Source, supported by many editors including
Visual Studio Code, and with minimal rules, easier to author.
PlatyPS is provided as a solution to allow documenting PowerShell help in any editor or tool
that supports markdown.

PlatyPS handles PowerShell documentation for complex scenarios (e.g. very large, closed source,
and/or C#/binary modules) where it may be desirable to have documentation abstracted away from the
codebase. PlatyPS does not need source access to generate documentation. PlatyPS solves this
scenario today at Microsoft by delivering
updatable help
files for PowerShell, SCCM, Windows, and other supported modules. Module authors may also use
PlatyPS to create the help that ships with their modules.

Announcing PlatyPS 2.0-Preview1

We are pleased to announce the release of PlatyPS 2.0.0-Preview1.

Our main goal for the 2.0.0 release is to maintain compatibility while fixing long standing issues.
We are re-writing PlatyPS in C# to leverage the existing markdown C# library allowing us to
address many of the open issues and make schema improvements recommended by the community. The main
functionality of PlatyPS remains unchanged except for community suggested bug fixes and
enhancements. We do expect a few necessary breaking changes in future previews.

In this Preview release, we focused on:

  • re-write in C# leveraging Markdig for parsing markdown.
  • updated New-MarkdownHelp
  • updated Get-MarkdownMetadata

Future preview releases will include more performance improvements and cmdlet updates.

What’s supported

PlatyPS 2.0.0-Preview1 is currently available for download from the PowerShell Gallery.

Supported PowerShell versions for PlatyPS 2.0.0:

  • Windows PowerShell 5.1+
  • PowerShell 7.0+

Installing PlatyPS

To begin working with PlatyPS 2.0 Preview1, download and install the PlatyPS module from PSGallery.

Install-Module PlatyPS -AllowPrerelease

Markdown Schema Updates

Markdown is designed to be human-readable, without rendering. This makes writing and editing easy
and efficient. Many editors support markdown including Visual Studio Code. PlatyPS expects
markdown to be authored in a particular way. We have defined a schema to determine how parameters
are described, where scripts examples are shown, and so on.

The schema closely resembles the existing output format of the Get-Help cmdlet in PowerShell. If
you break the schema in your markdown, you will receive error messages.

For more information about the planned changes, see the
PlatyPS Design Specification.
Note, this is a draft specification subject to change.

Documentation to get started

For information about PlatyPS including cmdlet reference, see PlatypS

For additional information and examples of working with PlatyPS, see
Create XML-based help using PlatyPS.

Call to action

Our goal is to make it easier to update and maintain module/cmdlet help files. We value your ideas
and feedback. Stop by our GitHub repository and let us know of any issues or features you would like

For more information about PlatyPS issues and features, see:
PlatyPS on GitHub

Jason Helmick
Program Manager, PowerShell

The post Announcing PlatyPS 2.0.0-Preview1 appeared first on PowerShell Team.

Announcing PSDesiredStateConfiguration on PowerShell Gallery

This post was originally published on this site

Continuing our investment in DSC, we are pleased to announce the release of
PSDesiredStateConfiguration 2.0.5 for DSC as a separate module on PowerShell Gallery. This is in
preparation for publishing previews of the 3.0 version of this module which will have new
capabilities, but also some breaking changes.

DSC is an important platform in configuration management, and we value the feedback we are
receiving from the community. To further the goals of DSC, PSDesiredStateConfiguration module
will no longer be included in the PowerShell package beginning in a future release of PowerShell 7.2
preview. Separating DSC into its own module allows us to invest and develop DSC independent of
PowerShell and reduces the size of the PowerShell package. Users of DSC will enjoy the benefit of
upgrading DSC without the need to upgrade PowerShell, accelerating time to deployment of new DSC

Customers impacted by this change and wishing to remain on DSC v2 can download
PSDesiredStateConfiguration 2.0.5 for Windows PowerShell from the PowerShell Gallery. Customers
working with non-Windows environments can expect cross-platform features in DSC v3. For more
information about DSC and the teams on-going investments for 2021, see
PowerShell Team 2021 Investments

To download PSDesiredStateConfiguration from the PowerShell Gallery:

To install PSDesiredStateConfiguration:

Install-Module -Name PSDesiredStateConfiguration -Repository PSGallery -MaximumVersion 2.99


Be sure to include the parameter MaximumVersion or you
will receive the latest version of PSDesireStateConfiguration which contains significant

For more information about DSC v3 and the teams investments for 2021, see
PowerShell Team 2021 Investments

The post Announcing PSDesiredStateConfiguration on PowerShell Gallery appeared first on PowerShell Team.

Announcing PowerShell Crescendo Preview.2

This post was originally published on this site

We are pleased to announce the second preview of PowerShell Crescendo, a framework to rapidly
develop PowerShell cmdlets for native commands, regardless of platform.

The updated preview releases are now available for download on the PowerShell Gallery:

To install Microsoft.PowerShell.Crescendo:

Install-Module Microsoft.PowerShell.Crescendo -AllowPrerelease

For more information on Microsoft.PowerShell.Crescendo, check out these previous blog posts:

Crescendo Preview 2 Updates

This update to Crescendo adds elevation support for native commands, and generation of a module
manifest when exporting a Crescendo module. Read the full list of changes below:

  • Added support for native command elevation on Windows and Linux platforms. (Issue #50)
  • Export-CrescendoModule now exports a module manifest (psd1) along with the module file (psm1). (Issue #51)
  • Added support for generating aliases for Crescendo cmdlets (Issue #52)
  • Added support for SupportsShouldProcess. Thanks jdhitsolutions! (Issue #59)

Native Command elevation

Native commands may require administrative elevation to perform the requested operation. The
Crescendo schema has been extended to support elevation on Windows and Linux/macOS platforms. The schema
supports the new keyword Elevation that has two properties Command and Arguments.

  • Command: Defines the elevation mechanism to be used to elevate the native command. The function
    Invoke-WindowsNativeAppWithElevation has been included to aid with elevation on Windows. sudo
    has been tested as well.
  • Arguments: These are the parameters to be used in conjunction with the elevation command. This can
    be a collection of parameters.

    • OriginalName: This is the parameter to be used with the elevation command.
    • DefaultValue: The default parameter value.

Windows Native Command Elevation

Elevation on Windows is supported using the built-in function
Invoke-WindowsNativeAppWithElevation. Invoke-WindowsNativeAppWithElevation uses Start-Process
with a PSCredential to invoke the native command and captures the output and errors of the native
command which are returned to the user. This function is inserted into the Crescendo module you
export. In the example below, the PowerShell cmdlet Get-Credential will prompt for credentials
necessary to elevate the native command when the exported Crescendo function is executed.

"Elevation": {
        "Command": "Invoke-WindowsNativeAppWithElevation",
        "Arguments": [
                "OriginalName" : "-Credential",
                "DefaultValue": "(get-credential)"

In automation scenarios that require elevation without user interaction, Crescendo supports
retrieving credentials from secured vaults. In the example below, credentials for elevation are
retrieved from SecretManagement and SecretStore.

For more information about storing and managing secrets, see [SecretManagement Announcement](

"Elevation": {
        "Command": "Invoke-WindowsNativeAppWithElevation",
        "Arguments": [
                "OriginalName" : "-Credential",
                "DefaultValue": "(get-secret admin)"

Elevation can be defined for each cmdlet. When authoring a Crescendo json configuration, include
the elevation definition before the parameters are defined. In the example below, the native command
netsh.exe requires elevation to enable and disable the Windows Firewall.

    "$schema": "./Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Set",
    "Noun": "WinFirewall",
    "Platform": ["Windows"],
    "OriginalCommandElements": ["advfirewall", "set", "allprofiles", "state"],
    "OriginalName": "$env:Windir/system32/netsh.exe",
    "Elevation": {
        "Command": "Invoke-WindowsNativeAppWithElevation",
        "Arguments": [
                "OriginalName" : "-Credential",
                "DefaultValue": "(get-credential)"
    "DefaultParameterSetName": "Enable",
    "Parameters": [
            "OriginalName": "on",
            "Name": "On",
            "ParameterType": "switch",
            "ParameterSetName": ["Enable"]
            "OriginalName": "off",
            "Name": "Off",
            "ParameterType": "switch",
            "ParameterSetName": ["Disable"]

Linux Native Command Elevation

Native command elevation for Linux and macOS is handled through the command sudo. To enable
elevation for Crescendo, include sudo as the Command value. In the example below, when the
function Get-TimeServer is executed, the user is prompted for the sudo password. The native
command is elevated with sudo privileges. In automation scenarios that require elevation without
user interaction, configure the sudoers file.

    "$schema": "../src/Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Get",
    "Noun": "TimeServer",
    "Elevation": {
        "Command": "sudo"
    "OriginalCommandElements": ["-getnetworktimeserver"],
    "OriginalName": "/usr/sbin/systemsetup",
    "Platform": ["MacOS"],
    "OutputHandlers": [
            "ParameterSetName": "Default",
            "Handler": "$args|%{[pscustomobject]@{ TimeServer = $_.Split(':')[1].Trim()}}"


Cmdlets that cause change to the system, such as files and service configurations, create a risk
that could negatively impact operations. To help mitigate risk, cmdlets support common parameters
-WhatIf and -Confirm. These parameters provide greater detail about the target of each
change, and provides a confirmation mechanism allowing the user to approve each change. Crescendo
supports this functionality when wrapping native commands though the schema keyword

In the example below, The keyword SupportsShouldProcess accepts a boolean to enable the common
parameters -WhatIf and -Confirm.

    "$schema": "../src/Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Remove",
    "Noun": "DockerImage",
    "OriginalName": "docker",
    "SupportsShouldProcess": true,
    "OriginalCommandElements": [
    "Parameters": [
            "Name": "ID",
            "OriginalName": "",
            "Mandatory": true,
            "ValueFromPipelineByPropertyName": true

Executing Remove-DockerImage with the -WhatIf parameter supported by SupportsShouldProcess.

Get-DockerImage | Where-Object {$ -match "d70eaf7277ea"} | Remove-DockerImage -WhatIf

When SupportsShouldProcess is set True, the following expected output will result.

What if: Performing the operation "Remove-DockerImage" on target "docker image rm d70eaf7277ea".


Aliases for Crecendo wrapped native commands are now supported in the Crescendo schema with the
keyword Aliases. In the snippet below, the cmdlet definition Set-WinFirewall includes the alias
definition SFW. When exported, the Crescendo created module will include the Set-WinFirewall
function and SFW alias.

    "$schema": "./Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Set",
    "Noun": "WinFirewall",
    "Platform": ["Windows"],
    "OriginalCommandElements": ["advfirewall", "set", "allprofiles", "state"],
    "OriginalName": "$env:Windir/system32/netsh.exe",
    "Aliases": ["SFW"],
    "Elevation": {
        "Command": "Invoke-WindowsNativeAppWithElevation",
        "Arguments": [
                "OriginalName" : "-Credential",
                "DefaultValue": "(get-credential)"

Future plans

Next/Future plans for preview.3 will be based on feedback and include a few items under investigation:

  • Improved OutputHandler support for building objects from native command string output.
  • Support for multiple cmdlet definitions in a single json configuration.

Our goal is to make it easier to convert your native commands to PowerShell cmdlets and receive the
benefits that PowerShell provides. We value your ideas and feedback and hope you will give Crescendo
a try, then stop by our GitHub repository and let us know of any issues or features you would like

For more information about PowerShell Crescendo issues and features, see:
Crescendo on GitHub

Jason Helmick

Program Manager, PowerShell

The post Announcing PowerShell Crescendo Preview.2 appeared first on PowerShell Team.

Optimizing your $Profile

This post was originally published on this site

Optimizing your $Profile

Your PowerShell Profile allows you to customize
your PowerShell session and runs at startup.
Complex profiles can cause a significant delay in the startup of PowerShell as it is a script that needs to be executed before the prompt first
shows up.

Spoiler: I’ll show how I got my profile loading time from 1465 ms to 217 ms!

It’s important to note that many of the optmizations I cover here are micro-optimizations.
They won’t have significant overall impact and, in general, may not be the best way to write your scripts.
In the case of a profile, I’m optimizing for speed and, in some cases, making the script slightly harder to read as a tradeoff.
For production scripts used in automation, the micro performance improvement may not be worthwhile as maintainability of the script
may be more important.

Using GitHub to store my profile.ps1

As part of working on PowerShell on GitHub, I have a macBook Pro, a Windows desktop, and also a Linux desktop I use frequently.
I like to keep my PowerShell environment customizations in sync across these devices without having to manually update the profile should I make changes.
The solution I decided upon was to publish my profile as a GitHub Gist.

NOTE: that the current version on GitHub contains all the optimizations covered in this blog post, you can look at the revisions on GitHub
to see how my profile has changed over time.
I would NOT recommend using my profile directly as I may make changes that break you or are not applicable to your daily usage of PowerShell.
Use it more as an example.

The key parts of the code that does this is a ThreadJob that makes a REST call to GitHub
to retrieve the latest version of my profile and compare with the current version.
Creating the ThreadJob does take time, but because I’m making a networking call which can lead to variable execution time, it’s a worthwhile
Also, because that scriptblock is running as a separate thread from the main startup thread, I don’t have to worry about performance optimizations
of that ThreadJob scriptblock.

My profile contains a # Version x.y.z comment near the top of the script and this version string is used to determine if the one on GitHub
is newer than the one that’s currently running.
A regular expression is used to parse this from the profile.
If the ThreadJob determines there is a newer version, it saves that version string to a file that I can easily check at the start of my profile.
At the start of my profile, I check the current loaded version against this file and will prompt to install the latest version if there is one.
This means that on startup, I won’t know that a newer version exists until the next startup, but it’s a worthwhile tradeoff.

Getting a baseline for pwsh

Before we start making changes, we want to get a baseline so we know what impact, if any, our changes are affecting the startup performance.
I wanted to separate the startup time of pwsh itself from the execution of my profile.
I got an average startup, in milliseconds, for pwsh starting up without loading any profile.

Here I use the Measure-Command cmdlet to make this easy.
However, I do a loop of 100 times to make sure any variance of my computer are accounted for and calculate the average time:

$p = 0
1..100 | ForEach-Object {
    Write-Progress -Id 1 -Activity 'pwsh' -PercentComplete $_
    $p += (Measure-Command {
        pwsh -noprofile -command 1
Write-Progress -id 1 -Activity 'profile' -Completed
$p = $p/100

I’m using the variable $p to store the result as I want to subtract that time from my profile measurements.
Since running this 100 times can take some time, I like to know how far it’s progressed, so I’m using Write-Progress as a visual indicator of
how many more need to be run.
Since the writing of progress is not in the scriptblock used by Measure-Command, it has no impact on the measured time.
pwsh -noprofile -command 1 will ensure that when PowerShell starts, it doesn’t load my profile, and the command 1 simply has PowerShell
emit the number 1 and exit.

For my baseline, I got a time of 1176 ms for the startup of pwsh.

Getting a baseline for my profile

To get the average start time of my profile, the script is quite similar:

$a = 0
1..100 | ForEach-Object {
    Write-Progress -Id 1 -Activity 'profile' -PercentComplete $_
    $a += (Measure-Command {
        pwsh -command 1
Write-Progress -id 1 -activity 'profile' -Completed
$a/100 - $p

The only major difference here is not using -noprofile so that my profile is loaded and also subtracting the startup time of pwsh $p from the result.
I got a time of 1465 ms for the startup of my profile.


Mathias Jessen published a great profiling tool for scripts called PSProfiler
that I decided to use against my profile to see which lines were taking the most time.

The module doesn’t require PowerShell 7, but if you use it with PowerShell 7.2, then you get some coloring to help identify the top lines that
are taking the longest execution time:

Measure-Script image

This screenshot shows a few lines I should focus on optimizing first.
At the top, you can see that reading contents of a file was identified as a performance hot spot.


One thing that is not obvious is that when you use the Get-Content cmdlet, it will add some additional note properties to the output identifying
the original source of the content.

For my usage in the profile, I have no need for this metadata.
The cmdlet has a -ReadCount parameter that specifies how to batch lines sent through the pipeline.
However, it also tells the cmdlet not to add the additional source information note properties.
Since I’m not using the pipeline to process the contents, using -ReadCount 0 will help avoid incurring the cost of the cmdlet adding the note properties.

This saved a very tiny amount of milliseconds although a good percentage improvement.
Using Get-Content $profile -Raw was 0.807888 ms, Get-Content $profile -Raw -ReadCount 0 was 0.651742 ms.
Instead of relying on the cmdlet, I could simply call the equivalent .NET API directly.
Since I’m not using the features of the cmdlet and simply getting the contents of the file, I could use [System.IO.File]::ReadAllText($profile) which took 0.185826 ms.

Now this is a much more significant improvement.
Also, these measurements were taken when the Get-Content cmdlet from the Management module was already loaded.
So for profile startup where the module is not loaded, this would be even more significant.

Changing cmdlets to using .NET APIs

Using cmdlets can make writing scripts much easier and potentially easier to read.
However, there is a startup time impact of loading the module (which itself is a series of complex steps), having the cmdlet process the input, and finally wrapping
the result in a PSObject and emitting to the pipeline.
Since my profile was not using any of the advanced capabilities of many cmdlets, I proceeded to change most of my cmdlet usage to .NET APIs.

In the screenshot, you can see that cmdlets like Join-Path and Resolve-Path are another line that is relatively slow and my profile uses these two cmdlets quite often.
Join-Path can be replaced by [System.IO.Path]::Combine().
I was using Resolve-Path ~ as Windows doesn’t understand that the tilde is supposed to point to the user’s home directly.
However, PowerShell already has a $HOME variable for that purpose, so I simply removed the use of Resolve-Path and used $HOME directly instead.

Taking another measurement

With all the changes from using cmdlets to .NET APIs, I was able to drop the start time to 1404 ms from 1465 ms.
This was pretty good for minimal work, but not enough of an improvement that I would notice on a daily basis.

Using Measure-Script again, I can see that the new hot spots were with Get-Command to see if some commands were available before doing some work.
There isn’t an equivalent .NET API to Get-Command, however, I could just try executing a command or cmdlet and catch the CommandNotFoundException.

Get-Command import-foo -ErrorAction Ignore takes about 15.8 ms.
However, try { import-foo } catch [System.Management.Automation.CommandNotFoundException] { } only takes about 10 ms.

With a few more changes I would also be reduce the profile start time to 1360 ms.
This was about a 100 ms improvement, but still not something I would notice on a daily basis.
To make an improvement that was noticeable I needed to take a different approach.

A new hope

It occurred to me that lots of the script I had in my profile were only ever intended for when I used PowerShell interactively.
This includes customizations to PSReadLine, ensuring dotnet was in my path, creating PSDrives as shortcuts to my test folder and git folders.
So the big change was to move all of that into a new private function called Initialize-Profile.
I would then update my prompt function to check if this function was ever called and if not, then call it to complete setting up my environment.

This meant that the majority of the script was moved to Initialize-Profile which was only ever run if there was an interactive prompt.
With this change, my profile startup reduced to 217 ms!

In a way, this is cheating a little bit as it will delay the first time I see a prompt when PowerShell starts up.
However, it’s a definite psycological impact not seeing the message from PowerShell telling me my profile was taking a large amount of the overall startup time.
There are other improvements I could make to defer more initialization until needed.
For example, only adding some tools to my $env:PATH when my file location is in a git repo.


  • Use tools like PSProfiler to identify hot spots in your scripts to know where to focus optimization efforts.
  • Understand trade offs for when to use cmdlets vs .NET APIs.
  • Micro-performance improvements do not necessarily translate to real world benefits.
  • Defer some initialization in your profile to the prompt if you can separate parts of your profile for interactive use vs automation.

The post Optimizing your $Profile appeared first on PowerShell Team.

SecretStore Release Candidate 3

This post was originally published on this site

The SecretStore release candidate 3 (RC3) module is now available on the PowerShell Gallery. This contains an exciting new feature which allows users to non-interactively create, and configure a SecretStore. This feature was added to support CI systems and other automated scenarios.

SecretStore is an extension vault module, for PowerShell SecretManagement, which works over all supported PowerShell platforms on Windows, Linux, and macOS. For more context on this module and the SecretManagement module refer to the previous blog posts:

For more context on these modules check out these previous blog posts:

Before installing this module, please uninstall the current preview versions of the module and restart your PowerShell session.

To install these updates run the following commands:

Uninstall-Module Microsoft.PowerShell.SecretStore -Force
# Restart your PowerShell session
Install-Module -Name Microsoft.PowerShell.SecretStore -Repository PSGallery
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault -AllowClobber

SecretStore Updates

Previously, Set-SecretStoreConfiguration required manual password confirmation to make changes to SecretStore configuration. This update adds a -Password parameter to Set-SecretStoreConfiguration to endable automated creation and configuration of a SecretVault.

Breaking Change

  • The -Force parameter was removed from the Set-SecretStoreConfiguration command, and instead the -Confirm:$false should be used to suppress PowerShell confirmation prompting in automation scripts.

New Feature

  • Set-SecretStoreConfiguration command now takes a -Password parameter so that there is no need to prompt for a password

How to non-interactively create and configure a SecretStore

This is an example of automation script that installs and configures the Microsoft.PowerShell.SecretStore module without user prompting. The configuration requires a password and sets user interaction to None, so that SecretStore will never prompt the user. The configuration also requires a password, and the password is passed in as a SecureString object. The -Confirm:false parameter is used so that PowerShell will not prompt for confirmation.

The SecretStore password must be provided in a secure fashion. Here the password is being imported from an encrypted file using Windows Data Protection API, but this is a Windows only solution. Another option is to use a CI system mechanism such as secure variables.

Next, the SecretManagement module is installed and the SecretStore module registered so that the SecretStore secrets can be managed.

The Unlock-SecretStore cmdlet is used to unlock the SecretStore for this session. The password timeout was configured for 1 hour and SecretStore will remain unlocked in the session for that amount of time, after which it will need to be unlocked again before secrets can be accessed.

Install-Module -Name Microsoft.PowerShell.SecretStore -Repository PSGallery -Force
$password = Import-CliXml -Path $securePasswordPath

Set-SecretStoreConfiguration -Scope CurrentUser -Authentication Password -PasswordTimeout 3600 -Interaction None -Password $password -Confirm:$false

Install-Module -Name Microsoft.PowerShell.SecretManagement -Repository PSGallery -Force
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault

Unlock-SecretStore -Password $password

General Availability (GA)

This is a “go live” release, which means that we feel that this RC is feature complete and supported in production.

If no bugs are identified through this release, we will increment the versioning and declare the module as GA in late March. If any high-risk bugs are identified we will continue to release RCs until the quality bar is met for a GA release.

Feedback and Support

Community feedback has been essential to the iterative development of these modules. Thank you to everyone who has contributed issues, and feedback thus far! To file issues or get support for the SecretManagement interface or vault development experience please use the SecretManagement repository. For issues which pertain specifically to the SecretStore and its cmdlet interface please use the SecretStore repository.

Sydney Smith

PowerShell Team



The post SecretStore Release Candidate 3 appeared first on PowerShell Team.

Updating help for the PSReadLine module in Windows PowerShell 5.1

This post was originally published on this site

Back in November of 2020 I
posted instructions
about how to work around the problem of updating the help for the PSReadLine module.

The version of the PSReadline module that shipped in Windows PowerShell 5.1 used a lowercase
letter in the name. The name of the module was changed for the release of PowerShell 6. It now uses
a capital L in the name. And even after following the instructions in the previous post, you still
get the same error when you try to update help for Windows PowerShell 5.1.

Failed to update Help for the module(s) ‘PSReadline

The root problem in Windows PowerShell 5.1 is that the module is automatically loaded at startup
using the PSReadline name (with the lowercase l). Use the following steps workaround this
problem and update the help.

  1. Close all powershell.exe processes. Then rename the PSReadline module folder name to
  2. Open an powershell.exe session in Administrator privilege.
  3. Run Remove-Module PSReadLine to remove the auto-loaded PSReadLine module.
  4. Run Import-Module PSReadLine (use PSReadLine with capital L).
  5. Then run Update-Help -Module PSReadLine -Force

These steps load the module in your session using the new name, which allows Update-Help to use the
correct name to find the help content.

The post Updating help for the PSReadLine module in Windows PowerShell 5.1 appeared first on PowerShell Team.

Announcing PSReadLine 2.2-beta.2 with Dynamic help

This post was originally published on this site

We are pleased to announce the new feature Dynamic Help in
PSReadLine 2.2.0-beta.2.
Features like TabCompletetion and the Help system make PowerShell easier to use. Expanding on that
theme, Dynamic Help provides just-in-time help at the command line. This experience allows you to
stay focused on your work in the terminal without losing your place typing on the pipeline.

Supported Releases

Dynamic Help is implemented and presented through the PSReadLine module. In an effort to bring the
benefits of Dynamic Help to the most users, Dynamic Help is supported down-level to Windows
PowerShell 5.1.

PSReadLine 2.2.0-beta2, including Dynamic Help, is supported in the following versions:

  • Windows PowerShell 5.1
  • PowerShell 7.0+

Install PSReadLine with Dynamic Help

To receive the benefits of Dynamic Help, download and install the PSReadLine module from PSGallery.

Install-Module PSReadLine -AllowPrerelease

Getting Cmdlet Help

Dynamic Help provides a view of full cmdlet help shown on an alternative screen buffer using a cross
platform Pager utility. PSReadLine maps the function ShowCommandHelp to the F1 key.

  • When the cursor is at the end of a fully expanded cmdlet, pressing F1 displays the help for
    that cmdlet.
  • When the cursor is at the end of a fully expanded parameter, pressing F1 displays the help
    beginning at the parameter.

Image DynHelp1

The Pager in PSReadLine supports viewing content one screen at a time. To navigate the displayed
help content, press the up and down arrow keys to scroll the screen. Pressing Q exits the
alternative screen buffer and returns to the current cursor location in the the primary screen

The Pager is a work in progress and we welcome your feedback and suggestions. To provide feedback
and for more information, see the Pager repository on GitHub.

Focused Parameter Help

Pressing the Alt-h key combination provides dynamic help for parameters. The help is shown below
the current command line like MenuComplete. The cursor must be at the end of the fully-expanded
parameter name when you press the Alt-h key.

Image DynHelp Alt H


The PSReadLine function ShowParameterHelp is bound to Alt-h.
The Alt bindings do not currently work on macOS as detailed in this
issue. Mac users will need to map
ShowParameterHelp function to an available key combination.

Set-PSReadLineKeyHandler -chord "Ctrl-l" -Function ShowParameterHelp

Selection of Arguments

To rapidly select and change the arguments of a cmdlet without disturbing your syntax, press
Alt-a. Selection of arguments is scoped within a script block. Based on the cursor position, it
searches from the innermost script block to the outmost script block, and stops when it finds any
arguments in a script block scope.

Image dynhelpA


The PSReadLine function SelectCommandArgument is bound to
Alt-a. The Alt bindings do not currently work on macOS as detailed in this
issue. Mac users will need to map
SelectCommandArgument function to an available key combination.

Set-PSReadLineKeyHandler -chord "Ctrl-k" -function SelectCommandArgument

Feedback and Support

Community feedback is essential to the iterative development of PSReadLine and features like Dynamic
Help. Thank you for your continued support and engagement. To file issues for PSReadLine or Dynamic
Help, please visit the PSReadLine repository.

Jason Helmick

PowerShell Team

The post Announcing PSReadLine 2.2-beta.2 with Dynamic help appeared first on PowerShell Team.