Technical Documentation: PowerShell/powershell
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
![logo][] PowerShell
Welcome to the PowerShell GitHub Community!
PowerShell is a cross-platform (Windows, Linux, and macOS) automation and configuration tool/framework that works well with your existing tools and is optimized
for dealing with structured data (e.g. JSON, CSV, XML, etc.), REST APIs, and object models.
It includes a command-line shell, an associated scripting language, and a framework for processing cmdlets.
Windows PowerShell vs. PowerShell 7+
Although this repository started as a fork of the Windows PowerShell codebase, changes made in this repository are not ported back to Windows PowerShell 5.1.
This also means that issues tracked here are only for PowerShell 7.x and higher.
Windows PowerShell specific issues should be reported with the Feedback Hub app, by choosing "Apps > PowerShell" in the category.
New to PowerShell?
If you are new to PowerShell and want to learn more, we recommend reviewing the getting started documentation.
Get PowerShell
PowerShell is supported on Windows, macOS, and a variety of Linux platforms. For
more information, see Installing PowerShell.
Upgrading PowerShell
For best results when upgrading, you should use the same install method you used when you first
installed PowerShell. The update method is different for each platform and install method.
Community Dashboard
Dashboard with visualizations for community contributions and project status using PowerShell, Azure, and PowerBI.
For more information on how and why we built this dashboard, check out this blog post.
Discussions
GitHub Discussions is a feature to enable free and open discussions within the community
for topics that are not related to code, unlike issues.
This is an experiment we are trying in our repositories, to see if it helps move discussions out of issues so that issues remain actionable by the team or members of the community.
There should be no expectation that PowerShell team members are regular participants in these discussions.
Individual PowerShell team members may choose to participate in discussions, but the expectation is that community members help drive discussions so that team members
can focus on issues.
Create or join a discussion.
Chat
Want to chat with other members of the PowerShell community?
There are dozens of topic-specific channels on our community-driven PowerShell Virtual User Group, which you can join on:
Developing and Contributing
Want to contribute to PowerShell? Please start with the Contribution Guide to learn how to develop and contribute.
If you are developing .NET Core C# applications targeting PowerShell Core, check out our FAQ to learn more about the PowerShell SDK NuGet package.
Also, make sure to check out our PowerShell-RFC repository for request-for-comments (RFC) documents to submit and give comments on proposed and future designs.
Building PowerShell
| Linux | Windows | macOS |
|---|---|---|
| Instructions | Instructions | Instructions |
If you have any problems building PowerShell, please start by consulting the developer FAQ.
Downloading the Source Code
You can clone the repository:
git clone https://github.com/PowerShell/PowerShell.gitFor more information, see working with the PowerShell repository.
Support
For support, see the Support Section.
Legal and Licensing
PowerShell is licensed under the MIT license.
Docker Containers
[!Important]
The PowerShell container images are now maintained by the .NET team. The containers atmcr.microsoft.com/powershellare currently not maintained.
License: By requesting and using the Container OS Image for Windows containers, you acknowledge, understand, and consent to the Supplemental License Terms available on Microsoft Artifact Registry.
Telemetry
Please visit our about_Telemetry
topic to read details about telemetry gathered by PowerShell.
Governance
The governance policy for the PowerShell project is described the PowerShell Governance document.
[Code of Conduct](CODE_OF_CONDUCT.md)
Please see our Code of Conduct before participating in this project.
[Security Policy](.github/SECURITY.md)
For any security issues, please see our Security Policy.
Overview (`CHANGELOG/README.md`)
Changelogs
- Current preview changelog
- 7.6 changelog
- 7.5 changelog
- 7.4 changelog
- 7.3 changelog
- 7.2 changelog
- 7.1 changelog
- 7.0 changelog
- 6.2 changelog
- 6.1 changelog
- 6.0 changelog
Overview (`assets/README.md`)
Use of Trademarked Logos
The assets in this folder are trademarked by Microsoft and don't fall under the same License as the source code.
Permitted Uses
Parties interested in using these logos can review the Use of Microsoft Copyrighted Material page.
If the use falls into any of those categories, you can move forward with the use and will not need additional permission.
Third Party Permission Requests
If questions remain after reviewing the permitted uses page, please submit a request to Third Party Permissions.
The people supporting this will direct the request depending upon the content.
If a request needs to be submitted to the Third Party Permissions, the request should include the following:
- A statement that the Use of Microsoft Copyrighted Material was reviewed and did not address the situation.
- A statement that Microsoft (and not a third party) is the owner of the logo at issue.
- Clear identification of the materials to be used (i.e., the picture of the book cover included by the author in the letter request should suffice).
- A description, including URLs of how they found or located the logo.
- A description of how the party intends to use or distribute the logo (i.e., that it is for the book cover of an instructional book on PowerShell).
- A description of how long the party needs to use the logo.
Overview (`docker/README.md`)
Docker
Dockerfiles to build the Docker images for PowerShell Core have been moved to PowerShell/PowerShell-Docker.
Overview (`test/README.md`)
Testing
The tests are organized by testing language. Thus Pester tests, which
are written in the PowerShell language, are in
./powershell and xUnit tests, written in C#, are in
./xUnit. The third-party
shebang test is in ./shebang.
Overview (`tools/install-powershell.ps1-README.md`)
install-powershell.ps1
Features of install-powershell.ps1
- Can be called directly from Git
- Optionally allows install of the latest Preview build
- Optionally installs using the latest MSI
- Automatically looks up latest version via Git tags
- Optionally installs silently
- Optionally adds the install location to Path environment variable
Examples
Install PowerShell Preview Build
Invoke-Expression "& { $(Invoke-RestMethod 'https://aka.ms/install-powershell.ps1') } -Preview"Install PowerShell using the MSI installer
Invoke-Expression "& { $(Invoke-RestMethod 'https://aka.ms/install-powershell.ps1') } -UseMSI"Overview (`tools/install-powershell.sh-README.md`)
install-powershell.sh
Features of install-powershell.sh
- Can be called directly from git
- Optionally installs vs code and vs powershell extension (aka PowerShell IDE) using optional
-includeideswitch - Defaults to completely automated operation (if appropriate permissions are available)
- Automatically looks up latest version via git tags
- Automatic selection of appropriate install sub-script
- Configures software installs for repositories when repositories are in place, otherwise pulls files from git releases. As repository versions are made available, script will be updated to take advantage.
- User permission checking
- Sub-installers called from local file system if they exist, otherwise pulled from git
- Sub-installers can be called directly if auto-selection is not needed
Minimum Requirements for install-powershell.sh
- bash shell
sed- native package manager available
curl(auto-installed if missing)tr
Parameters
- -includeide - installs VSCode and VSCode PowerShell extension (only relevant to machines with a desktop environment)
- -interactivetesting - do a quick launch test of VSCode (only relevant when used with -includeide)
- -skip-sudo-check - use sudo without verifying its availability (hard to accurately do on some distros)
- -preview - installs the latest preview release of PowerShell core side-by-side with any existing production releases
Usage
Direct from Github
bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) <ARGUMENTS>
wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh | bash -s <ARGUMENTS>Local Copy
bash install-powershell.sh <ARGUMENTS>Examples
Only Install PowerShell Core
bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh)Install PowerShell Core with IDE
bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) -includeideInstall PowerShell Core with IDE and do tests that require a human to interact with the installation process
bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) -includeide -interactivetestingInstallation To do list
- Detect and wait when package manager is busy/locked? - at least Ubuntu (CentOS does this internally)
Overview (`docs/debugging/README.md`)
Visual Studio Code
The Visual Studio Code editor supports cross-platform debugging.
This is made possible by the OmniSharp extension for VS Code.
Please review their detailed instructions. In
addition to being able to build PowerShell, you need:
- C# Extension for VS Code installed
- .NET Core debugger installed (semi-automatic)
powershellexecutable in your path (self-host if not on Windows)
The .NET CLI tools must be on your path for Visual Studio Code.Start-PSBootstrap installs the tools to ~/.dotnet (non-Windows) or "$env:LocalAppData\Microsoft\dotnet" (Windows),
but does not add this to your PATH.
You can do this in Bash with export PATH=$PATH:$HOME/.dotnet or in PowerShell with $env:path = $env:path+";$env:LocalAppData\Microsoft\dotnet".
Once the extension is installed, you have to open a C# file to force VS Code to
install the actual .NET Core debugger (the editor will tell you to do this if
you attempt to debug and haven't already opened a C# file).
The committed .vscode folder in the root of this repository contains
the launch.json and tasks.json files which provide Core PowerShell
debugging configurations and a build task.
The "build" task will run Start-PSBuild, emitting the executable toPowerShell/debug/powershell so that the debugger always knows where to find it
(regardless of platform). If you edit this, please do not commit it, as the
default is meant to "just work" for anyone.
The ".NET Core Launch" configuration will build and start a powershell
process, with justMyCode disabled, and stopAtEntry enabled, thus PowerShell
will stop right at Main, and you need to click the green arrow to continue.
With either Gnome Terminal or XTerm installed, the launch configuration will
launch an external console with PowerShell running interactively. If neither of
these installed, the editor will tell you to do so.
Alternatively, the ".NET Core Attach" configuration will start listening for a
process named powershell, and will attach to it. If you need more fine-grained
control, replace processName with processId and provide a PID. (Please be
careful not to commit such a change.)
PowerShell
The Trace-Command cmdlet can be used to enable tracing of certain PowerShell
subsystems. Use Get-TraceSource for a list of tracers:
- CmdletProviderClasses
- CommandDiscovery
- CommandSearch
- ConsoleHost
- ConsoleHostRunspaceInit
- ConsoleHostUserInterface
- ConsoleLineOutput
- DisplayDataQuery
- ETS
- FileSystemProvider
- FormatFileLoading
- FormatViewBinding
- LocationGlobber
- MemberResolution
- Modules
- MshSnapinLoadUnload
- ParameterBinderBase
- ParameterBinderController
- ParameterBinding
- PathResolution
- PSDriveInfo
- PSSnapInLoadUnload
- RunspaceInit
- SessionState
- TypeConversion
- TypeMatch
Then trace it like this:
Trace-Command -Expression { Get-ChildItem . } -Name PathResolution -PSHostThe -PSHost specifies the sink, in this case the console host,
so we can see the tracing messages.
The -Name chooses the list of tracers to enable.
LLDB with SOS plug-in
The ./tools/debug.sh script can be used to launch PowerShell inside of LLDB
with the SOS plug-in provided by .NET Core. This provides an additional way to
debug PowerShell on Linux, but VS Code is recommended for a better user
experience (and its single-stepping capabilities).
The script is self-documented and contains a link to the
CoreCLR debugging help .
`corehost`
The native executable produced by .NET CLI will produce trace output
if launched with COREHOST_TRACE=1 ./powershell.
CoreCLR PAL
The native code in the CLR has debug channels to selectively output
information to the console. These are controlled by thePAL_DBG_CHANNELS, e.g., export PAL_DBG_CHANNELS="+all.all", as
detailed in the dbgmsg.h header.
Enabling +all.all is incredibly noisy;
you will need to narrow your scope.
Debugging .NET Core
The .NET Core libraries downloaded from NuGet and shipped with PowerShell are release versions.
This means that PAL_DBG_CHANNELS will not work with them,
and instead you must build and deploy .NET Core built in debug mode.
These instructions are not meant to be comprehensive,
but should prove useful.
They are currently written for Linux and are meant only as a shortcut means to debug.
Build and deploy CoreCLR
- Clone CoreCLR:
git clone -b release/1.0.0 https://github.com/dotnet/coreclr.git - Follow building instructions
- Wait for
./build.shto finish - Overwrite PowerShell libraries:
cp bin/Product/Linux.x64.Debug/*{so,dll} /path/to/powershell/
Build and deploy CoreFX
- Clone CoreFX:
git clone -b release/1.0.0 https://github.com/dotnet/corefx.git - Follow building instructions
- Wait for
./build.sh skipteststo finish - Overwrite PowerShell libraries:
This must be done in a particular order to get the most specific build,
and each phase must be allowed to overwrite both the previous phase
and any files previously found (hence the use of-exec cp).
The glob cannot go more than one directory deep,
as subdirectories can have alternative and unwanted implementations
of libraries with the same name.
dest=/path/to/powershell/
find bin/AnyOS.AnyCPU.Debug/*/*.dll -exec cp -p {} $dest \;
find bin/Unix.AnyCPU.Debug/*/*.dll -exec cp -p {} $dest \;
find bin/Linux.AnyCPU.Debug/*/*.dll -exec cp -p {} $dest \;
find bin/Linux.x64.Debug/ -name *.so -exec cp -p {} $dest \;Overview (`docs/git/README.md`)
Working with PowerShell repository
Get the code for the first time
git clone https://github.com/PowerShell/PowerShell.git --branch=masterBranches
- Don't commit your changes directly to master.
It will make the collaborative workflow messy. - Checkout a new local branch from
masterfor every change you want to make (bugfix, feature). - Use lowercase-with-dashes for naming.
- Follow Linus' recommendations about history.
- "People can (and probably should) rebase their private trees (their own work). That's a cleanup. But never other peoples code. That's a 'destroy history'...
You must never EVER destroy other peoples history. You must not rebase commits other people did.
Basically, if it doesn't have your sign-off on it, it's off limits: you can't rebase it, because it's not yours."
- "People can (and probably should) rebase their private trees (their own work). That's a cleanup. But never other peoples code. That's a 'destroy history'...
Understand branches
- master is the branch with the latest and greatest changes.
It could be unstable. - Send your pull requests to master.
Sync your local repository
Use git rebase instead of git merge and git pull, when you're updating your feature-branch.
# fetch updates all remote branch references in the repository
# --all : tells it to do it for all remotes (handy, when you use your fork)
# -p : tells it to remove obsolete remote branch references (when they are removed from remote)
git fetch --all -p
# rebase on origin/master will rewrite your branch history
git rebase origin/masterMore complex scenarios
Covering all possible git scenarios is behind the scope of the current document.
Git has excellent documentation and lots of materials available online.
We are leaving a few links here:
Tags
If you are looking for the source code for a particular release,
you will find it via tags.
git tagwill show you list of all tags.- Find the tag that corresponds to the release.
- Use
git checkout <tag-name>to get this version.
Note: checking out a tag will move the repository to a DETACHED HEAD state.
If you want to make changes, based on tag's version (i.e. a hotfix),
checkout a new branch from this DETACHED HEAD state.
git checkout -b vors/hotfixRecommended Git configurations
We highly recommend these configurations to help deal with whitespace,
rebasing, and general use of Git.
Auto-corrects your command when it's sure (
statstostatus)
git config --global help.autoCorrect -1Refuses to merge when pulling, and only pushes to branch with same name.
git config --global pull.ff only
git config --global push.default currentShows shorter commit hashes and always shows reference names in the log.
git config --global log.abbrevCommit true
git config --global log.decorate shortIgnores whitespace changes and uses more information when merging.
git config --global apply.ignoreWhitespace change
git config --global rerere.enabled true
git config --global rerere.autoUpdate true
git config --global am.threeWay trueOverview (`docs/host-powershell/README.md`)
Host PowerShell Core in .NET Core Applications
PowerShell Core v6.0.1 and Later
The runtime assemblies for Windows, Linux and OSX are now published in NuGet package version 6.*.
Please see the .NET Core Sample Application section for an example that uses PowerShell Core NuGet packages.
.NET Core Sample Application
Note: The .NET Core 2.1 runtime and .NET Core SDK 2.1 or higher is required for the examples below:
You can find the sample application project MyApp in each of the above 2 sample folders. You can quickly test-run it using dotnet run.
To build the sample project properly for distribution, run the following command (make sure the required .NET Core SDK is in use):
dotnet publish .\MyApp --configuration releaseThis builds it for the runtimes specified by the RuntimeIdentifiers property in the .csproj file.
Then you can run the MyApp binary from the publish folder and see the results:
PS:> .\MyApp.exe
Evaluating 'Get-Command Write-Output' in PS Core Runspace
Write-Output
Evaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace
System.Management.Automation.ActionPreference
System.Management.Automation.AliasAttributeSpecial Hosting Scenario For Native Host
There is a special hosting scenario for native hosts,
where Trusted Platform Assemblies (TPA) do not include PowerShell assemblies,
such as the in-box powershell.exe in Nano Server and the Azure DSC host.
For such hosting scenarios, the native host needs to bootstrap by calling PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext.
When using this API, the native host can pass in the path to the directory that contains PowerShell assemblies.
A handler will then be registered to the Resolving
event of the default load context to deal with the loading of assemblies from that directory.
Overview (`docs/maintainers/README.md`)
Repository Maintainers
Repository Maintainers are trusted stewards of the PowerShell repository responsible for maintaining consistency and quality of PowerShell code.
One of their primary responsibilities is merging pull requests after all requirements have been fulfilled.
They have write access to the PowerShell repositories which gives them the power to:
git pushto the official PowerShell repository- Merge pull requests
- Assign labels, milestones, and people to issues and pull requests
Table of Contents
- Current Repository Maintainers
- Repository Maintainer Responsibilities
- Issue Management Process
- Pull Request Workflow
- Becoming a Repository Maintainer
Current Repository Maintainers
- Aditya Patwardhan (adityapatwardhan)
- Andrew Menagarishvili (anmenaga)
- Dongbo Wang (daxian-dbw)
- Ilya Sazonov (iSazonov)
- Robert Holt (rjmholt)
- Travis Plunk (TravisEz13)
Former Repository Maintainers
- Andy Jordan (andyleejordan)
- Jason Shirk (lzybkr)
- Mike Richmond (mirichmo)
- Sergei Vorobev (vors)
Repository Maintainer Responsibilities
Repository Maintainers enable rapid contributions while maintaining a high level of quality in PowerShell by ensuring that all development processes are being followed correctly.
If you are a Repository Maintainer, you:
- MUST abide by the Code of Conduct and report suspected violations to the PowerShell Committee
- MUST ensure that each contributor has signed a valid Microsoft Contributor License Agreement (CLA)
- MUST verify compliance with any third party code license terms (e.g., requiring attribution, etc.) if the contribution contains third party code.
- MUST make sure that any change requiring approval from the PowerShell Committee has gone through the proper RFC or approval process
- MUST validate that code reviews have been conducted before merging a pull request when no code is written
- MUST validate that tests and documentation have been written before merging a pull request that contains new functionality
- SHOULD add the correct labels to issues and pull requests
- SHOULD make sure the correct Area Experts are assigned to relevant pull requests and issues.
This includes adding extra reviewers when it makes sense
(e.g. a pull request that adds remoting capabilities might require a security expert) - SHOULD validate that the names and email addresses in the git commits reasonably match identity of the person submitting the pull request
- SHOULD make sure contributors are following the contributor guidelines
- SHOULD ask people to resend a pull request, if it doesn't target
master - SHOULD wait for the CI system build to pass for pull requests
(unless, for instance, the pull request is being submitted to fix broken CI) - SHOULD encourage contributors to refer to issues in their pull request description (e.g.
Resolves issue #123).
If a user did not create an issue prior to submitting their pull request, their pull request should not be rejected.
However, they should be reminded to create an issue in the future to frontload any potential problems with the work and to minimize duplication of efforts. - SHOULD encourage contributors to create meaningful titles for all PRs.
Edit the title if necessary to provide clarity on the problem - SHOULD encourage contributors to write meaningful, descriptive git commits
- SHOULD NOT merge pull requests with a failed CI build
(unless, for instance, the pull request is being submitted to fix broken CI) - SHOULD NOT merge pull requests without the status check passing from the Microsoft CLA bot
(unless the CLA bot is broken, and CLA signing can be confirmed through other means) - SHOULD NOT merge pull requests too quickly after they're submitted.
Even if the pull request meets all the requirements, people should have time to give their input
(unless the pull request is particularly urgent for some reason) - SHOULD NOT merge your own pull requests.
If a Repository Maintainer opens a pull request, another Maintainer should merge it unless there are extreme, short-term circumstances requiring a merge or another Maintainer has given explicit sign-off without merging
Issue Management Process
Please see Issue Management
Pull Request Workflow
Please see Contributing
Maintainer Best Practices
Please see Best Practices
Becoming a Repository Maintainer
Repository Maintainers currently consist mostly of Microsoft employees.
It is expected that over time, regular trusted contributors to the PowerShell repository will be made Repository Maintainers.
Eligibility is heavily dependent on the level of contribution and expertise: individuals who contribute consistently in meaningful ways to the project will be recognized accordingly.
At any point in time, the existing Repository Maintainers can unanimously nominate a strong community member to become a Repository Maintainer.
Nominations are brought to the PowerShell Committee to understand the reasons and justification.
A simple majority of the PowerShell Committee is required to veto the nomination.
When a nominee has been approved, a PR will be submitted by a current Maintainer to update this document to add the nominee's name to
the Current Repository Maintainers with justification as the description of the PR to serve as the public announcement.
Overview (`src/Microsoft.PowerShell.SDK/README.md`)
Microsoft PowerShell SDK
This is a meta-project that serves two purposes:
- Group PowerShell sub projects to avoid unnecessary duplication of reference declarations.
- Group extra .NET Core packages that PowerShell doesn't depend on directly but must provide for our users at runtime.
Overview (`src/Modules/README.md`)
Modules
There are 3 directories with content files.
Content files includes:
- ps1xml
- psm1
- psd1
- ps1
These files are copied as-is by dotnet
- Shared (shared between Windows and Unix)
- Windows
- Unix
Notes
We have files with the same names in different folders.
That means that the contents of these two files are different.
I.e. if it's .psd1 file, it could be because CmdletsToExport
are different for different runtimes (platforms) or frameworks.
Overview (`src/PowerShell.Core.Instrumentation/README.md`)
PowerShell.Core.Instrumentation
The PowerShell.Core.Instrumentation.man has actually been migrated to PowerShell-Native repository.
The corresponding manifest resource DLL PowerShell.Core.Instrumentation.dll is now produced in the release build of PowerShell-Native and is shipped in the Microsoft.PowerShell.Native NuGet package.
However, we still need to keep PowerShell.Core.Instrumentation.man in the PowerShell repository because the PowerShell packages for Windows ship the manifest file and RegisterManifest.ps1 for users to register the manifest resource DLL if they want to.
Therefore, when making changes to PowerShell.Core.Instrumentation.man, make sure your changes are made to both repositories -- this file needs to be kept in sync between those 2 repositories.
Overview (`src/TypeCatalogGen/README.md`)
TypeCatalogGen
./build.sh runs the Core PowerShell TypeCatalogGen.exe under Mono
on Linux (using dotnet-cli). The Runtime folder is a facade
project that, when published, produces a list of the currently used
runtime assemblies for .NET Core. The output isCorePsTypeCatalog.cs, which contains an "initialized" dictionary
that we copy into typecatalog.cs of System.Management.Automation
Overview (`src/libpsl-native/README.md`)
libpsl-native
The code under libpsl-native is being migrated to the PowerShell-native repository.
Please make PRs to the new repository.
Code under here will be removed once the move is complete.
Overview (`src/powershell-native/README.md`)
powershell-native
The code under powershell-native is being migrated to PowerShell-native repository.
Please make PRs to the new repository.
Code under here will be removed once the move is complete.
Overview (`src/powershell/README.md`)
PowerShell
The powershell[.exe] executable for PowerShell is built by
powershell-unix and powershell-windows projects,
as they are the top dependencies of the graph, and has emitEntryPoint: true,
meaning a native executable is produced automatically by CLI (no need to own a
separate native host).
This project is a very simple shim that provides a Main function for .NET CLI
to produce an app. It initializes PowerShell's custom AssemblyLoadContext and
then delegates to the same Start function inMicrosoft.PowerShell.ConsoleHost that the original native PowerShell host
executes; thus we share the same entry point and the same PowerShell host, but
use a different native host. This lets us take full advantage of .NET CLI's
native host.
We use this shim so that the ConsoleHost project and the original native host
do not have to be changed. Additionally, until .NET CLI bugs surrounding content
file deployment are solved, this shim allows us to continue with our splitModules folders work-around to deploy the correct versions.
Overview (`test/powershell/README.md`)
Pester Testing Test Guide
Also see the Writing Pester Tests
document.
Running Pester Tests
Go to the top level of the PowerShell repository and run: Start-PSPester
inside a self-hosted copy of PowerShell.
You can use Start-PSPester -Tests SomeTestSuite* to limit the tests run.
Testing new `powershell` processes
Any launch of a new powershell process must include -noprofile so that
modified user and system profiles do not causes tests to fail. You also must
take care to call the development copy of PowerShell, which is not the first
one on the path.
Example:
$powershell = Join-Path -Path $PsHome -ChildPath "pwsh"
& $powershell -noprofile -command "ExampleCommand" | Should Be "ExampleOutput"Portability
Some tests simply must be tied to certain platforms. Use Pester's-Skip directive on an It statement to do this. For instance to run
the test only on Windows:
It "Should do something on Windows" -Skip:($IsLinux -Or $IsMacOS) { ... }Or only on Linux and OS X:
It "Should do something on Linux" -Skip:$IsWindows { ... }Pending
When writing a test that should pass, but does not, please do not skip or delete
the test, but use It "Should Pass" -Pending to mark the test as pending, and
file an issue on GitHub.
Overview (`test/xUnit/README.md`)
xUnit Tests
The folder contains xUnit tests for PowerShell Core project.
Running xUnit Tests
Go to the top level of the PowerShell repository and run full set of tests:Start-PSxUnit inside a self-hosted copy of PowerShell.
Go to the test project folder and run dotnet test -c Release.
Use filter parameter to run only needed tests:
dotnet test -c Release --filter "FullyQualifiedName~UnitTest1 # Runs tests which have UnitTest1 in FullyQualifiedName
dotnet test --filter Name~TestMethod1 # Runs tests whose name contains TestMethod1Creating xUnit Tests
Keep the folder structure that is for Pester ../../test/powershell and C# files ../../src.
Use namespace names started with PSTests.
namespace PSTests.YourNameSpace
{
}Overview (`tools/AttackSurfaceAnalyzer/README.md`)
Attack Surface Analyzer Testing
This directory contains tools for running Attack Surface Analyzer (ASA) tests on PowerShell MSI installations using Docker.
Overview
Attack Surface Analyzer is a Microsoft tool that helps analyze changes to a system's attack surface. These scripts allow you to run ASA tests locally in a clean Windows container to analyze what changes when PowerShell is installed.
Files
- Run-AttackSurfaceAnalyzer.ps1 - PowerShell script to run ASA tests with official MSIs
- Summarize-AsaResults.ps1 - PowerShell script to analyze and summarize ASA results
- docker/Dockerfile - Multi-stage Dockerfile for building a container image with ASA pre-installed
- README.md - This documentation file
Docker Architecture
The Docker implementation uses a multi-stage build to optimize the testing and result extraction process:
Multi-Stage Build Stages
asa-runner: Main execution environment
- Base:
mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022 - Contains Attack Surface Analyzer CLI tools
- Runs the complete test workflow
- Generates reports in both
C:\workandC:\reportsdirectories
- Base:
asa-reports: Minimal results layer
- Base:
mcr.microsoft.com/windows/nanoserver:ltsc2022 - Contains only the test reports from the runner stage
- Enables clean extraction of results without container internals
- Base:
final: Default stage (inherits from asa-runner)
- Provides backward compatibility
- Used when no specific build target is specified
Benefits
- Clean Result Extraction: Reports are isolated in a dedicated layer
- Efficient Transfer: Only test results are copied, not the entire container filesystem
- Fallback Support: Script includes fallback to volume-based extraction if needed
- Minimal Footprint: Final results layer contains only the necessary output files
Prerequisites
- Windows 10/11 or Windows Server
- Docker Desktop with Windows containers enabled
- PowerShell 5.1 or later
- An official signed PowerShell MSI file from a released build
MSI Requirements
Important: This tool now requires an official, digitally signed PowerShell MSI from Microsoft releases:
- Must be signed by Microsoft Corporation
- Must be from an official release (downloaded from PowerShell Releases)
- Local builds are not supported - unsigned or development MSIs will be rejected
- The script automatically verifies the digital signature before proceeding
Where to get official MSIs:
- Download from: https://github.com/PowerShell/PowerShell/releases
- Look for files like:
PowerShell-7.x.x-win-x64.msi
Quick Start
Option 1: Using the PowerShell Script (Recommended)
The script requires an official signed PowerShell MSI file:
# Run ASA test with official MSI (MsiPath is required)
.\tools\AttackSurfaceAnalyzer\Run-AttackSurfaceAnalyzer.ps1 -MsiPath "C:\path\to\PowerShell-7.4.0-win-x64.msi"
# Specify custom output directory for results
.\tools\AttackSurfaceAnalyzer\Run-AttackSurfaceAnalyzer.ps1 -MsiPath ".\PowerShell-7.4.0-win-x64.msi" -OutputPath "C:\asa-results"
# Keep the temporary work directory for debugging
.\tools\AttackSurfaceAnalyzer\Run-AttackSurfaceAnalyzer.ps1 -MsiPath ".\PowerShell-7.4.0-win-x64.msi" -KeepWorkDirectoryThe script will:
- Verify MSI signature - Ensures the MSI is officially signed by Microsoft Corporation
- Create a temporary work directory
- Build a custom Docker container from the static Dockerfile
- Start the Windows container with Attack Surface Analyzer
- Take a baseline snapshot
- Install the PowerShell MSI
- Take a post-installation snapshot
- Export comparison results
- Copy results back to your specified output directory
Security Note: The script will reject any MSI that is not digitally signed by Microsoft Corporation to ensure analysis is performed only on official releases.
Option 2: Using the Dockerfile
If you prefer to build and use the container image directly:
# Build the Docker image (Dockerfile is in docker subfolder with clean context)
docker build -f tools\AttackSurfaceAnalyzer\docker\Dockerfile -t powershell-asa-test tools\AttackSurfaceAnalyzer\docker\
# Run the container with your MSI (script is built into the container)
docker run --rm --isolation process `
-v "C:\path\to\msi\directory:C:\work" `
powershell-asa-testOutput Files
The test will generate output files in the ./asa-results/ directory (or your specified -OutputPath):
asa.sqlite- SQLite database with full analysis data (primary result file)install.log- MSI installation log file*_summary.json.txt- Summary of detected changes (if generated)*_results.json.txt- Detailed results in JSON format (if generated)*.sarif- SARIF format results (if generated, can be viewed in VS Code)
Analyzing Results
Using the Summary Script (Recommended)
Use the included summary script to get a comprehensive analysis:
# Basic summary of ASA results
.\tools\AttackSurfaceAnalyzer\Summarize-AsaResults.ps1
# Detailed analysis with rule breakdowns
.\tools\AttackSurfaceAnalyzer\Summarize-AsaResults.ps1 -ShowDetails
# Analyze results from a specific location
.\tools\AttackSurfaceAnalyzer\Summarize-AsaResults.ps1 -Path "C:\custom\path\asa-results.json" -ShowDetailsThe summary script provides:
- Overall statistics - Total findings, analysis levels, category breakdowns
- Rule analysis - Which security rules were triggered and how often
- File analysis - Detailed breakdown of file-related security issues by rule type
- Category cross-reference - Shows which rules affect which categories
Using VS Code
The SARIF files can be opened directly in VS Code with the SARIF Viewer extension to see a formatted view of the findings.
Using PowerShell
# Read the JSON results directly
$results = Get-Content "asa-results\asa-results.json" | ConvertFrom-Json
$results.Results.FILE_CREATED.Count # Number of files created
# Query the SQLite database (requires SQLite tools)
# Example: List all file changes
# sqlite3 asa.sqlite "SELECT * FROM file_system WHERE change_type != 'NONE'"Troubleshooting
Docker Not Available
The script automatically handles Docker Desktop installation and startup:
If Docker Desktop is installed but not running:
- The script will automatically start Docker Desktop for you
- It waits up to 60 seconds for Docker to become available
- You'll be prompted for confirmation (supports
-Confirmand-WhatIf)
If Docker Desktop is not installed:
- The script will prompt you to install it automatically using winget
- After installation completes, start Docker Desktop and run the script again
Manual Installation:
- Install Docker Desktop from https://www.docker.com/products/docker-desktop
- Ensure Docker is running
- Switch to Windows containers (right-click Docker tray icon → "Switch to Windows containers")
Container Fails to Start
- Ensure you have enough disk space (containers can be large)
- Check that Windows containers are enabled in Docker settings
- Try pulling the base image manually:
docker pull mcr.microsoft.com/dotnet/sdk:9.0-windowsservercore-ltsc2022
MSI Signature Verification Fails
If you get signature verification errors:
- Ensure you're using an official MSI from PowerShell Releases
- Do not use local builds - only signed release MSIs are supported
- Check certificate validity - very old MSIs may have expired certificates
- Verify file integrity - redownload the MSI if it may be corrupted
No Results Generated
- Check the install.log file for MSI installation errors
- Run with
-KeepWorkDirectoryto inspect the temporary work directory - Verify the MSI file is valid and not corrupted
Advanced Usage
Parameters
The Run-AttackSurfaceAnalyzer.ps1 script supports these parameters:
-MsiPath(Required) - Path to the official signed PowerShell MSI file-OutputPath(Optional) - Directory for results (defaults to./asa-results)-ContainerImage(Optional) - Custom container base image-KeepWorkDirectory(Optional) - Keep temp directory for debugging
Example with custom container image:
.\tools\AttackSurfaceAnalyzer\Run-AttackSurfaceAnalyzer.ps1 `
-MsiPath ".\PowerShell-7.4.0-win-x64.msi" `
-ContainerImage "mcr.microsoft.com/dotnet/sdk:8.0-windowsservercore-ltsc2022"Debugging
To debug issues, keep the work directory and examine the files:
.\tools\AttackSurfaceAnalyzer\Run-AttackSurfaceAnalyzer.ps1 -KeepWorkDirectory
# The script will print the work directory path
# You can then examine:
# - run-asa.ps1 - The script that runs in the container
# - install.log - MSI installation log
# - Any other generated filesIntegration with CI/CD
These tools were extracted from the GitHub Actions workflow to allow local testing. If you need to integrate ASA testing back into a CI/CD pipeline, you can:
- Use the PowerShell script directly in your pipeline
- Build and push the Docker image to a registry
- Use the Dockerfile as a base for custom testing scenarios
More Information
Overview (`tools/clearlyDefined/readme.md`)
ClearlyDefined
Purpose
This tool is intended to test if all the license data in ClearlyDefined is present to generate the PowerShell license.
If the data is not present, it can request that ClearlyDefined gather (called Harvest in their terminology) the data.
Use
Testing
Run ./ClearlyDefined.ps1 -test.
If there is any missing data, the script should write verbose messages about the missing data and throw.
If there is no missing data, the script should not throw.
Harvesting
Run ./ClearlyDefined.ps1 -Harvest.
The script will trigger the harvest and output the result from ClearlyDefined.
Give ClearlyDefined 24 hours to harvest the data.
You can use the -Test switch without the -Harvest switch to test if Harvesting is done.
Caching
If you run in the same PowerShell session, the script will be faster due to caching.
The module will cache any results from ClearlyDefined that indicate the package is Harvested for 60 minutes.
No caching is done for packages that are not yet harvested.
To clear the cache, run with the -ForceModuleReload switch.
Overview (`tools/performance/README.md`)
PowerShell Performance Analysis
This directory contains useful scripts and related files for analyzing PowerShell performance.
If you use the Windows Performance Toolkit, you can use the following to collect data and analyze a trace.
$PowerShellGitRepo = "D:\PowerShell"
wpr -start $PowerShellGitRepo\tools\performance\PowerShell.wprp -filemode
pwsh.exe -NoProfile -Command "echo 1"
wpr -stop PowerShellTrace.etl
wpa -i wpa://.\PowerShellTrace.etl?profile=$PowerShellGitRepo\tools\performance\PowerShell.wpaProfileWhen wpa opens, under System Activity, you'll find a section "Regions of Interest".
With the above wpaProfile, you should see a bunch of PowerShell related regions as well as GC and JIT activity.
If you use PerfView, you can collect a trace by running
Invoke-PerfviewPS.ps1 -scenario { echo 1 }
perfview .\perfviewdata.etlThe etl files collected with perfview or wpr should contain roughly the same events.
Also note that you can collect the trace with one tool and analyze with the other.
Symbols
PDB files are not published for PowerShell Core,
so the current recommendation is to build PowerShell yourself passing -CrossGen to Start-Build.
If profiling Windows PowerShell, symbols are generated from GAC.
wprui.exe and perfview.exe will both generate the PDB files needed.
Files
| File | Description |
|---|---|
| GC.Regions.xml | WPA regions of interest for GC |
| JIT.Regions.xml | WPA regions of interest for JIT |
| PowerShell.Regions.xml | WPA regions of interest for PowerShell |
| PowerShell.stacktags | PowerShell stack tags |
| PowerShell.wpaProfile | WPA profile to load regions of interest and stack tags |
| PowerShell.wprp | WPR profile to enable CLR and PowerShell ETW events |
| Invoke-PerfviewPS.ps1 | Script to run perfview and with PowerShell ETW events enabled |
Overview (`test/perf/benchmarks/README.md`)
Micro Benchmarks
This folder contains micro benchmarks that test the performance of PowerShell Engine.
Requirement
- A good suite of benchmarks
Something that measures only the thing that we are interested in and produces accurate, stable and repeatable results. - A set of machine with the same configurations.
- Automation for regression detection.
Design Decision
- This project is internal visible to
System.Management.Automation.
We want to be able to target some internal APIs to get measurements on specific scoped scenarios,
such as measuring the time to compile AST to a delegate by the compiler. - This project makes
ProjectReferenceto other PowerShell assemblies.
This makes it easy to run benchmarks with the changes made in the codebase.
To run benchmarks with a specific version of PowerShell,
just replace theProjectReferencewith aPackageReferenceto theMicrosoft.PowerShell.SDKNuGet package of the corresponding version.
Quick Start
You can run the benchmarks directly using dotnet run in this directory:
To run the benchmarks in Interactive Mode, where you will be asked which benchmark(s) to run:
textdotnet run -c Release -f net6.0To list all available benchmarks (read more):
textdotnet run -c Release -f net6.0 --list [flat/tree]To filter the benchmarks using a glob pattern applied to
namespace.typeName.methodName(read more]):textdotnet run -c Release -f net6.0 --filter *script* --list flatTo profile the benchmarked code and produce an ETW Trace file (read more)
textdotnet run -c Release -f net6.0 --filter *script* --profiler ETW
You can also use the function Start-Benchmarking from the module perf.psm1 to run the benchmarks:
Start-Benchmarking [-TargetFramework <string>] [-List <string>] [-Filter <string[]>] [-Artifacts <string>] [-KeepFiles] [<CommonParameters>]
Start-Benchmarking [-TargetPSVersion <string>] [-Filter <string[]>] [-Artifacts <string>] [-KeepFiles] [<CommonParameters>]
Start-Benchmarking -Runtime <string[]> [-Filter <string[]>] [-Artifacts <string>] [-KeepFiles] [<CommonParameters>]Run Get-Help Start-Benchmarking -Full to see the description of each parameter.
Regression Detection
We use the tool ResultsComparer to compare the provided benchmark results.
See the README.md for ResultsComparer for more details.
The module perf.psm1 also provides Compare-BenchmarkResult that wraps ResultsComparer.
Here is an example of using it:
/* Detailed source-code truncated for AI context efficiency. */References
- Getting started with BenchmarkDotNet
- Micro-benchmark Design Guidelines
- Adam SITNIK: Powerful benchmarking in .NET
Overview (`test/perf/dotnet-tools/README.md`)
Tools
The tools here are copied from dotnet/performance,
the performance testing repository for the .NET runtime and framework libraries.
- BenchmarkDotNet.Extensions
- It provides the needed extensions for running benchmarks,
such as theRecommendedConfigwhich defines the set of recommended configurations for running the dotnet benchmarks.
- It provides the needed extensions for running benchmarks,
- Reporting
- It provides additional result reporting support
which may be useful to us when running our benchmarks in lab.
- It provides additional result reporting support
- ResultsComparer
- It's a tool for comparing different benchmark results.
It's very useful to show the regression of new changes by comparing its benchmark results to the baseline results.
- It's a tool for comparing different benchmark results.
Overview (`test/tools/CodeCoverageAutomation/README.md`)
Code Coverage Automation
The script Start-CodeCoverageRun.ps1 automates the execution of tests and uploading the results to Coveralls.io.
The script is self contained and can be executed on any Windows system which supports Powershell v5.0 or above. It has not been tested on Powershell v6.0.
Execution
The virtual machine hosting the script has a scheduled task to start the tests at 5 am PDT.
The script follows the steps below:
- Download the code coverage binaries package from Azure DevOps Windows nightly builds artifacts (CodeCoverage.zip).
- Download the OpenCover powershell module from Azure DevOps Windows nightly builds artifacts. (OpenCover.zip)
- Download the tests from Azure DevOps Windows nightly builds artifacts (tests.zip)
- Download Coveralls.net from here
- Invoke 'Install-OpenCover' to install OpenCover toolset.
- Invoke 'Invoke-OpenCover' to execute tests.
- Invoke powershell to get the git commit ID of the downloaded daily build package.
- Using the commit ID get committer info like, message, author and email using the github REST API.
- Invoke 'csmacnz.Coveralls.exe' to upload the coverage results to Coveralls.net
The uploaded coverage data can be viewed at: https://coveralls.io/github/PowerShell/PowerShell
Overview (`test/tools/WebListener/README.md`)
WebListener App
ASP.NET Core app for testing HTTP and HTTPS Requests.
Run with `dotnet`
dotnet restore
dotnet publish --output bin --configuration Release
cd bin
dotnet WebListener.dll ServerCert.pfx password 8083 8084 8085 8086NOTE: ServerCert.pfx is no longer a static asset
and you will need to create your own certificate for this purpose.
The SelfSignedCertificate module in the PowerShell Gallery provides this functionality.
The test site can then be accessed via http://localhost:8083/, https://localhost:8084/, https://localhost:8085/, or https://localhost:8086/.
The WebListener.dll takes 6 arguments:
- The path to the Server Certificate
- The Server Certificate Password
- The TCP Port to bind on for HTTP
- The TCP Port to bind on for HTTPS using TLS 1.2
- The TCP Port to bind on for HTTPS using TLS 1.1
- The TCP Port to bind on for HTTPS using TLS 1.0
Run With WebListener Module
Import-Module .\build.psm1
Publish-PSTestTools
$Listener = Start-WebListener -HttpPort 8083 -HttpsPort 8084 -Tls11Port 8085 -TlsPort 8086Tests
/ or /Home/
Returns a static HTML page containing links and descriptions of the available tests in WebListener. This can be used as a default or general test where no specific test functionality or return data is required.
/Auth/Basic/
Provides a mock Basic authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned.
$credential = Get-Credential
$uri = Get-WebListenerUrl -Test 'Auth' -TestValue 'Basic' -Https
Invoke-RestMethod -Uri $uri -Credential $credential -SkipCertificateCheck{
"headers":{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Authorization": "Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk",
"Host": "localhost:8084"
},
"origin": "127.0.0.1",
"args": {},
"url": "https://localhost:8084/Auth/Basic"
}/Auth/Negotiate/
Provides a mock Negotiate authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned.
$uri = Get-WebListenerUrl -Test 'Auth' -TestValue 'Negotiate' -Https
Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck{
"headers":{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Authorization": "Negotiate jjaguasgtisi7tiqkagasjjajvs",
"Host": "localhost:8084"
},
"origin": "127.0.0.1",
"args": {},
"url": "https://localhost:8084/Auth/Negotiate"
}/Auth/NTLM/
Provides a mock NTLM authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned.
$uri = Get-WebListenerUrl -Test 'Auth' -TestValue 'NTLM' -Https
Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck{
"headers":{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Authorization": "NTLM jjaguasgtisi7tiqkagasjjajvs",
"Host": "localhost:8084"
},
"origin": "127.0.0.1",
"args": {},
"url": "https://localhost:8084/Auth/NTLM"
}/Cert/
Returns a JSON object containing the details of the Client Certificate if one is provided in the request.
$certificate = Get-WebListenerClientCertificate
$uri = Get-WebListenerUrl -Test 'Cert' -Https
Invoke-RestMethod -Uri $uri -Certificate $certificateResponse when certificate is provided in request:
{
"Status": "OK",
"IssuerName": "[email protected], CN=adatum.com, OU=R&D, O=A. Datum Corporation, L=Redmond, S=Washington, C=US",
"SubjectName": "[email protected], CN=adatum.com, OU=R&D, O=A. Datum Corporation, L=Redmond, S=Washington, C=US",
"NotAfter": "2044-12-26T12:16:46-06:00",
"Issuer": "[email protected], CN=adatum.com, OU=R&D, O=A. Datum Corporation, L=Redmond, S=Washington, C=US",
"Subject": "[email protected], CN=adatum.com, OU=R&D, O=A. Datum Corporation, L=Redmond, S=Washington, C=US",
"NotBefore": "2017-08-10T13:16:46-05:00",
"Thumbprint": "2DECF1348FF21B780F45D316A039B5EB4C6312F7"
}Response when certificate is not provided in request:
{
"Status": "FAILED"
}/Compression/Brotli/
Returns the same results as the Get test with brotli compression.
$uri = Get-WebListenerUrl -Test 'Compression' -TestValue 'Brotli'
Invoke-RestMethod -Uri $uri{
"args": {},
"origin": "127.0.0.1",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) PowerShell/6.0.0",
"Host": "localhost:8083"
},
"url": "http://localhost:8083/Compression/Brotli"
}/Compression/Deflate/
Returns the same results as the Get test with deflate compression.
$uri = Get-WebListenerUrl -Test 'Compression' -TestValue 'Deflate'
Invoke-RestMethod -Uri $uri{
"args": {},
"origin": "127.0.0.1",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) PowerShell/6.0.0",
"Host": "localhost:8083"
},
"url": "http://localhost:8083/Compression/Deflate"
}/Compression/Gzip/
Returns the same results as the Get test with gzip compression.
$uri = Get-WebListenerUrl -Test 'Compression' -TestValue 'Gzip'
Invoke-RestMethod -Uri $uri{
"args": {},
"origin": "127.0.0.1",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) PowerShell/6.0.0",
"Host": "localhost:8083"
},
"url": "http://localhost:8083/Compression/Gzip"
}/Delay/
Returns the same results as the Get test. If a number is supplied, the server will wait that many seconds before returning a response. This can be used to test timeouts.
$uri = Get-WebListenerUrl -Test 'Delay' -TestValue '5'
Invoke-RestMethod -Uri $uriAfter 5 Seconds:
{
"args": {
},
"origin": "127.0.0.1",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.15063.608",
"Host": "localhost:8083"
},
"url": "http://localhost:8083/Delay/5"
}/Delete/
Returns the same results as the Get test. Will only accept the DELETE request method.
$uri = Get-WebListenerUrl -Test 'Delete'
$Body = @{id = 12345} | ConvertTo-Json -Compress
Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'{
"method": "DELETE",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083",
"Content-Length": "12"
},
"origin": "127.0.0.1",
"url": "http://localhost:8083/Delete",
"args": {},
"data": "{\"id\":12345}"
}/Dos/
Returns HTML designed to create denial of service against specific RegEx Expressions
Image Parsing RegEx
$uri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='img'
dosLength='5000'
}
Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'Return the following followed by 5,000 spaces.
<imgCharset Parsing RegEx
$uri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='charset'
dosLength='5000'
}
Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'Return the following followed by 5,000 spaces.
<meta/Encoding/Utf8/
Returns page containing UTF-8 data.
$uri = Get-WebListenerUrl -Test 'Encoding' -TestValue 'Utf8'
Invoke-RestMethod -Uri $uri/Get/
Returns a JSON object containing the Request URL, Request Headers, GET Query Fields and Values, and Origin IP. This emulates the functionality of HttpBin's get test.
$uri = Get-WebListenerUrl -Test 'Get'
Invoke-RestMethod -Uri $uri -Body @{TestField = 'TestValue'}{
"origin": "127.0.0.1",
"url": "http://localhost:8083/Get?TestField=TestValue",
"method": "GET",
"args": {
"TestField": "TestValue"
},
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083"
}
}/Link/
Returns Link response headers to test paginated results. The endpoint accepts 3 query items:
linknumber- The current link number. This determines the current page. If not supplied or less than 1, this will be set to 1.maxlinks- The maximum number of links. This determines the last page. If not supplied or less than 1, this will be set to 3.type- The type of link to return. When not supplied or not in the list below,defaultwill be used.default- Does not return any special test links and returnsnextlink if one is available.norel- Returns a Link header that does not include therel=portion. Suppressesnextlink.nourl- Returns a Link header that does not include the URI portion. Suppressesnextlink.malformed- Returns a malformed Link header. Suppressesnextlink.multiple- Returns multiple Link headers instead of a single Link header and returnsnextlink if one is available.nowhitespace- Returnsdefaultlinks without any whitespace between the semicolon andrelextrawhitespace- Returnsdefaultlinks with double whitespace between the semicolon andrel
The body will contain the same results as /Get/ with the addition of the type, linknumber, and maxlinks for the current page.
$Query = @{
linknumber = 1
maxlinks = 3
type = 'default'
}
$Uri = Get-WebListenerUrl -Test 'Link' -Query $Query
Invoke-RestMethod -Uri $uri -FollowRelLink -MaximumFollowRelLink 1Headers:
HTTP/1.1 200 OK
Date: Sat, 06 Jan 2018 14:27:36 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Link: <http://localhost:8083/Link/?maxlinks=3&linknumber=3>; rel="last",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="first",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="self",<http://localhost:8083/Link/?maxlinks=3&linknumber=2>; rel="next"Body:
{
"type": "default",
"url": "http://localhost:8083/Link/?maxlinks=3&linknumber=1&type=default",
"maxlinks": 3,
"linknumber": 1,
"headers": {
"User-Agent": "insomnia/5.12.4",
"Accept": "*/*",
"Content-Length": "0",
"Host": "localhost:8083",
"Content-Type": "application/json"
},
"args": {
"linknumber": "1",
"maxlinks": "3",
"type": "default"
},
"origin": "127.0.0.1",
"method": "GET"
}/Multipart/
GET
Provides an HTML form for multipart/form-data submission.
POST
Accepts a multipart/form-data submission and returns a JSON object containing information about the submission including the items and files submitted.
$uri = Get-WebListenerUrl -Test 'Multipart'
Invoke-RestMethod -Uri $uri -Body $multipartData -Method 'POST'{
"Files": [
{
"ContentDisposition": "form-data; name=fileData; filename=test.txt",
"Headers": {
"Content-Disposition": [
"form-data; name=fileData; filename=test.txt"
],
"Content-Type": [
"text/plain"
]
},
"FileName": "test.txt",
"Length": 15,
"ContentType": "text/plain",
"Name": "fileData",
"Content": "Test Contents\r\n"
}
],
"Items": {
"stringData": [
"TestValue"
]
},
"Boundary": "83027bde-fd9b-4ea0-b1ca-a1f661d01ada",
"Headers": {
"Content-Type": "multipart/form-data; boundary=\"83027bde-fd9b-4ea0-b1ca-a1f661d01ada\"",
"Connection": "Keep-Alive",
"Content-Length": "336",
"Host": "localhost:8083",
"User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) WindowsPowerShell/6.0.0"
}
}/Patch/
Returns the same results as the Get test. Will only accept the PATCH request method.
$uri = Get-WebListenerUrl -Test 'Patch'
$Body = @{id = 12345} | ConvertTo-Json -Compress
Invoke-RestMethod -Uri $uri -Body $body -Method 'Patch'{
"method": "PATCH",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083",
"Content-Length": "12"
},
"origin": "127.0.0.1",
"url": "http://localhost:8083/Patch",
"args": {},
"data": "{\"id\":12345}"
}/Post/
Returns the same results as the Get test. Will only accept the POST request method. If the POST request is sent with a forms based content type the body will be interpreted as a form instead of raw data.
$uri = Get-WebListenerUrl -Test 'Post'
$Body = @{id = 12345}
Invoke-RestMethod -Uri $uri -Body $body -Method 'Post'{
"method": "POST",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "8"
},
"form": {
"id": [
"12345"
]
},
"origin": "127.0.0.1",
"url": "http://localhost:8083/Post",
"args": {}
}Otherwise, the body will be interpreted as raw data.
$uri = Get-WebListenerUrl -Test 'Post'
$Body = @{id = 12345} | ConvertTo-Json -Compress
Invoke-RestMethod -Uri $uri -Body $body -Method 'Post' -ContentType 'application/json'{
"method": "POST",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083",
"Content-Type": "application/json",
"Content-Length": "12"
},
"origin": "127.0.0.1",
"url": "http://localhost:8083/Post",
"args": {},
"data": "{\"id\":12345}"
}/Put/
Returns the same results as the Get test. Will only accept the PUT request method.
$uri = Get-WebListenerUrl -Test 'Put'
$Body = @{id = 12345} | ConvertTo-Json -Compress
Invoke-RestMethod -Uri $uri -Body $body -Method 'Put'{
"method": "PUT",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
"Connection": "Keep-Alive",
"Host": "localhost:8083",
"Content-Length": "12"
},
"origin": "127.0.0.1",
"url": "http://localhost:8083/Put",
"args": {},
"data": "{\"id\":12345}"
}/Redirect/
Will 302 redirect to /Get/. If a number is supplied, redirect will occur that many times. Can be used to test maximum redirects.
If the type query field is supplied the corresponding System.Net.HttpStatusCode will be returned instead of 302.
If type is relative, the redirect URI will be relative instead of absolute.
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue '2'
Invoke-RestMethod -Uri $uriRequest 1:
GET http://localhost:8083/Redirect/2 HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) WindowsPowerShell/6.0.0
Host: localhost:8083Response 1:
HTTP/1.1 302 Found
Date: Fri, 15 Sep 2017 10:46:41 GMT
Content-Type: text/html; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Location: /Redirect/1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/Redirect/1">/Redirect/1</a>. If not click the link.Request 2:
GET http://localhost:8083/Redirect/1 HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) WindowsPowerShell/6.0.0
Host: localhost:8083Response 2:
HTTP/1.1 302 Found
Date: Fri, 15 Sep 2017 10:46:41 GMT
Content-Type: text/html; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
Location: /Get/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/Get/">/Get/</a>. If not click the link./Response/
Will return a response crafted from the query string. The following four fields are supported:
body- a string containing the response bodystatuscode- the HTTP Status Code to returncontenttype- TheContent-Typeresponse headerheaders- a JSON string containing response headers.Content-Typewill be ignored inheaders. Usecontenttypeinstead.responsephrase- the HTTP response phrase to return
$Query = @{
statsucode = 200
responsephrase = 'OK'
contenttype = 'application/json'
body = '{"key1": "value1"}'
headers = @{
"X-Header" = "Response header value"
} | ConvertTo-Json
}
$Uri = Get-WebListenerUrl -Test 'Response' -Query $Query
Invoke-RestMethod -Uri $uriResponse headers:
Content-Type: application/json
X-Header: Response header valueResponse Body:
{"key1": "value1"}/ResponseHeaders/
Will return the response headers passed in query string. The response body will be the supplied headers as a JSON object.
$uri = Get-WebListenerUrl -Test 'ResponseHeaders' -Query @{'Content-Type' = 'custom'; 'x-header-01' = 'value01'; 'x-header-02' = 'value02'}
Invoke-RestMethod -Uri $uriResponse Headers:
HTTP/1.1 200 OK
Date: Sun, 08 Oct 2017 18:20:38 GMT
Transfer-Encoding: chunked
Server: Kestrel
x-header-02: value02
x-header-01: value01
Content-Type: customBody:
{
"Content-Type": "custom",
"x-header-02": "value02",
"x-header-01": "value01"
}/Resume/
This endpoint simulates the download of a 20 byte file with support for resuming with the use of the Range HTTP request header.
The bytes returned are numbered 1 to 20 inclusive.
If the Range header is greater than 20, the endpoint will return a 416 Requested Range Not Satisfiable response.
The endpoint also returns an X-WebListener-Has-Range response header containing true or false if the HTTP Request contains a Range request header.
The endpoint will also return an X-WebListener-Request-Range response header which contains the Range header value if one was present.
$uri = Get-WebListenerUrl -Test 'Resume'
$response = Invoke-WebRequest -Uri $uri -Headers @{"Range" = "bytes=0-"}Response Headers:
HTTP/1.1 206 PartialContent
Date: Tue, 20 Mar 2018 08:45:42 GMT
Server: Kestrel
X-WebListener-Has-Range: true
X-WebListener-Request-Range: bytes=0-
Content-Length: 20
Content-Type: application/octet-stream
Content-Range: bytes 0-19/20/Resume/Bytes/{NumberBytes}
This endpoint emulates a partial download of the same 20 bytes provided by the /Resume/ endpoint.
The endpoint will return {NumberBytes} bytes of the 20 bytes.
For example /Resume/Bytes/5 will return bytes 1 through 5 inclusive of the 20 byte file.
$uri = Get-WebListenerUrl -Test 'Resume' -TestValue 'Bytes/5'
$response = Invoke-WebRequest -Uri $uriResponse Headers:
HTTP/1.1 200 OK
Date: Tue, 20 Mar 2018 08:50:57 GMT
Server: Kestrel
Content-Length: 5
Content-Type: application/octet-stream/Resume/NoResume
This endpoint is the same as /Resume/ with the exception that it ignores the Range HTTP request header.
This endpoint always returns the full 20 bytes and a 200 status.
The X-WebListener-Has-Range and X-WebListener-Request-Range headers are also returned the same as the /Resume/ endpoint.
$uri = Get-WebListenerUrl -Test 'Resume' -TestValue 'NoResume'
$response = Invoke-WebRequest -Uri $uriResponse Headers:
HTTP/1.1 200 OK
Date: Tue, 20 Mar 2018 08:48:21 GMT
Server: Kestrel
X-WebListener-Has-Range: false
Content-Length: 20
Content-Type: application/octet-stream/Retry/{sessionId}/{failureCode}/{failureCount}
This endpoint causes the failure specified by failureCode for failureCount number of times.
After that a status 200 is returned with body containing the number of times the failure was caused.
$response = Invoke-WebRequest -Uri 'http://127.0.0.1:8083/Retry?failureCode=599&failureCount=2&sessionid=100&' -MaximumRetryCount 2 -RetryIntervalSec 1Response Body:
{
"failureResponsesSent":2,
"sessionId":100
}Overview (`.github/actions/infrastructure/get-changed-files/README.md`)
Get Changed Files Action
A reusable composite action that retrieves the list of files changed in a pull request or push event.
Features
- Supports both
pull_requestandpushevents - Optional filtering by file pattern
- Returns files as JSON array for easy consumption
- Filters out deleted files (only returns added, modified, or renamed files)
- Handles up to 100 changed files per request
Usage
Basic Usage (Pull Requests Only)
- name: Get changed files
id: changed-files
uses: "./.github/actions/infrastructure/get-changed-files"
- name: Process files
run: |
echo "Changed files: ${{ steps.changed-files.outputs.files }}"
echo "Count: ${{ steps.changed-files.outputs.count }}"With Filtering
# Get only markdown files
- name: Get changed markdown files
id: changed-md
uses: "./.github/actions/infrastructure/get-changed-files"
with:
filter: '*.md'
# Get only GitHub workflow/action files
- name: Get changed GitHub files
id: changed-github
uses: "./.github/actions/infrastructure/get-changed-files"
with:
filter: '.github/'Support Both PR and Push Events
- name: Get changed files
id: changed-files
uses: "./.github/actions/infrastructure/get-changed-files"
with:
event-types: 'pull_request,push'Inputs
| Name | Description | Required | Default |
|---|---|---|---|
filter |
Optional filter pattern (e.g., *.md for markdown files, .github/ for GitHub files) |
No | '' |
event-types |
Comma-separated list of event types to support (pull_request, push) |
No | pull_request |
Outputs
| Name | Description |
|---|---|
files |
JSON array of changed file paths |
count |
Number of changed files |
Filter Patterns
The action supports simple filter patterns:
Extension matching: Use
*.extto match files with a specific extension- Example:
*.mdmatches all markdown files - Example:
*.ymlmatches all YAML files
- Example:
Path prefix matching: Use a path prefix to match files in a directory
- Example:
.github/matches all files in the.githubdirectory - Example:
tools/matches all files in thetoolsdirectory
- Example:
Example: Processing Changed Files
- name: Get changed files
id: changed-files
uses: "./.github/actions/infrastructure/get-changed-files"
- name: Process each file
shell: pwsh
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.files }}
run: |
$changedFilesJson = $env:CHANGED_FILES
$changedFiles = $changedFilesJson | ConvertFrom-Json
foreach ($file in $changedFiles) {
Write-Host "Processing: $file"
# Your processing logic here
}Limitations
- Simple filter patterns only (no complex glob or regex patterns)
Pagination
The action automatically handles pagination to fetch all changed files in a PR, regardless of how many files were changed:
- Fetches files in batches of 100 per page
- Continues fetching until all files are retrieved
- Logs a note when pagination occurs, showing the total file count
- No file limit - all changed files will be processed, even in very large PRs
This ensures that critical workflows (such as merge conflict checking, link validation, etc.) don't miss files due to pagination limits.
Related Actions
- markdownlinks: Uses this pattern to get changed markdown files
- merge-conflict-checker: Uses this pattern to get changed files for conflict detection
- path-filters: Similar functionality but with more complex filtering logic
Overview (`.github/actions/infrastructure/markdownlinks/README.md`)
Verify Markdown Links Action
A GitHub composite action that verifies all links in markdown files using PowerShell and Markdig.
Features
- ✅ Parses markdown files using Markdig (built into PowerShell 7)
- ✅ Extracts all link types: inline links, reference links, and autolinks
- ✅ Verifies HTTP/HTTPS links with configurable timeouts and retries
- ✅ Validates local file references
- ✅ Supports excluding specific URL patterns
- ✅ Provides detailed error reporting with file locations
- ✅ Outputs metrics for CI/CD integration
Usage
Basic Usage
- name: Verify Markdown Links
uses: ./.github/actions/infrastructure/markdownlinks
with:
path: './CHANGELOG'Advanced Usage
- name: Verify Markdown Links
uses: ./.github/actions/infrastructure/markdownlinks
with:
path: './docs'
fail-on-error: 'true'
timeout: 30
max-retries: 2
exclude-patterns: '*.example.com/*,*://localhost/*'With Outputs
- name: Verify Markdown Links
id: verify-links
uses: ./.github/actions/infrastructure/markdownlinks
with:
path: './CHANGELOG'
fail-on-error: 'false'
- name: Display Results
run: |
echo "Total links: ${{ steps.verify-links.outputs.total-links }}"
echo "Passed: ${{ steps.verify-links.outputs.passed-links }}"
echo "Failed: ${{ steps.verify-links.outputs.failed-links }}"
echo "Skipped: ${{ steps.verify-links.outputs.skipped-links }}"Inputs
| Input | Description | Required | Default |
|---|---|---|---|
path |
Path to the directory containing markdown files to verify | No | ./CHANGELOG |
exclude-patterns |
Comma-separated list of URL patterns to exclude from verification | No | '' |
fail-on-error |
Whether to fail the action if any links are broken | No | true |
timeout |
Timeout in seconds for HTTP requests | No | 30 |
max-retries |
Maximum number of retries for failed requests | No | 2 |
Outputs
| Output | Description |
|---|---|
total-links |
Total number of unique links checked |
passed-links |
Number of links that passed verification |
failed-links |
Number of links that failed verification |
skipped-links |
Number of links that were skipped |
Excluded Link Types
The action automatically skips the following link types:
- Anchor links (
#section-name) - Would require full markdown parsing - Email links (
mailto:[email protected]) - Cannot be verified without sending email
GitHub Workflow Test
This section provides a workflow example and instructions for testing the link verification action.
Testing the Workflow
To test that the workflow properly detects broken links:
- Make change to this file (e.g., this README.md file already contains one in the Broken Link Test section)
- The workflow will run and should fail, reporting the broken link(s)
- Revert your change to this file
- Push again to verify the workflow passes
Example Workflow Configuration
name: Verify Links
on:
push:
branches: [ main ]
paths:
- '**/*.md'
pull_request:
branches: [ main ]
paths:
- '**/*.md'
schedule:
# Run weekly to catch external link rot
- cron: '0 0 * * 0'
jobs:
verify-links:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Verify CHANGELOG Links
uses: ./.github/actions/infrastructure/markdownlinks
with:
path: './CHANGELOG'
fail-on-error: 'true'
- name: Verify Documentation Links
uses: ./.github/actions/infrastructure/markdownlinks
with:
path: './docs'
fail-on-error: 'false'
exclude-patterns: '*.internal.example.com/*'How It Works
- Parse Markdown: Uses
Parse-MarkdownLink.ps1to extract all links from markdown files using Markdig - Deduplicate: Groups links by URL to avoid checking the same link multiple times
- Verify Links:
- HTTP/HTTPS links: Makes HEAD/GET requests with configurable timeout and retries
- Local file references: Checks if the file exists relative to the markdown file
- Excluded patterns: Skips links matching the exclude patterns
- Report Results: Displays detailed results with file locations for failed links
- Set Outputs: Provides metrics for downstream steps
Error Output Example
✗ FAILED: https://example.com/broken-link - HTTP 404
Found in: /path/to/file.md:42:15
Found in: /path/to/other.md:100:20
Link Verification Summary
============================================================
Total URLs checked: 150
Passed: 145
Failed: 2
Skipped: 3
Failed Links:
• https://example.com/broken-link
Error: HTTP 404
Occurrences: 2Requirements
- PowerShell 7+ (includes Markdig)
- Runs on:
ubuntu-latest,windows-latest,macos-latest
Broken Link Test
License
Same as the PowerShell repository.
Overview (`.github/actions/infrastructure/merge-conflict-checker/README.md`)
Merge Conflict Checker
This composite GitHub Action checks for Git merge conflict markers in files changed in pull requests.
Purpose
Automatically detects leftover merge conflict markers (<<<<<<<, =======, >>>>>>>) in pull request files to prevent them from being merged into the codebase.
Usage
In a Workflow
- name: Check for merge conflict markers
uses: "./.github/actions/infrastructure/merge-conflict-checker"Complete Example
jobs:
merge_conflict_check:
name: Check for Merge Conflict Markers
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
pull-requests: read
contents: read
steps:
- name: checkout
uses: actions/checkout@v5
- name: Check for merge conflict markers
uses: "./.github/actions/infrastructure/merge-conflict-checker"How It Works
- File Detection: Uses GitHub's API to get the list of files changed in the pull request
- Marker Scanning: Reads each changed file and searches for the following markers:
<<<<<<<(conflict start marker)=======(conflict separator)>>>>>>>(conflict end marker)
- Result Reporting:
- If markers are found, the action fails and lists all affected files
- If no markers are found, the action succeeds
Outputs
files-checked: Number of files that were checkedconflicts-found: Number of files containing merge conflict markers
Behavior
- Event Support: Only works with
pull_requestevents - File Handling:
- Checks only files that were added, modified, or renamed
- Skips deleted files
- Filters out
*.csfiles (C# files are excluded from merge conflict checking) - Skips binary/unreadable files
- Skips directories
- Empty File List: Gracefully handles cases where no files need checking (e.g., PRs that only delete files)
Example Output
When conflict markers are detected:
❌ Merge conflict markers detected in the following files:
- src/example.cs
Markers found: <<<<<<<, =======, >>>>>>>
- README.md
Markers found: <<<<<<<, =======, >>>>>>>
Please resolve these conflicts before merging.When no markers are found:
✅ No merge conflict markers foundIntegration
This action is integrated into the linux-ci.yml workflow and runs automatically on all pull requests to ensure code quality before merging.
Overview (`src/System.Management.Automation/engine/ComInterop/README.md`)
ComInterop
The ComInterop code shipped in PowerShell comes from dotnet/runtime with a considerable amount of refactoring work to make it work properly with PowerShell.
NOTE: Do not modify the ComInterop code unless for fixing a bug. We want to keep minimal diffs when comparing with the .NET 5.0 ComInterop code base.
There are 3 sources of the ComInterop code as our references:
The .NET Framework version.
The code was archived in 2012.The .NET 5.0 version.
It was merged into .NET 5.0 in May 2020 through the PR dotnet/runtime#33060.
It's based on the .NET Framework version code with quite amount of refactoring work.The legacy ComInterop code from Windows PowerShell.
The legacy code has always been in the repository, but it was excluded from compilation.
It was based on the .NET Framework version code with a considerable amount of refactoring work to make it work properly with Windows PowerShell.
Code Refreshing
The ComInterop code was refreshed and enabled in compilation in PowerShell 7.1, August 2020.
It was done manually by:
A careful three-way comparison across all the code sources listed above:
- Compare (3) to (1) to get the PowerShell specific changes
- Compare (2) to (1) to get the .NET 5.0 changes
Applying the PowerShell specific changes from (3) to (2), with necessary further refactoring
A careful review of the new PowerShell specific changes applied to (2), again using the three-way comparison:
- Compare the refreshed ComInterop code with (3) to get the differences
- Analyze the differences to make sure they are either the pure .NET 5.0 changes over the .NET Framework code,
or the refactoring changes that is necessary to apply the PowerShell specific changes.
Code Layout Changes
The major changes in code layout are recorded below.
Removed Source Files
The following source files that existed in (1) and (3) were removed in (2):
ComDispIds.cs
ComEventSink.cs
ComEventSinkProxy.cs
ComParamDesc.cs
ComType.cs
ComTypeLibInfo.cs
ComTypeLibMemberDesc.cs
TypeLibInfoMetaObject.csI examined them all and it was mainly a clean-up work done in .NET 5.0:
- The code in
ComParamDesc.cs,ComType.cs,ComTypeLibInfo.cs,ComTypeLibMemberDesc.csandTypeLibInfoMetaObject.cs
are not used or not needed, and thus removed. - The code in
ComDispIds.cs,ComEventSink.csandComEventSinkProxy.cswere moved or refactored to different files.
New Source Files
Some source files were refactored and moved to the System.Runtime.InteropServices namespace in .NET 5.0,
they can be found here.
In PowerShell, the corresponding source files are placed under engine\ComInterop\InteropServices.
Additionally, ComEventsSink.Extended.cs and Variant.Extended.cs under engine\ComInterop are new in .NET 5.0.
Combined with the files ComEventsSink.cs and Variant.cs under engine\ComInterop\InteropServices,
they are the refactored replacement of the old ComEventSink.cs, ComEventSinkProxy.cs and Variant.cs files in .NET Framework.
Overview (`test/perf/dotnet-tools/ResultsComparer/README.md`)
Results Comparer
This simple tool allows for easy comparison of provided benchmark results.
It can be used to compare:
- historical results (eg. before and after my changes)
- results for different OSes (eg. Windows vs Ubuntu)
- results for different CPU architectures (eg. x64 vs ARM64)
- results for different target frameworks (eg. .NET Core 3.1 vs 5.0)
All you need to provide is:
--base- path to folder/file with baseline results--diff- path to folder/file with diff results--threshold- threshold for Statistical Test. Examples: 5%, 10ms, 100ns, 1s
Optional arguments:
--top- filter the diff to top/bottomNresults--noise- noise threshold for Statistical Test. The difference for 1.0ns and 1.1ns is 10%, but it's just a noise. Examples: 0.5ns 1ns. The default value is 0.3ns.--csv- path to exported CSV results. Optional.-f|--filter- filter the benchmarks by name using glob pattern(s). Optional.
Sample: compare the results stored in C:\results\windows vs C:\results\ubuntu using 1% threshold and print only TOP 10.
dotnet run --base "C:\results\windows" --diff "C:\results\ubuntu" --threshold 1% --top 10Note: the tool supports only *full.json results exported by BenchmarkDotNet. This exporter is enabled by default in this repository.
Sample results
| Slower | diff/base | Base Median (ns) | Diff Median (ns) | Modality |
|---|---|---|---|---|
| PerfLabTests.BlockCopyPerf.CallBlockCopy(numElements: 100) | 1.60 | 9.22 | 14.76 | |
| System.Tests.Perf_String.Trim_CharArr(s: "Test", c: [' ', ' ']) | 1.41 | 6.18 | 8.72 |
| Faster | base/diff | Base Median (ns) | Diff Median (ns) | Modality |
|---|---|---|---|---|
| System.Tests.Perf_Array.ArrayCopy3D | 1.31 | 372.71 | 284.73 |
If there is no difference or if there is no match (we use full benchmark names to match the benchmarks), then the results are omitted.
Overview (`test/tools/Modules/UnixSocket/README.md`)
UnixSocket Module
A PowerShell module for managing the UnixSocket App.
Running UnixSocket
Import-Module .\build.psm1
Publish-PSTestTools
$Listener = Start-UnixSocketStopping UnixSocket
Stop-UnixSocketOverview (`test/tools/Modules/WebListener/README.md`)
WebListener Module
A PowerShell module for managing the WebListener App.
When the WebListener is started from this module,
it will automatically generate two fresh certificates,ClientCert.pfx and ServerCert.pfx using the SelfSignedCertificate module.
The generated Self-Signed Certificate ServerCert.pfx has a randomly generated password
and is issued for the Client and Server Authentication key usages.
This certificate is used by the WebListener App for SSL/TLS.
The generated Self-Signed Certificate ClientCert.pfx has a randomly generated password
and is not issued for any specific key usage.
This Certificate is used for Client Certificate Authentication with the WebListener App.
The port used for -HttpsPort will use TLS 1.2.
Running WebListener
Import-Module .\build.psm1
Publish-PSTestTools
$Listener = Start-WebListener -HttpPort 8083 -HttpsPort 8084 -Tls11Port 8085 -TlsPort 8086Stopping WebListener
Stop-WebListenerDOCSMIGRATION (`docs/DOCSMIGRATION.md`)
Documentation Migration
The docs folder in this repository contains documentation about the PowerShell source code and build
environment.
User-focused documentation has been moved to the MicrosoftDocs/PowerShell-Docs repository.
The files from the learning-powershell folder have been removed from this repository. This list
provides links to suitable replacements.
- create-powershell-scripts.md
- debugging-from-commandline.md
- powershell-beginners-guide.md
- README.md
- working-with-powershell-objects.md
FAQ (`docs/FAQ.md`)
Frequently Asked Questions
Where can I learn PowerShell's syntax?
What are the best practices and styles?
The PoshCode unofficial guide is our reference.
What are PowerShell's scoping rules?
- Variables are created in your current scope unless explicitly indicated.
- Variables are visible in a child scope unless explicitly indicated.
- Variables created in a child scope are not visible to a parent unless explicitly indicated.
- Variables may be placed explicitly in a scope.
Things that create a scope
Things that operate in the current scope
- about Operators - Dot source operator (
. { }) - about Language Keywords (
if .. else,for,switch, etc.)
Why didn't an error throw an exception?
Error handling in PowerShell is unique, as not all errors result in catchable exceptions by default.
Setting $ErrorActionPreference = 'Stop' will likely do what you want; that is, cause
non-terminating errors instead of terminating. Read the GitHub issue for more information.
Where do I get the PowerShell Core SDK package?
The SDK NuGet package Microsoft.PowerShell.SDK is provided for developers to write C# code
targeting PowerShell. PowerShell NuGet packages are published to Microsoft.PowerShell.SDK on
NuGet.org.
To use the Microsoft.PowerShell.SDK NuGet package, declare PackageReference tags in your.csproj file as follows:
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.3.5" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.3.5" />
<PackageReference Include="Microsoft.WSMan.Management" Version="7.3.5"/>
</ItemGroup>Why did my build fail?
There are few common issues with the build. The easiest way to resolve most issues with the build is
to run Start-PSBuild -Clean.
Dependency changed
If package dependencies were changed in any project.json, you need to manually rundotnet restore to update your local dependency graphs. Start-PSBuild -Restore can automatically
do this.
Resource changed
Start-PSBuild automatically calls Start-ResGen on the very first run. On subsequent runs, you
may need to explicitly use Start-PSBuild -ResGen command.
Try it, when you see compilation error about *strings.
More details about resource.
TypeGen
Similar to -ResGen parameter, there is -TypeGen parameter that triggers regeneration of type
catalog.
Why did `Start-PSBuild` tell me to update `dotnet`?
We depend on the latest version of the .NET CLI, as we use the output of dotnet --info to
determine the current runtime identifier. Without this information, our build function can't know
where dotnet is going to place the build artifacts.
You can automatically install this using Start-PSBootstrap.
However, you must first manually uninstall other versions of the CLI.
If you have installed by using any of the following means:
MSIexeapt-getpkg
You must manually uninstall it.
Additionally, if you've just unzipped their binary drops (or used their obtain scripts, which do
essentially the same thing), you must manually delete the folder, as the .NET CLI team re-engineered
how their binaries are setup, such that new packages' binaries get stomped on by old packages'
binaries.
Building Internals (`docs/building/internals.md`)
Internals of build process
The purpose of this document is to explain build process internals with subtle nuances.
This document is not by any means complete.
The ultimate source of truth is the code in .\build.psm1 that's getting executed on the corresponding CI system.
This document assumes that you can successfully build PowerShell from sources for your platform.
Top directory
We are calling dotnet tool build for $Top directory
src\powershell-win-corefor CoreCLR on Windows.src\powershell-unixfor CoreCLR on Linux and macOS.
Dummy dependencies
We use dummy dependencies between projects to leverage dotnet build functionality.
For example, src\powershell-win-core\powershell-win-core.csproj has dependency on Microsoft.PowerShell.Commands.Diagnostics.csproj,
but in reality, there is no build dependency.
Dummy dependencies allow us to build just $Top folder, instead of building several folders.
Dummy dependencies rules
If assembly is part of CoreCLR build,
it should be listed as a dependency for $Top folder (src\powershell-unix or src\powershell-win-core)
Preliminary steps
Bundled modules and early-access feeds
The package references in PSGalleryModules.csproj
select the bundled module versions. Their restore sources are configured insrc/Modules/nuget.config, not in the project file.
Switch-PSNugetConfig -Source Private -UseEarlyAccessModules enables thepowershell-module-earlyaccess Azure Artifacts feed alongside the existing PowerShell
feed for released modules. The UseEarlyAccessModules switch is independent of
the .NET feed selected by Source and can be used with any source mode.
Without the switch, bundled modules use only the normal module source.
For public source modes, the normal module source is PowerShell Gallery.
Update a module's package-reference version to consume an internal version.
Internal versions should be distinct from released versions because NuGet caches packages by ID and version.
In Azure Pipelines, enable the UseEarlyAccessModules build parameter (default: false).
This does not require IsEarlyAccess, EARLY_ACCESS_FEED, or an early-access .NET SDK.
The flags can also be enabled together. The sharedinsert-nuget-config-azfeed.yml
template installs the Azure Artifacts Credential Provider and supplies an Azure DevOps
access token through the secret VSS_NUGET_EXTERNAL_FEED_ENDPOINTS variable.
The powershell-net-early-access service connection identity must have read access
to the module feed. The existing PowerShell feed continues to use its separate PAT.
For local restores, install the Azure Artifacts Credential Provider and useRestore-PSPackage -ProjectDirs .\src\Modules -PSModule -Force -InteractiveAuth
with an identity that has read access to both feeds.
ResGen
Until the .NET CLI dotnet-resgen tool supports the generation of strongly typed resource access classes
(tracked by Microsoft/msbuild #2272),
we run our own C# ResGen tool.
While the Start-PSBuild command runs this automatically via the Start-ResGen function,
it does not require PowerShell.
The same command can be run manually:
cd src/ResGen
dotnet restore
dotnet runRunning the program does the following work:
- For each project, given a
resourcesfolder, create agenfolder. - For each
*.resxfile from theresourcesfolder,
create a strongly typed C# resource access class,
and write it to the corresponding*.csfile in thegenfolder.
These files are not automatically updated on each build,
as the project lacks the ability to detect changes.
Thus, running it for every build would break incremental recompilation.
If you pull new commits and get an error about missing strings,
you likely need to delete the gen folders and re-run the tool.
Type Catalog
A pre-generated catalog of C# types is used in PowerShell to help type resolution.
Generating this catalog is a pre-build step that is run via Start-TypeGen,
which Start-PSBuild calls.
Again, however, PowerShell is not required.
The necessary steps can be run manually:
cd ../TypeCatalogGen
dotnet restore
dotnet run ../System.Management.Automation/CoreCLR/CorePsTypeCatalog.cs powershell.incThe file powershell.inc is generated by running a custom MSBuild target.Start-TypeGen handles generating this file,
but you can also do it manually by navigating to the src directory and running the following commands:
targetFile="Microsoft.PowerShell.SDK/obj/Microsoft.PowerShell.SDK.csproj.TypeCatalog.targets"
cat > $targetFile <<-"EOF"
<Project>
<Target Name="_GetDependencies"
DependsOnTargets="ResolveAssemblyReferencesDesignTime">
<ItemGroup>
<_RefAssemblyPath Include="%(_ReferencesFromRAR.HintPath)%3B" Condition=" '%(_ReferencesFromRAR.NuGetPackageId)' != 'Microsoft.Management.Infrastructure' "/>
</ItemGroup>
<WriteLinesToFile File="$(_DependencyFile)" Lines="@(_RefAssemblyPath)" Overwrite="true" />
</Target>
</Project>
EOF
dotnet msbuild Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj /t:_GetDependencies "/property:DesignTimeBuild=true;_DependencyFile=$(pwd)/TypeCatalogGen/powershell.inc" /nologopowershell.inc contains the resolved paths to the DLLs of each dependency of PowerShell,
and is taken as input to the TypeCatalogGen tool,
which generates the source file CorePsTypeCatalog.cs for the System.Management.Automation project.
The error The name 'InitializeTypeCatalog' does not exist in the current context
indicates that the CorePsTypeCatalog.cs source file does not exist,
so follow the steps to generate it.
Native Components
On Windows, PowerShell Core depends on the WinRM plugin pwrshplugin.dll to enable remoting over WinRM.
On Linux/macOS, PowerShell Core depends on the binary libpsl-native.so/libpsl-native.dylib to provide some necessary supports.
Building those native components requires setting up additional dependencies,
which could be a burden to those who don't seek to make changes to the native components.
In the meantime, the native component code seldom changes,
so it doesn't make sense to always build them with Start-PSBuild.
Therefore, we decided to wrap the native components into NuGet packages,
so that we only need to build them once when changes are made,
and then reuse the produced binaries for many builds subsequently.
The NuGet package for pwrshplugin.dll is psrp.windows,
and the NuGet package for libpsl-native is libpsl.
Windows packages: PSRP.Windows and PowerShell.Core.Instrumentation
To build pwrshplugin.dll and PowerShell.Core.Instrumentation.dll, you need to install Visual Studio 2017 and run Start-PSBootstrap -BuildWindowsNative to install the prerequisites.
Ensure the following individual components are selected:
- VC++ 2017 v141 toolset (x86, x64)
- Visual C++ compilers and libraries for ARM
- Visual C++ compilers and libraries for ARM64
- Visual C++ tools for CMake
- Visual C++ ATL Support
- Windows 10 SDK (10.0.16299.0) for Desktop C++ (ARM and ARM64)
- Windows 10 SDK (10.0.16299.0) for Desktop C++ (x86 and x64)
Ensure CMake 3.10.0 or newer is installed which supports VS2017 and ARM64 generator.
Then run Start-BuildNativeWindowsBinaries to build the binary.
For example, the following builds the release flavor of the binary targeting arm64 architecture.
Start-BuildNativeWindowsBinaries -Configuration Release -Arch x64_arm64Be sure to build and test for all supported architectures: x86, x64, x64_arm, and x64_arm64.
The x64_arm and x64_arm64 architectures mean that the host system needs to be x64 to cross-compile to ARM.
When building for multiple architectures, be sure to use the -clean switch as cmake will cache the previous run and the wrong compiler will be used to generate the subsequent architectures.
After that, the binary pwrshplugin.dll, its PDB file, and powershell.core.instrumentation.dll will be placed under src\powershell-win-core.
To create a new NuGet package for pwrshplugin.dll, first you need to get the psrp.windows.nuspec from an existing psrp.windows package.
You can find it at ~/.nuget/packages/psrp.windows on your windows machine if you have recently built PowerShell on it.
Or you can download the existing package from powershell-core feed.
Once you get psrp.windows.nuspec, copy it to an empty folder and update the <version> element.
After building successfully, copy the produced files to the same folder,
and create the same layout of files as in the existing package.
The layout of files should look like this:
\---runtimes
+---win-x64
| \---native
| pwrshplugin.dll
| pwrshplugin.pdb
|
+---win-x86
| \---native
| pwrshplugin.dll
| pwrshplugin.pdb
+---win-arm
| \---native
| pwrshplugin.dll
| pwrshplugin.pdb
\---win-arm64
\---native
pwrshplugin.dll
pwrshplugin.pdbHave the DLLs signed with authenticode dual certificate and run nuget pack from the parent of the runtimes folder where psrp.windows.nuspec resides.
Be sure to use the latest recommended version of nuget.exe.
Publish the latest nupkg to https://powershell.myget.org/feed/powershell-core/package/nuget/psrp.windows.
PowerShell.Core.Instrumentation.dll NuGet package is created the same way, but in a separate directory following the same layout above.
To create a new NuGet package for PowerShell.Core.Instrumentation.dll, you will need the PowerShell.Core.Instrumentation.nuspec found in the repo under src\PowerShell.Core.Instrumentation.
Publish the latest nupkg to https://powershell.myget.org/feed/powershell-core/package/nuget/PowerShell.Core.Instrumentation.
libpsl
For linux-arm, you need to run Start-PSBootstrap -BuildLinuxArm to install additional prerequisites to build libpsl-native.
Note that currently you can build linux-arm only on a Ubuntu machine.
For linux-x64 and macOS, the initial run of Start-PSBootstrap would be enough -- no additional prerequisite required.
After making sure the prerequisites are met, run Start-BuildNativeUnixBinaries to build the binary:
## Build targeting linux-x64 or macOS
Start-BuildNativeUnixBinaries
## Build targeting linux-arm
Start-BuildNativeUnixBinaries -BuildLinuxArmAfter the build succeeds, the binary libpsl-native.so (libpsl-native.dylib on macOS) will be placed under src/powershell-unix.
To create a new NuGet package for libpsl-native, first you need to get the libpsl.nuspec from an existing libpsl package.
You can find it at ~/.nuget/packages/libpsl on your Linux or macOS machine if you have recently built PowerShell on it.
Or you can download the existing package from powershell-core feed.
Once you get psrp.windows.nuspec, copy it to an empty folder on your Windows machine.
Then you need to build three binaries of libpsl-native targeting linux-x64, linux-arm and osx respectively.
Please note that, in order for the linux-x64 binary libpsl-native.so to be portable to all other Linux distributions,
the linux-x64 binary needs to be built on CentOS 7
(.NET Core Linux native binaries are also built on CentOS 7 to ensure that they don't depend on newer glibc).
After building successfully, copy those three binaries to the same folder,
and create the same layout of files as in the existing package.
The layout of files should look like this:
└── runtimes
├── linux-arm
│ └── native
│ └── libpsl-native.so
├── linux-x64
│ └── native
│ └── libpsl-native.so
└── osx
└── native
└── libpsl-native.dylibLastly, run nuget pack . from within the folder. Note that you may need the latest nuget.exe.
Building Linux (`docs/building/linux.md`)
Build PowerShell on Linux
This guide will walk you through building PowerShell on Linux.
We'll start by showing how to set up your environment from scratch.
Environment
These instructions are written assuming Ubuntu 24.04 LTS (the CI uses ubuntu-latest).
The build module works on a best-effort basis for other distributions.
Git Setup
Using Git requires it to be set up correctly;
refer to the Working with the PowerShell Repository,
Readme, and Contributing Guidelines.
This guide assumes that you have recursively cloned the PowerShell repository and cded into it.
Toolchain Setup
We use the .NET Command-Line Interface (dotnet) to build the managed components.
Installing the toolchain is as easy as running Start-PSBootstrap in PowerShell.
Of course, this requires a self-hosted copy of PowerShell on Linux.
Fortunately, this is as easy as downloading and installing the package.
The ./tools/install-powershell.sh script will also install the PowerShell package.
In Bash:
./tools/install-powershell.sh
pwshYou should now be in a PowerShell console host that is installed.
Just import our module, bootstrap the dependencies, and build!
In PowerShell:
Import-Module ./build.psm1
Start-PSBootstrap -Scenario BothThe Start-PSBootstrap function does the following:
- Installs build dependencies and packaging tools via
apt-get(or equivalent package manager) - Downloads and installs the .NET SDK (currently version 10.0.100-rc.1) to
~/.dotnet
If you want to use dotnet outside of Start-PSBuild, add ~/.dotnet to your PATH environment variable.
Build using our module
We maintain a PowerShell module with the function Start-PSBuild to build PowerShell.
Since this is PowerShell code, it requires self-hosting.
If you have followed the toolchain setup section above, you should have PowerShell Core installed.
Import-Module ./build.psm1
Start-PSBuild -UseNuGetOrgThe PowerShell project by default references packages from the private Azure Artifacts feed, which requires authentication. The
-UseNuGetOrgflag reconfigures the build to use the public NuGet.org feed instead.
Congratulations! If everything went right, PowerShell is now built.
The Start-PSBuild script will output the location of the executable:
./src/powershell-unix/bin/Debug/net11.0/linux-x64/publish/pwsh.
You should now be running the PowerShell Core that you just built, if you run the above executable.
You can run our cross-platform Pester tests with Start-PSPester -UseNuGetOrg, and our xUnit tests with Start-PSxUnit.
Building Macos (`docs/building/macos.md`)
Build PowerShell on macOS
This guide supplements the Linux instructions, as
building on macOS is almost identical.
.NET Core 2.x (and by transitivity, us) only supports macOS 10.13+.
Environment
You will want Homebrew or MacPorts, the missing package manager for macOS.
Once installed, follow the same instructions to download and
install a self-hosted copy of PowerShell on your macOS machine.
From pwsh.exe, run Import-Module ./build.psm1 and use Start-PSBootstrap to install the dependencies.
The Start-PSBootstrap function does the following:
- Uses
breworportto install OpenSSL, and GNU WGet - Uninstalls any prior versions of .NET CLI
- Downloads and installs .NET Core SDK to
~/.dotnet
If you want to use dotnet outside of Start-PSBuild,
add ~/.dotnet to your PATH environment variable.
error: Too many open files
Due to a bug in NuGet, the dotnet restore command will fail without the limit increased.
Run ulimit -n 2048 to fix this in your session;
add it to your shell's profile to fix it permanently.
We cannot do this for you in the build module due to #847.
Build using our module
Start a PowerShell session by running pwsh, and then use Start-PSBuild -UseNuGetOrg from the module.
After building, PowerShell will be at ./src/powershell-unix/bin/Debug/net6.0/osx-x64/publish/pwsh.
The PowerShell project by default references packages from the private Azure Artifacts feed, which requires authentication. The
-UseNuGetOrgflag reconfigures the build to use the public NuGet.org feed instead.
Building Windows Core (`docs/building/windows-core.md`)
Build PowerShell on Windows for .NET Core
This guide will walk you through building PowerShell on Windows, targeting .NET Core.
We'll start by showing how to set up your environment from scratch.
Environment
These instructions are tested on Windows 10 and Windows Server 2012
R2, though they should work anywhere the dependencies work.
Git Setup
Using Git requires it to be set up correctly; refer to the
Readme and
Contributing Guidelines.
This guide assumes that you have recursively cloned the PowerShell repository and cded into it.
Visual Studio
Install Visual Studio 2019. The Community edition is available free of charge.
The PowerShell/PowerShell repository requires at least Visual Studio 2019 16.7.
Visual Studio Code
Building PowerShell using Visual Studio Code depends on the PowerShell executable to be called pwsh which means
that you must have PowerShell Core 6 Beta.9 (or newer) installed to successfully build this project (typically for the purpose of debugging).
.NET CLI
We use the .NET command-line interface (dotnet) to build PowerShell.
The version we are currently using is mentioned in global.json at the root of this repository.
The Start-PSBootstrap function will automatically install it and add it to your path:
Import-Module ./build.psm1
Start-PSBootstrap -Scenario DotnetOr you can call Install-Dotnet directly:
Install-DotnetIt removes the previously installed version of .NET CLI and installs the version that PowerShell Core depends on.
If you have any problems installing dotnet, please see their documentation.
Build using our module
We maintain a PowerShell module with the function Start-PSBuild to build PowerShell.
We do not recommend using Visual Studio Dev Environment Terminal to build the source code.
Import-Module ./build.psm1
Start-PSBuild -Clean -PSModuleRestore -UseNuGetOrgThe PowerShell project by default references packages from the private Azure Artifacts feed, which requires authentication. The
-UseNuGetOrgflag reconfigures the build to use the public NuGet.org feed instead.
Congratulations! If everything went right, PowerShell is now built and executable as ./src/powershell-win-core/bin/Debug/net6.0/win7-x64/publish/pwsh.exe.
This location is of the form ./[project]/bin/[configuration]/[framework]/[rid]/publish/[binary name],
and our project is powershell, configuration is Debug by default,
framework is net6.0, runtime identifier is win7-x64 by default,
and binary name is pwsh.
The function Get-PSOutput will return the path to the executable;
thus you can execute the development copy via & (Get-PSOutput).
The powershell project is the .NET Core PowerShell host.
It is the top-level project, so dotnet build transitively builds all its dependencies,
and emits a pwsh executable.
The cross-platform host has built-in documentation via --help.
You can run our cross-platform Pester tests with Start-PSPester.
Import-Module ./build.psm1
Start-PSPester -UseNuGetOrgBuilding in Visual Studio
We currently have the issue #3400 tracking this task.
Cmdlet Example Command Line Simple Example (`docs/cmdlet-example/command-line-simple-example.md`)
Creating a cross-platform binary module with the .NET Core command-line interface tools
This example uses the .NET Core command-line interface tools (akadotnet CLI) to demonstrate how to create a binary module that is portable across operating
systems supported by PowerShell Core as well as Windows PowerShell version 3 and higher.
Because the binary module's assembly will be created as a .NET Standard 2.0 class library,
the same assembly can be imported into both PowerShell Core and Windows PowerShell.
This means you do not have to build and distribute separate assemblies that target these two
different implementations of PowerShell.
Prerequisites
PowerShell Core and/or Windows PowerShell
For this example, you can use any operating system that is supported by PowerShell Core.
To see if your operating system is supported and to get instructions on how to install
PowerShell Core on your operating system, see the Get PowerShell topic in
the PowerShell repo's README.md file.Note: On Windows 10 Anniversary Update or higher, you can use the Windows Subsystem for
Linux (WSL) console to build the module. In order to import and use the module, you'll need
to install PowerShell Core for the distribution and version of Linux you're running.
You can get that version info by running the commandlsb_release -afrom the WSL console..NET Core 2.x SDK
Download and install the .NET Core 2.x SDK for your operating system.
It is recommended that you use a package manager to install the SDK on Linux.
See these instructions on how to install the SDK on Linux.
Be sure to pick your distribution of Linux e.g. RHEL, Debian, etc to get the
appropriate instructions for your platform.
Create the .NET Standard 2.0 Binary Module
Verify you are running the 2.0.0 version of the
dotnetCLI.powershelldotnet --versionThis should output
2.0.0or higher. If it returns a major version of 1, make sure you have
installed the .NET Core 2.x SDK and have restarted your shell to get the newer version of
the SDK tools.Use the
dotnetCLI to create a starterclasslibproject based on .NET Standard 2.0
(the default for classlib projects).powershelldotnet new classlib --name MyModuleAdd a
global.jsonfile that specifies that the project requires the2.0.0version of
the .NET Core SDK. This is necessary to prevent issues if you have more than one
version of the .NET Core SDK installed.powershellcd MyModule dotnet new globaljson --sdk-version 2.0.0Add the PowerShell Standard Library package to the project file.
This package provides theSystem.Management.Automationassembly.Note: As newer versions of this library are released, update the version number
in this command to match the latest version.powershelldotnet add package PowerShellStandard.Library --version 3.0.0-preview-01Add source code for a simple PowerShell command to the
Class1.csfile by opening
that file in an editor and replacing the existing code with the following code.csharpusing System; using System.Management.Automation; namespace MyModule { [Cmdlet(VerbsCommunications.Write, "TimestampedMessage")] public class WriteTimestampedMessageCommand : PSCmdlet { [Parameter(Position=1)] public string Message { get; set; } = string.Empty; protected override void EndProcessing() { string timestamp = DateTime.Now.ToString("u"); this.WriteObject($"[{timestamp}] - {this.Message}"); base.EndProcessing(); } } }Build the project.
powershelldotnet buildImport the binary module and invoke the new command.
Note: The previous steps could have been performed in a different shell such as
Bash if you're on Linux. For this step, make sure you are running PowerShell Core.powershellcd 'bin/Debug/netstandard2.0' Import-Module ./MyModule.dll Write-TimestampedMessage "Test message."
Using a .NET Standard 2.0 based binary module in Windows PowerShell
You may have heard that a .NET assembly compiled as a .NET Standard 2.0 class library
will load into both .NET Core 2.x applications such as PowerShell Core and
.NET Framework 4.6.1 (or higher) applications such as Windows PowerShell.
This allows you to build a single, cross-platform binary module.
Unfortunately, this works best when the .NET Framework application, in this case
Windows PowerShell, has either been compiled against a .NET Standard 2.0 library or with
support declared for .NET Standard libraries. In which case, the build system can provide the
appropriate binding redirects and facade and shim assemblies so that the .NET Standard 2.0
library can find the .NET Framework types it needs within the context of the running
application.
Fortunately, this has been fixed in .NET Framework 4.7.1 and in the Windows 10 Fall
Creators Update. This version of the .NET Framework allows existing applications to
"just work" without the need to modify and/or re-compile them. On these systems, a
.NET Standard 2.0 based binary module will work in Windows PowerShell.
However, for Windows systems that have not been updated to .NET Framework 4.7.1 such a
binary module will not run correctly in Windows PowerShell.
Let's see what happens when you attempt to use this module in Windows PowerShell on
Windows 10 CU (1703 or lower) without .NET Framework 4.7.1 installed.
Copy
MyModule.dllto a folder on a Windows machine.Import the module.
powershellImport-Module .\MyModule.dllNote: The module should import without errors.
Execute the
Write-TimestampedMessagecommand.powershellWrite-TimestampedMessage "Test message."This will result in the following error:
textWrite-TimestampedMessage : Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified. At line:1 char:1 + Write-TimestampedMessage "Test message." + ~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], FileNotFoundException + FullyQualifiedErrorId : System.IO.FileNotFoundException
If the command worked, congratulations! Your system was probably updated to
.NET Framework 4.7.1. Otherwise, this error indicates that the MyModule.dll assembly
can't find the netstandard.dll "implementation" assembly for the version of the
.NET Framework that Windows PowerShell is using.
The fix for missing netstandard.dll
If you install (or already have) the .NET Core SDK for Windows, you can
find the netstandard.dll implementation assembly for .NET 4.6.1 in the following directory:C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib.
Note that, the version number in the path may vary depending on the installed SDK.
If you copy netstandard.dll from this directory to the directory containingMyModule.dll, the Write-TimestampedMessage command will work. Let's try that.
Install .NET Core SDK for Windows, if it isn't already installed.
Start a new Windows PowerShell console. Remember that once a binary assembly is
loaded into PowerShell it can't be unloaded. Restarting PowerShell is necessary to
get it to reloadMyModule.dll.Copy the
netstandard.dllimplementation assembly for .NET 4.6.1 to the module's directory.powershellcd 'path-to-where-you-copied-module.dll' Copy-Item 'C:\Program Files\dotnet\sdk\<version-number>\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll' .Import the module and execute the command:
powershellImport-Module .\MyModule.dll Write-TimestampedMessage "Test message."Now the command should succeed.
Note: If it fails, restart Windows PowerShell to make sure
you don't have a previously loaded version of the assembly in the session and repeat
step 4.
If you use additional libraries there may be more work involved. This approach has
been successfully tested using types from System.Xml and System.Web.
Wrap-up
In a few steps, we have built a PowerShell binary module using a .NET Standard 2.0
class library that will run in PowerShell Core on multiple operating systems.
It will also run in Windows PowerShell on Windows systems that have been updated to
.NET Framework 4.7.1 as well as the Windows 10 Fall Creators Update which comes with that
version pre-installed. Furthermore, this binary module can be built on Linux
and macOS as well as Windows using the .NET Core 2.x SDK command-line tools.
For more information on .NET Standard, check out the documentation
and the .NET Standard YouTube channel.
Cmdlet Example Visual Studio Simple Example (`docs/cmdlet-example/visual-studio-simple-example.md`)
Building a C# Cmdlet with Visual Studio
This document describes steps for building a C# Cmdlet with Visual Studio in 2 ways:
Building a C# Cmdlet for PowerShell Core with Visual Studio
This demonstrates how to build your own C# cmdlet for PowerShell Core with Visual Studio.
Targeting for PowerShell Core means that the cmdlet may not work against Windows PowerShell if you take dependencies on new APIs introduced in PowerShell Core.
We will use the free Visual Studio Community 2017.
When installing Visual Studio 2017 select
.NET Core cross-platform developmentunderOther ToolsetsCreate new C# project
SendGreetingof typeClass Library (.NET Core)Now we need to setup PowerShell Core reference assemblies.
InSolution Explorerright click on projectDependenciesand selectManage NuGet Packages...
In the top-right corner of the package manager click on the smallSettingssprocket icon that is to the right fromPackage sourcedropdown.
By default, there will be onlynuget.orgpackage source inAvailable package sourceslist.
Add another package source with namepowershell-coreand sourcehttps://powershell.myget.org/F/powershell-core/api/v3/index.jsonIn the package manager select new
powershell-coreinPackage sourcedropdown, selectBrowsetab, type inSystem.Management.Automationin the search and selectInclude prerelease.
It should findSystem.Management.Automationpackage, select it and it will show package details; install it usingInstallbutton.Add the code of cmdlet:
csharpusing System.Management.Automation; // PowerShell namespace. namespace SendGreeting { // Declare the class as a cmdlet and specify and // appropriate verb and noun for the cmdlet name. [Cmdlet(VerbsCommunications.Send, "Greeting")] public class SendGreetingCommand : Cmdlet { // Declare the parameters for the cmdlet. [Parameter(Mandatory = true)] public string Name { get; set; } // Override the ProcessRecord method to process // the supplied user name and write out a // greeting to the user by calling the WriteObject // method. protected override void ProcessRecord() { WriteObject("Hello " + Name + "!"); } } }At this point everything should look like this:
Build solution (F6); The
Outputwindow will print the location of generated cmdlet DLL:Start PowerShell Core, run
Import-Moduleon DLL path from previous step and run cmdlet:
You can also run the same cmdlet on Linux and other systems that PowerShell Core supports:
Building a C# Cmdlet for PowerShell Standard 3.0 with Visual Studio
Steps below show how to build your own C# cmdlet for PowerShell Standard 3.0 with Visual Studio.
Targeting PowerShell Standard 3.0 means that the same module will work against PowerShell Core as well as Windows PowerShell v3 and newer,
however, you are limited to a subset of the available PowerShell APIs.
We will use the free Visual Studio Community 2017.
When installing Visual Studio 2017 select
.NET Core cross-platform developmentunderOther ToolsetsCreate new C# project
SendGreetingStdof typeClass Library (.NET Standard)On project properties verify that
Target frameworkis.NET Standard 2.0:Now we need to setup reference assemblies.
InSolution Explorerright click on projectDependenciesand selectManage NuGet Packages...
In the top-right corner of the package manager selectnuget.orgpackage source, selectBrowsetab, type inPowerShellStandard.Libraryin the search and selectInclude prerelease.
It should findPowerShellStandard.Librarypackage, select it and it will show package details; install it usingInstallbutton.Add the code of cmdlet:
csharpusing System.Management.Automation; // PowerShell namespace. namespace SendGreeting { // Declare the class as a cmdlet and specify and // appropriate verb and noun for the cmdlet name. [Cmdlet(VerbsCommunications.Send, "Greeting")] public class SendGreetingCommand : Cmdlet { // Declare the parameters for the cmdlet. [Parameter(Mandatory = true)] public string Name { get; set; } // Override the ProcessRecord method to process // the supplied user name and write out a // greeting to the user by calling the WriteObject // method. protected override void ProcessRecord() { WriteObject("Hello " + Name + "!"); } } }At this point everything should look like this:
Build solution (F6); The
Outputwindow will print the location of generated cmdlet DLL:Now cmdlet can be run on systems supported by PowerShell Standard;
For example:
On PowerShell Core on Windows:
On PowerShell Core on Linux:
On Windows PowerShell on Windows (this requires .NET Framework 4.7.1
Community Governance (`docs/community/governance.md`)
PowerShell Governance
Terms
- PowerShell Committee: A committee of project owners who are responsible for design decisions,
approving RFCs, and approving new maintainers/committee members - Repository maintainer: An individual responsible for merging pull requests (PRs) into
masterwhen all requirements are met (code review, tests, docs, and RFC approval as applicable).
Repository Maintainers are the only people with write permissions for themasterbranch. - Working Groups (WGs) are collections of contributors responsible for
providing expertise on a specific area of PowerShell in order to help establish consensus within
the community and Committee. - Corporation: The Corporation owns the PowerShell repository and, under extreme circumstances,
reserves the right to dissolve or reform the PowerShell Committee, the Project Leads, and the Corporate Maintainer.
The Corporation for PowerShell is Microsoft. - Corporate Maintainer: The Corporate Maintainer is an entity, person or set of persons,
with the ability to veto decisions made by the PowerShell Committee or any other collaborators on the PowerShell project.
This veto power will be used with restraint since it is intended that the community drive the project.
The Corporate Maintainer is determined by the Corporation both initially and in continuation.
The initial Corporate Maintainer for PowerShell is Jeffrey Snover (jpsnover). - RFC process: The "request-for-comments" (RFC) process whereby design decisions get made.
PowerShell Committee
The PowerShell Committee and its members (aka Committee Members) are the primary caretakers of the PowerShell experience, including the PowerShell language, design, and project.
Current Committee Members
- Bruce Payette (BrucePay)
- Jim Truher (JamesWTruher)
- Paul Higinbotham (paulhigin)
- Rob Holt (rjmholt)
- Steve Lee (SteveL-MSFT)
Committee Member Responsibilities
Committee Members are responsible for reviewing and approving PowerShell RFCs proposing new features or design changes.
Changes that require an [RFC][RFC-repo]
The following types of decisions require a written RFC and ample time for the community to respond with their feedback before a contributor begins work on the issue:
- new features or capabilities in PowerShell (e.g. PowerShell classes, PSRP over SSH, etc.)
- anything that might require a breaking change, as defined in our Breaking Changes Contract
- new modules, cmdlets, or parameters that ship in the core PowerShell modules (e.g.
Microsoft.PowerShell.*,PackageManagement,PSReadLine) - the addition of new PowerShell Committee Members or Repository Maintainers
- any changes to the process of maintaining the PowerShell repository (including the responsibilities of Committee Members, Repository Maintainers, and Area Experts)
Changes that don't require an RFC
In some cases, a new feature or behavior may be deemed small enough to forgo the RFC process
(e.g. changing the default PSReadline EditMode to Emacs on Mac/Linux).
In these cases, issues marked as 1 - Planning require only a simple majority of Committee Members to sign off.
After that, a Repository Maintainer should relabel the issue as 2 - Ready so that a contributor can begin working on it.
If any Committee Members feels like this behavior is large enough to warrant an RFC, they can add the label RFC-required and the issue owner is expected to follow the RFC process.
Committee Member DOs and DON'Ts
As a PowerShell Committee Member:
DO enforce the Code of Conduct by taking reports of abuse and violations to the committee for resolution
DO reply to issues and pull requests with design opinions
(this could include offering support for good work or exciting new features)DO encourage healthy discussion about the direction of PowerShell
DO raise "red flags" on PRs that haven't followed the proper RFC process when applicable
DO contribute to documentation and best practices
DO maintain a presence in the PowerShell community outside of GitHub (Twitter, blogs, Stack Overflow, Reddit, Hacker News, etc.)
DO heavily incorporate community feedback into the weight of your decisions
DO be polite and respectful to a wide variety of opinions and perspectives
DO make sure contributors are following the contributing guidelines
DON'T constantly raise "red flags" for unimportant or minor problems to the point that the progress of the project is being slowed
DON'T offer up your opinions as the absolute opinion of the PowerShell Committee.
Members are encouraged to share their opinions, but they should be presented as such.
PowerShell Committee Membership
The initial PowerShell Committee consists of Microsoft employees.
It is expected that over time, PowerShell experts in the community will be made Committee Members.
Membership is heavily dependent on the level of contribution and expertise: individuals who contribute in meaningful ways to the project will be recognized accordingly.
At any point in time, a Committee Member can nominate a strong community member to join the Committee.
Nominations should be submitted in the form of RFCs detailing why that individual is qualified and how they will contribute.
After the RFC has been discussed, a unanimous vote will be required for the new Committee Member to be confirmed.
PowerShell Committee Code of Conduct Enforcement
As stated in the contributing guidelines:
Reports of abuse will be reviewed by the PowerShell Committee and if it has been determined that violations of the
Code of Conduct has occurred, then a temporary ban may be imposed.
The duration of the temporary ban will depend on the impact and/or severity of the infraction.
This can vary from 1 day, a few days, a week, and up to 30 days.
Repeat offenses may result in a permanent ban from the PowerShell org.
Microsoft employees on the PowerShell committee should review the available support at
aka.ms/opensource/moderation-support.
Repository Maintainers
Repository Maintainers are trusted stewards of the PowerShell community/repository responsible for maintaining consistency and quality of PowerShell code.
One of their primary responsibilities is merging pull requests after all requirements have been fulfilled.
For more information on Repository Maintainers--their responsibilities, who they are, and how one becomes a Maintainer--see the README for Repository Maintainers.
Working Groups
Working Groups (WGs) are collections of contributors with knowledge of specific components or
technologies in the PowerShell domain.
They are responsible for issue triage/acceptance, code reviews, and providing their expertise to
others in issues, PRs, and RFC discussions,
as well as to the Committee when said expertise is helpful in broader discussions.
They have write access to the PowerShell repository which gives them the power to:
git pushto all branches exceptmaster.- Merge pull requests to all branches except
master(though this should not be common given thatmasteris the only long-living branch). - Assign labels, milestones, and people to issues.
Working Group Responsibilities
If you are a member of a Working Group, you are expected to be actively involved in any development, design, or contributions in the focus area of the WG.
More information on the responsibilities of Working Groups can be found here,
while current WG definitions and membership can be found here.
If you are a Working Group member:
DO triage and contribute to discussions in issues and PRs that have
WG-*labels assignedDO regularly communicate with other members of your WG to coordinate decisions about
whether issues should move forwardDO make decisions on whether or not issues should proceed forward with implementations or RFCs
DO assign the correct labels to issues
DO assign yourself to issues and PRs labeled with your area of expertise only when you are actively
working on or implementing themDO code reviews for PRs where you're assigned or in your WG
(while reviewing PRs, leave your comment even if everything looks good - a simple "Looks good to me" or "LGTM" will suffice, so that we know someone has already taken a look at it).DO ensure that contributors are following the contributor guidelines
and Code of ConductDO ensure that contributions include Pester tests for all new/changed functionality
DO ensure that contributions include documentation for all new-/changed functionality
DO encourage contributions to refer to issues in their pull request description (e.g.
Resolves issue #123)DO encourage contributions to have meaningful titles for all PRs,
editing their title if necessary to ensure that changelogs convey useful and accurate informationDO verify that all contributions are following the Coding Guidelines
DON'T create new features, new designs, or change behaviors without following the RFC or approval process
Issue Management Process
See our Issue Management Process
Pull Request Process
See our Pull Request Process
Community Working Group Definitions (`docs/community/working-group-definitions.md`)
Working Group Definitions
This document maintains a list of the current PowerShell Working Groups (WG),
as well as their definitions, membership, and a non-exhaustive set of examples of topics that fall
within the purview of that WG.
For an up-to-date list of the issue/PR labels associated with these WGs,
see Issue Management
Desired State Configuration (DSC)
The Desired State Configuration (DSC) WG manages all facets of DSC in PowerShell 7,
including language features (like the Configuration keyword)
and the PSDesiredStateConfiguration module.
Today, DSC is integrated into the PowerShell language, and we need to manage it as such.
Members
- @TravisEz13
- @theJasonHelmick
- @anmenaga
- @gaelcolas
- @michaeltlombardi
- @SteveL-MSFT
Developer Experience
The PowerShell developer experience includes the development of modules (in C#, PowerShell script, etc.),
as well as the experience of hosting PowerShell and its APIs in other applications and language runtimes.
Special consideration should be given to topics like backwards compatibility with Windows PowerShell
(e.g. with PowerShell Standard) and integration with related developer tools
(e.g. .NET CLI or the PowerShell extension for Visual Studio Code).
Members
- @JamesWTruher (PS Standard, module authoring)
- @adityapatwardhan (SDK)
- @michaeltlombardi
- @SeeminglyScience
- @bergmeister
Engine
The PowerShell engine is one of the largest and most complex aspects of the codebase.
The Engine WG should be focused on the
implementation and maintenance of core PowerShell engine code.
This includes (but is not limited to):
- The language parser
- The command and parameter binders
- The module and provider systems
*-Itemcmdlets- Providers
- Performance
- Componentization
- AssemblyLoadContext
It's worth noting that the Engine WG is not responsible for the definition of the PowerShell language.
This should be handled by the Language WG instead.
However, it's expected that many issues will require input from both WGs.
Members
- @daxian-dbw
- @JamesWTruher
- @rkeithhill
- @vexx32
- @SeeminglyScience
- @IISResetMe
- @powercode
- @kilasuit
Interactive UX
While much of PowerShell can be used through both interactive and non-interactive means,
some of the PowerShell user experience is exclusively interactive.
These topics include (but are not limited to):
- Console
- Help System
- Tab completion / IntelliSense
- Markdown rendering
- PSReadLine
- Debugging
Members
- @theJasonHelmick
- @daxian-dbw (PSReadline / IntelliSense)
- @adityapatwardhan (Markdown / help system)
- @JamesWTruher (cmdlet design)
- @SeeminglyScience
- @sdwheeler
- @kilasuit
- @FriedrichWeinmann
- @StevenBucher98
Language
The Language WG is distinct from the Engine WG in that they deal with the abstract definition
of the PowerShell language itself.
While all WGs will be working closely with the PowerShell Committee (and may share members),
it's likely that the Language WG will work especially close with them,
particularly given the long-lasting effects of language decisions.
Members
- @JamesWTruher
- @daxian-dbw
- @SeeminglyScience
Remoting
The Remoting WG should focus on topics like the PowerShell Remoting Protocol (PSRP),
the protocols implemented under PSRP (e.g. WinRM and SSH),
and other protocols used for remoting (e.g. "pure SSH" as opposed to SSH over PSRP).
Given the commonality of serialization boundaries, the Remoting WG should also focus on
the PowerShell job system.
Members
- @anmenaga
- @TravisEz13
Cmdlets and Modules
The Cmdlet WG should focus on core/inbox modules whose source code lives within thePowerShell/PowerShell repository,
including the proposal of new cmdlets and parameters, improvements and bugfixes to existing
cmdlets/parameters, and breaking changes.
However, some modules that ship as part of the PowerShell package are managed in other source repositories.
These modules are owned by the maintainers of those individual repositories.
These modules include:
Microsoft.PowerShell.ArchivePackageManagement(formerlyOneGet)PowerShellGetPSDesiredStateConfiguration
(Note: this community repository maintains a slightly different version of this module on the Gallery,
but should be used for future development ofPSDesiredStateConfiguration.)PSReadLineThreadJob
Members
- @JamesWTruher
- @SteveL-MSFT
- @jdhitsolutions
- @TobiasPSP
- @doctordns
- @kilasuit
Security
The Security WG should be brought into any issues or pull requests which may have security implications
in order to provide their expertise, concerns, and guidance.
Members
- @TravisEz13
- @SydneySmithReal
- @anamnavi
- @SteveL-MSFT
Explicitly not Working Groups
Some areas of ownership in PowerShell specifically do not have Working Groups.
For the sake of completeness, these are listed below:
Build
Build includes everything that is needed to build, compile, and package PowerShell.
This bucket is also not oriented a customer-facing deliverable and is already something handled by Maintainers,
so we don't need to address it as part of the WGs.
- Build
build.psm1install-powershell.ps1- Build infrastructure and automation
- Packaging
- Scripts
- Infrastructure
Quality
Similar to the topic of building PowerShell, quality
(including test code, test infrastructure, and code coverage)
should be managed by the PowerShell Maintainers.
- Test code
- Pester unit tests
- xUnit unit tests
- Test infrastructure
- Nightlies
- CI
- Code coverage
- Pester
Community Working Group (`docs/community/working-group.md`)
Working Groups
Working Groups (WGs) are collections of contributors with knowledge of specific components or
technologies in the PowerShell domain.
They are responsible for issue triage/acceptance, code reviews, and providing their expertise to
others in issues, PRs, and RFC discussions.
The list, description, and membership of the existing Working Groups is available
here.
Terms
- Contributor is used interchangeably within this doc as anyone participating in issues or
contributing code, RFCs, documentations, tests, bug reports, etc.,
regardless of their status with the PowerShell project. - Repository Maintainers are trusted stewards of the PowerShell repository responsible for
maintaining consistency and quality of PowerShell code.
One of their primary responsibilities is merging pull requests after all requirements have been fulfilled.
(Learn more about the Repository Maintainers here.) - The PowerShell Committee is responsible for the design and governance of the PowerShell project,
primarily by voting to accept or reject review-for-comment (RFC) documents.
(Learn more about the PowerShell Committee here.) - A Working Group is a collection of people responsible for providing expertise on a specific
area of PowerShell in order to help establish consensus within the community and Committee.
The responsibilities of Working Groups are outlined below.
(Note: while some experts within Working Groups may have more specific expertise in a sub-topic
of the team,
the intent is that each team is holistically and collectively responsible for making decisions
within the larger topic space.)
Goals
In designing the WG process, the Committee had a few goals:
- Increase the velocity of innovation without compromising the stability of PowerShell
- Reduce the time spent by contributors on writing/reviewing PRs and RFCs that are not feasible
- Increase the formal authority of subject matter experts (SMEs) inside and outside of Microsoft
- Decrease the volume of required technical discussions held by the Committee
Process
This process is represented within the process_diagram.vsdx Visio diagram:
A contributor has an idea for PowerShell
The contributor files an issue informally describing the idea,
including some rationale as to why it should happen and a few use cases to show how it could work,
as well as to determine viability and value to the community.
This should include examples of expected input and output so that others understand how the feature would function in the real world.The issue gets triaged into an Area label
by Maintainers.
This area label maps to one or more Working Groups.If the Working Group determines that an idea can be prototyped or built outside of the PowerShell repo
(e.g. as a module),
contributors should start that idea outside of the PowerShell project.
Given that the issue is no longer directly relevant to the PowerShell project, it should be closed
(with a link to the new project, if available).
If the implementation turns out to be successful and particularly popular,
and a contributor believes that it would be overwhelmingly valuable to include in the primary PowerShell package,
they can restart the process in a new issue proposing that the functionality be
incorporated directly into the primary PowerShell package.After the issue is filed, interested contributors should discuss the
feasibility and approach of the idea in the issue.
The Working Group that owns that Area is expected to contribute to this discussion.
Working groups may have their own criteria to consider in their areas.After an appropriately exhaustive discussion/conversation
(i.e. the Working Group has determined that no new arguments are being made),
the Working Group makes a call on whether they believe the idea should continue through this process.
Note: this should be done via a best effort of consensus among the Working Group.
We don't currently have hard requirements for how this should be done,
but some ideas include:- a single member of the Working Group makes a proposal as a comment and other members should
"react" on GitHub with up/down thumbs - Working Groups communicate privately through their own established channel to reach consensus
It's worth noting that Working Group members who repeatedly speak on behalf of the Working Group without
consensus, they may be censured or removed from the team.- a single member of the Working Group makes a proposal as a comment and other members should
Working Groups reject the proposal
If the Working Group says the idea should not pursued, the process stops.
Some reasons for rejection include (but are not limited to):
- the idea can be implemented and validated for usefulness and popularity outside of the primary PowerShell repo/package
- the idea is difficult/impossible to implement
- the implementation would introduce undesirable (and possibly breaking) changes to PowerShell
- other reasons specific to individual Working Groups
In the instance that the contributor feels they have compelling arguments showing that the
Working Group is incorrect in their rejection,
they can appeal to the PowerShell Committee by mentioning @PowerShell/PowerShell-Committee,
upon which a maintainer will add the Review-Committee label to the issue and reopen it.
Then, the PS Committee will discuss further with the Working Group and others to make a final call on
whether or not the issue should be pursued further.
Be sure to enumerate your reasons for appeal, as unfounded appeals may be rejected for consideration
by the Committee until reasons are given.
Working groups believe the proposal has merit and/or potential
If the idea passes the preliminary acceptance criteria for the Working Group,
the process proceeds on one of a few different paths:
"RFC Not Required"
In some cases, a proposed idea is determined by the Working Group to be small, uncontroversial, or simple,
such that an RFC is not required (to be determined by the Working Group).
In these circumstances, the Working Group should mark the issue as "RFC not required",
upon which it can move directly to the code PR phase to be reviewed by Working Groups and Maintainers.
The Committee still holds the authority to require an RFC if they see an "RFC Not Required" issue
that they feel needs more exposition before merging.
In cases of minor breaking changes, Maintainers or Working Groups can add the Review - Committee label to get
additional opinions from the Committee.
RFC/Prototype Process
If an idea has any significant design or ecosystem implications,
cannot be prototyped or built outside of the PowerShell repo,
and the community and Working Groups agree that the idea is worth pursuing,
a contributor (who may or may not be the original issue filer) must do two things:
- Write an RFC as a Draft PR
intoPowerShell/PowerShell-RFC - Prototype the implementation as a Draft PR
intoPowerShell/PowerShell
In both cases, the intention is to provide Working Groups and other contributors who care about the
idea an opportunity to provide feedback on the design and implementation.
Either of these two steps can be done first, but both are required in order to have code accepted
into the PowerShell repository, including experimental features.
Note: When "Draft" is capitalized in this document, I'm referring to the
Draft pull request
feature on GitHub.
We intend to use this feature liberally to mark the lifecycle of an idea through to implementation.
RFCs
The existing RFC process uses folders as a way to move an RFC through a multi-stage process
(Draft -> Experimental -> Accepted/Final).
However, it was difficult to reconcile this process with the benefits of PR reviews.
With the introduction of Draft PRs on GitHub, we are reorienting the acceptance process around the PR itself.
Going forward, an RFC will have three stages:
- A Draft PR, denoting that the RFC is still in the review period, openly soliciting comments,
and that it may continue to be significantly iterated upon with revisions, edits, and responses to
community feedback or concerns. - After a minimum of two months of discussion, the RFC/PR author marks the PR as
ready for review,
upon which the RFC will enter the Committee's review queue to discuss the RFC contents and comments,
and make a decision on whether it is reasonable to pursue.
If after this review, the Committee determines that the intent of the RFC is not reasonable
(e.g. there may be irreconcilable issues with the design,
or the intent may not fit with the principles of PowerShell),
they will reject the PR and the process terminates. - In most cases, the Committee will choose to wait for the code PR to be merged as experimental,
and leverage user feedback or telemetry to determine if the RFC PR should be merged. - Finally, the Committee will choose to either merge or close the RFC PR,
marking the RFC as either accepted or rejected, respectively.
Experiments
Often times, implementing an idea can demonstrate opportunities or challenges that were not well
understood when the idea was formulated.
Similarly, a "simple" code change can have far reaching effects that are not well understood
until you're able to experiment with an idea within working code.
To that end, it's required that some implementation exist before the Committee will consider an
RFC for acceptance.
That way, contributors can compile the PR branch to play with a working iteration of the idea
as a way to understand whether the feature is working as expected and valuable.
In most cases, as long as an RFC has already been written and published as a draft,
Working Groups or the Committee will approve the feature for incorporation as an experimental feature,
so that it can be trialed with greater usage as part of a preview release.
In addition to increasing the scope of those who can provide real-world feedback on the feature,
this enables us to use telemetry to understand if users are turning off the feature in large numbers.
Note: today, this will be done on a case-by-case basis, but over time, the Committee will establish
firmer guidelines around when PRs should be merged as experimental.
Experiments should be complete to the extent that they serve as reasonable indicators of the user experience.
In the case that breaking changes are required of the feature, the break should be made in the prototype
so that users can experiment with whether or not the break has a significant negative effect.
Reporting Working Group members abuse
Working group members are individuals and in many cases volunteers.
There may be situations where a working group member might exhibit behavior that is objectionable,
exceed the authority defined in this document or violate the Code of Conduct.
We recommend to report such issues by using the Report Content mechanism to bring it to the attention of the maintainers to review.
Dev Process Breaking Change Contract (`docs/dev-process/breaking-change-contract.md`)
Breaking Changes
We have a serious commitment to backwards compatibility with all earlier versions of PowerShell – including the language, cmdlets, APIs and the various protocols (e.g. PowerShell Remoting Protocol) and data formats (e.g. cdxml).
Below is a summary of our approach to handing breaking changes including what kinds of things constitute breaking changes, how we categorize them, and how we decide what we're willing to take.
Note that these rules only apply to existing stable features that have shipped in a supported release. New features marked as “in preview” that are still under development may be modified from one preview release to the next.
These are not considered breaking changes.
To help triage breaking changes, we classify them in to four buckets:
- Public Contract
- Reasonable Grey Area
- Unlikely Grey Area
- Clearly Non-Public
Bucket 1: Public Contract
Any change that is a clear violation of the public contract.
Unacceptable changes
- A code change that results in a change to the existing behavior observed for a given input with an API, protocol or the PowerShell language.
- Renaming or removing a public type, type member, or type parameter; renaming or removing a cmdlet or cmdlet parameter (note: it is possible to rename a cmdlet parameter if a parameter alias is added.
This is an acceptable solution for PowerShell scripts but may break .NET code that depends on the name of the original member on the cmdlet object type.)
- Decreasing the range of accepted values within a given parameter.
- Changing the value of a public constant or enum member; changing the type of a cmdlet parameter to a more restrictive type.
- Example: A cmdlet with a parameter -p1 that was previously type as [object] cannot be changed to be or a more restrictive type such as [int].
- Making an incompatible change to any protocol without increasing the protocol version number.
Acceptable changes
- Any existing behavior that results in an error message generally may be changed to provide new functionality.
- A new instance field is added to a type (this impacts .NET serialization but not PowerShell serialization and so is considered acceptable.)
- Adding new types, new type members and new cmdlets
- Making changes to the protocols with a protocol version increment. Older versions of the protocol would still need to be maintained to allow communication with earlier systems. This would require that protocol negotiation take place between the two systems. In addition to any protocol code changes, the Microsoft Open Specification program requires that the formal protocol specification for a protocol be updated in a timely manner. An example of a MS protocol specification document (MS-PSRP) can be found at: https://msdn.microsoft.com/library/mt242417.aspx
Bucket 2: Reasonable Grey Area
Change of behavior that customers would have reasonably depended on.
Examples:
- Change in timing/order of events (even when not specified in docs)
- Example: PowerShell events are handled by interleaving their execution with the execution of the main pipeline thread. Where and when this interleaving occurs might change. This order of execution is not specified but is deterministic so changing it might break some scripts.
- Change in parsing of input and throwing new errors (even if parsing behavior is not specified in the docs)
- Example: a script may be using a JSON parser that is forgiving to minor syntactic errors in the JSON text. Changing that parser to be more rigorous in its processing would result in errors being thrown when no error was generated in the past thus breaking scripts.
Judiciously making changes in these type of features require judgement: how predictable, obvious, consistent was the behavior?
In general, a significant external preview of the change would need to be done also possibly requiring an RFC to be created to allow for community input on the proposal.
Bucket 3: Unlikely Grey Area
Change of behavior that customers could have depended on, but probably wouldn't.
Examples:
correcting behavior in a subtle corner case that has no obvious utility.
Example: the existing behavior of the PowerShell cd called without arguments is to do nothing. Changing this behavior to be consistent with UNIX shells which typically set the CWD to the user’s home directory
Example: changes to formatting for an object type. We have always considered the output formatting of an object to be a user experience issue and thus open for change. Since PowerShell pipes objects not text, changes to the way an object is rendered to text is generally considered to be allowed.
As with type 2 changes, these require judgement: what is reasonable and what’s not?
Bucket 4: Clearly Non-Public
Changes to surface area or behavior that is clearly internal or non-breaking in theory, but breaks an app.
Examples:
- Changes to internal APIs that break private reflection.
- Changes to APIs in the
System.Management.Automation.Internalnamespace (even if they are public, they are still considered internal and subject to change). - Renaming a parameter set (see related discussion here).
It is impossible to evolve a code base without making such changes, so we don't require up-front approval for these, but we will sometimes have to go back and
revisit such change if there's too much pain inflicted on the ecosystem through a popular app or library.
What This Means for Contributors
- All bucket 1, 2, and 3 breaking changes require contacting team at @powershell/powershell.
- If you're not sure into which bucket a given change falls, contact us as well.
- It doesn't matter if the existing behavior is "wrong", we still need to think through the implications. PowerShell has been in broad use for over 10 years so we be must be extremely sensitive to breaking existing users and scripts.
- If a change is deemed too breaking, we can help identify alternatives such as introducing a new API or cmdlet and obsoleting the old one.
Request for clarifications or suggested alterations to this document should be done by opening issues against this document.
Dev Process Coding Guidelines (`docs/dev-process/coding-guidelines.md`)
C# Coding Guidelines
Coding Conventions
As a general rule, our coding convention is to follow the style of the surrounding code.
So if a file happens to differ in style from conventions defined here
(e.g. private members are named m_member rather than _member),
the existing style in that file takes precedence.
When making changes, you may find some existing code goes against the conventions defined here.
In such cases, please avoid reformatting any existing code when submitting a PR as it obscures the functional changes of the PR.
A separate PR should be submitted for style-only changes.
We also run the .NET code formatter tool regularly to keep consistent formatting.
Naming Conventions
Use meaningful, descriptive words for names.
For method names, it's encouraged to useVerbObjectpair such asLoadModule.Use
_camelCaseto name internal and private fields and usereadonlywhere possible.
Prefix instance fields with_, static fields withs_and thread static fields witht_.
When used on static fields,readonlyshould come afterstatic(i.e.static readonlynotreadonly static).Use
camelCaseto name non-constant local variables.Use
PascalCaseto name constant local variables and fields.
The only exception is for interop code where the constant should exactly match the name and value of the code you are calling via interop (e.g.const int ERROR_SUCCESS = 0).Use
PascalCaseto name types and all other type members.
Layout Conventions
Use four spaces of indentation (no tabs).
Avoid more than one blank empty line at any time.
Avoid trailing spaces at the end of a line.
Braces usually go on their own lines,
with the exception of single line statements that are properly indented.Namespace imports should be specified at the top of the file,
outside ofnamespacedeclarations.Fields should be specified at the top within type declarations.
For those that serve as backing fields for properties,
they should be specified next to the corresponding properties.Preprocessor directives like
#ifand#endifshould be placed at the beginning of a line,
without any leading spaces.File encoding should be
ASCII.
AllBOMencodings should be avoided.
Tests that need aBOMencoding file should generate the file on the fly.
Member Conventions
Use of
thisis neither encouraged nor discouraged.Use
nameof(<member-name>)instead of"<member-name>"whenever possible and relevant.
The motivation is to easily and more accurately find references.Always specify the visibility, even if it's the default (i.e.
private string _foonotstring _foo).
Visibility should be the first modifier (i.e.public abstractnotabstract public).Make members private where possible.
Avoid declaring public members unless it's absolutely necessary.Public members in a namespace that ends with
Internal,
for exampleSystem.Management.Automation.Internalare not considered a supported public API.
Such members are necessarily public as implementation details in code shared between C# and PowerShell script,
or must be available publicly by generated code.
Commenting Conventions
Place the comment on a separate line, not at the end of a line of code.
Begin comment text with an uppercase letter.
It's recommended to end comment text with a period but not required.Add comments where the code is not trivial or could be confusing.
Add comments where a reviewer needs help to understand the code.
Update/remove existing comments when you are changing the corresponding code.
Make sure the added/updated comments are meaningful, accurate and easy to understand.
Documentation comments
Create documentation using XML documentation comments so that Visual Studio and other IDEs can use IntelliSense to show quick information about types or members.
Publicly visible types and their members must be documented.
Internal and private members may use doc comments but it is not required.Documentation text should be written using complete sentences ending with full stops.
Performance Considerations
PowerShell has a lot of performance sensitive code as well as a lot of inefficient code.
We have some guidelines that we typically apply widely even in less important code because code and patterns are copied,
and we want certain inefficient code to stay out of the performance critical code.
Some general guidelines:
Avoid LINQ - it can create lots of avoidable garbage.
Instead, iterate through a collection directly usingfororforeachloop.Between
forandforeach,foris slightly preferred when you're uncertain ifforeachallocates an iterator.Avoid
paramsarrays, prefer adding overloads with 1, 2, 3, and maybe more parameters.Be aware of APIs such as
String.Split(params char[])that do not provide overloads to avoid array allocation.
When calling such APIs, reuse a static array when possible (e.g.Utils.Separators.Colon).Avoid using string interpolations and overloads with implicit parameters such as
CultureandStringComparison.
Instead, use overloads with more explicit parameters such asString.Format(IFormatProvider, String, Object[])andEquals(String, String, StringComparison).Avoid unnecessary memory allocation in a loop.
Move the memory allocation outside the loop if possible.Avoid gratuitous exceptions as much as possible.
Exception handling can be expensive due to cache misses and page faults when accessing the handling code and data.
Finding and designing away exception-heavy code can result in a decent performance win.
For example, you should stay away from things like using exceptions for control flow.Avoid
if (obj is Example) { example = (Example)obj }when casting an object to a type.
Instead, usevar example = obj as Exampleor the C# 7 syntaxif (obj is Example example) {...}as appropriate.
In this way you can avoid converting to the type twice.Use generic collections instead of the non-generic ones such as
ArrayListandHashtableto avoid type casting and unnecessary boxing whenever possible.Use collection constructor overloads that take an initial capacity for collection types that have them.
Internally,List<T>,Dictionary<TKey, TValue>,
and the other generic collections use one or more arrays to hold valid data.
Whenever resizing is needed,
one or more new arrays double the size of existing arrays are created and items from the existing arrays are copied.
Setting an approximate initial capacity will reduce the number of resizing operations.Use
dict.TryGetValueinstead ofdict.Containsanddict[..]when retrieving value from aDictionary.
In this way you can avoid hashing the key twice.It's OK to use the
+operator to concatenate one-off short strings.
But when dealing with strings in loops or large amounts of text,
use aStringBuilderobject.
Security Considerations
Security is an important aspect of PowerShell and we need to be very careful about changes that may introduce security risks,
such as code injection caused by the lack of input validation,
privilege escalation due to the misuse of impersonation,
or data privacy breach with a plain text password.
Reviewers of a PR should be sensitive to changes that may affect security.
Some security related keywords may serve as good indicators,
such as password, crypto, encryption, decryption, certificate, authenticate, ssl/tls and protected data.
When facing a PR with such changes,
the reviewers should request a designated security Subject Matter Expert (SME) to review the PR.
Currently, @PaulHigin and @TravisEz13 are our security SMEs.
See CODEOWNERS for more information about the area experts.
Best Practices
Avoid hard-coding anything unless it's absolutely necessary.
Avoid a method that is too long and complex.
In such case, separate it to multiple methods or even a nested class as you see fit.Use the
usingstatement instead oftry/finallyif the only code in thefinallyblock is to call theDisposemethod.Use of object initializers (e.g.
new Example { Name = "Name", ID = 1 }) is encouraged for better readability,
but not required.Stick to the
DRYprinciple -- Don't Repeat Yourself.- Wrap the commonly used code in methods,
or even put it in a utility class if that makes sense,
so that the same code can be reused (e.g.StringToBase64Converter.Base64ToString(string)). - Check if the code for the same purpose already exists in the code base before inventing your own wheel.
- Avoid repeating literal strings in code. Instead, use
constvariable to hold the string. - Resource strings used for errors or UI should be put in resource files (
.resx) so that they can be localized later.
- Wrap the commonly used code in methods,
Use of new C# language syntax is encouraged.
But avoid refactoring any existing code using new language syntax when submitting a PR
as it obscures the functional changes of the PR.
A separate PR should be submitted for such refactoring without any functional changes.Consider using the
Interlockedclass instead of thelockstatement to atomically change simple states. TheInterlockedclass provides better performance for updates that must be atomic.Here are some useful links for your reference:
- Framework Design Guidelines - Naming, Design and Usage guidelines including:
- Best Practices for Developing World-Ready Applications - Unicode, Culture, Encoding and Localization.
- Best Practices for Exceptions
- Best Practices for Using Strings in .NET
- Best Practices for Regular Expressions in .NET
- Serialization Guidelines
- Managed Threading Best Practices
Portable Code
There are 3 primary preprocessor macros we use during builds:
DEBUG- guard code that should not be included in release buildsCORECLR- guard code that differs between Full CLR and CoreCLRUNIX- guard code that is specific to Unix (Linux and macOS)
Any other preprocessor defines found in the source are used for one-off custom builds,
typically to help debug specific scenarios.
Here are some general guidelines for writing portable code:
We are in the process of cleaning up Full CLR specific code (code enclosed in
!CORECLR),
so do not useCORECLRor!CORECLRin new code.
PowerShell Core targets .NET Core only and all new changes should support .NET Core only.The PowerShell code base started on Windows and depends on many Win32 APIs through P/Invoke.
Going forward, we try to depend on .NET Core to handle platform differences,
so avoid adding new P/Invoke calls where a suitable alternative exists in .NET Core.Try to minimize the use of
#if UNIX.
When absolutely necessary, avoid duplicating more code than necessary,
and instead prefer introducing helper functions to minimize the platform differences.When adding platform dependent code (
Windowsvs.UNIX), prefer preprocessor directives over runtime checks.
However, runtime checks are acceptable if it would greatly improve readability
without causing performance concerns in performance-sensitive code.We produce a single binary for all UNIX variants,
so runtime checks are currently necessary for some of them (e.g. macOS vs. Linux).
Dev Process Resx Files (`docs/dev-process/resx-files.md`)
Resources
Resources are .resx files with string values that we use for error messages and such.
They live in src\\resources folders.
At the moment dotnet cli doesn't support generating C# bindings (strongly typed resource files).
We are using our own Start-ResGen to generate them.
Usually it's called as part of the regular build with
Start-PSBuild -ResGenIf you see compilation errors related to resources, try to call Start-ResGen explicitly.
Start-ResGenEditing `.resx` files
Don't edit .resx files from Visual Studio.
It will try to create .cs files for you and you will get whole bunch of hard-to-understand errors.
To edit a resource file, use any plain text editor.
A resource file is a simple XML file, and it's easy to edit.
Convert `.txt` resource files into `.resx` files
dotnet cli doesn't support embedding old-fashioned .txt resource.
You can do a one-time conversion of .txt resources into .resx files with a helper function:
# example, converting all .txt resources under src\Microsoft.WSMan.Management\resources
Convert-TxtResourceToXml -Path src\Microsoft.WSMan.Management\resources.resx files would be placed next to .txt files.
Git Basics (`docs/git/basics.md`)
Getting started with Git
We are using Git version 2.9.0, but any recent version should be good.
It's recommended to learn the git command-line tool for full
cross-platform experience and a deeper understanding of Git itself.
Install
Windows
Install Git for Windows.
During the installation process, choose these recommended settings:
- Use Git from the Windows Command Prompt
- Use OpenSSH
- Checkout Windows-style, commit Unix-style line endings
- Use Windows' default console window
- Enable file system caching
Linux
Install by using the package manager on your system.
A list of all the package managers and commands can be found here.
Interactive tutorials
There are (too) many Git tutorials on the internet. Here we post
references to our favorites.
Hello World
If you're new to Git, learn the following commands: checkout, branch,pull, push, merge.
Use GitHub's Hello World to learn how to create a feature branch, commit
changes, and issue a pull request.
Githug
Githug is a great gamified way to
learn Git in couple hours. After finishing 50+ real-world scenarios
you will have a pretty good idea about what and when you can do with
Git.
Authentication
Windows
On Windows, the best way to use Git securely is Git Credential Manager for Windows.
It's included in the official Git installer for Windows.
Linux and macOS
If you do not have a preferred method of authentication, enable the storage
credential helper, which will cache your credentials in plaintext on your
system, so use a token.
git config --global credential.helper storeAlternatively, you can use SSH key.
In this case, you may want to use git-ssh even for HTTPS Git URLs.
git config --global [email protected]:.insteadOf https://github.com/Maintainers Best Practice (`docs/maintainers/best-practice.md`)
Maintainer Best Practices
PR Types
- Feature-work PR: A PR that implements an RFC, which usually involves relatively large set of changes.
- Regular PR: A bug fix or an enhancement change that are not backed by an RFC.
Review PRs
- Ask the author to reword the PR title based on guidelines in Contributing.
- Ask the author to apply
[feature]tag to trigger full test builds if it's necessary. - Label the PR with
Breaking-Change,Documentation NeededandArea-XXXas appropriate. - When labelling a PR with
Review-Committee, leave a detailed comment to summarize the issue you want the committee to look into.
It's recommended to include examples to explain/demonstrate behaviors.
Merge PRs
Use
Squash and mergeby default to keep clean commit history in Master branch.Use
Create a merge commitfor feature-work PRs only if the commit history of the PR is reasonably clean.
After using this option, GitHub will make it your default option for merging a PR.
Do remember to change the default back toSquash and mergeas it will be useful next time.Avoid
Rebase and mergeunless you have a strong argument for using it.Before clicking
Confirm squash and mergeorConfirm merge,
make sure you run through the following steps:The commit title should be a short summary of the PR.
When merging with the
Squash and mergeoption,
the PR title will be used as the commit title by default.
Reword the title as needed to make sure it makes sense (can be used without change inCHANGELOG.md).When merging with the
Create a merge commitoption,
the default commit title would beMerge pull request XXX from YYY.
Replace it with a short summary of the PR, and add the PR number to the end, like(#1234).
The optional extended description is required for feature-work PRs, or regular PRs with breaking changes.
For other PRs, it's not required but good to have based on the judgement of the maintainer.- If a PR introduces breaking changes from the previous stable release,
make sure you put the tag[breaking change]at the first line of the extended description,
and start the description text from the second line.
- If a PR introduces breaking changes from the previous stable release,
Use the present tense and imperative mood for both the commit title and description.
Maintainers Issue Management (`docs/maintainers/issue-management.md`)
Issue Management
Security Vulnerabilities
If you believe that there is a security vulnerability in PowerShell,
first follow the vulnerability issue reporting policy before submitting an issue.
Long-living issue labels
Issue labels for PowerShell/PowerShell can be found here.
Issue and PR Labels
Issues are opened for many different reasons.
We use the following labels for issue classifications:
Issue-Announcement: the issue is for discussing an AnnouncementIssue-Bug: the issue is reporting a bugIssue-Code Cleanup: the issue is for cleaning up the code with no impact on functionalityIssue-Discussion: the issue may not have a clear classification yet.
The issue may generate an RFC or may be reclassified as a bug or enhancement.Issue-Enhancement: the issue is more of a feature request than a bug.Issue-Meta: an issue used to track multiple issues.Issue-Question: ideally support can be provided via other mechanisms,
but sometimes folks do open an issue to get a question answered and we will use this label for such issues.
When an issue is resolved, the following labels are used to describe the resolution:
Resolution-Answered: the issue was anIssue-Questionand was answered.Resolution-By Design: the issue is not considered a bug, the behavior is working as designed.Resolution-Duplicate: the issue is a duplicate - there must be a comment linking to another issue.Resolution-External: the issue cannot be address by this repo. Should be addressed externally.Resolution-Fixed: the issue has been fixed and should be referenced from a PR.Resolution-Won't Fix: the issue may be considered a bug or enhancement but won't be fixed.
If there is an inadequate explanation as to why the issue was closed,
anyone should feel free to reopen the issue.
Feature areas
These labels describe what feature area of PowerShell that an issue affects.
Those labels denoted by WG-* are owned by a Working Group (WG) defined
here:
Area-Maintainers-Build: build issuesArea-Cmdlets-Core: cmdlets in the Microsoft.PowerShell.Core moduleArea-Cmdlets-Utility: cmdlets in the Microsoft.PowerShell.Utility moduleArea-Cmdlets-Management: cmdlets in the Microsoft.PowerShell.Management moduleArea-Documentation: PowerShell repo documentation issues, general PowerShell doc issues go hereArea-DSC: DSC related issuesArea-PowerShellGet: PowerShellGet related issuesArea-SideBySide: side by side supportWG-DevEx-Portability: anything related to authoring cross-platform or cross-architecture
modules, cmdlets, and scriptsWG-DevEx-SDK: anything related to hosting PowerShell as a runtime, PowerShell's APIs,
PowerShell Standard, or the development of modules and cmdletsWG-Engine: core PowerShell engine, interpreter, and runtimeWG-Engine-Performance: core PowerShell engine, interpreter, and runtime performanceWG-Engine-Providers: built-in PowerShell providers such as FileSystem, Certificates,
Registry, etc. (or anything returned byGet-PSProvider)WG-Interactive-Console: the console experienceWG-Interactive-Debugging: debugging PowerShell scriptWG-Interactive-HelpSystem: anything related to the help infrastructure and formatting of helpWG-Interactive-IntelliSense: tab completionWG-Interactive-PSReadline: PSReadline related issuesWG-Language: parser, language semanticsWG-Quality-Test: issues in a test or in test infrastructureWG-Remoting: PSRP issues with any transport layerWG-Security: security related areas such as JEA
Operating Systems
These are for issues that are specific to certain Operating Systems:
OS-LinuxOS-macOSOS-WindowsOS-WSL: Windows Subsystem for Linux
Process Tags
The following labels are used on PRs:
Review - Needed: The PR is being reviewed. Please see Pull Request - Code ReviewReview - Waiting on Author: The PR was reviewed by the team and requires changes or comments from the author before being accepted.Review - Abandoned: The PR was not updated for a significant number of days (the exact number could vary over time).
Maintainers should look into such PRs and re-evaluate them.Review - Committee: The PR/Issue needs a review from powershell-committee
Miscellaneous labels
Blocked: An issue cannot be addressed due to external factors,
but should not be closed because those external factors are temporary.BVT/DRT: An issue affecting or exposed by tests that have not been open sourced.Changelog Needed: The PR requires an addition to the changelog,
and should be removed when it has been added.Committee-Reviewed: The PR/Issue has been reviewed by the powershell-committeeCompliance: Issues with the compliance label are required to be fixed either in the long term or short term for
Microsoft to continue to sign and release packages from the project as official Microsoft packages.
The time frame in which it needs to be fixed should be identified in the issue.Documentation Needed: The PR has changes that require a documentation change or new documentation added to PowerShell-DocsFirst-Time-Issue: An issue that is identified as being easy and a good candidate for first time contributorsHackathonorHacktoberfest: An issue that would be a good candidate for hackathons such asHacktoberfestorHackillinoisPorting: An issue that affects a feature not yet ported to other platforms.Up-for-Grabs: We've acknowledged the issue but have no immediate plans to address it.
If you're looking for a way to contribute, these issues can be a good place to start.Usability: This label is used to help us filter issues that might be higher priority
because they more directly affect the usability of a particular feature or area.Waiting - DotNetCore: An issue waiting on a fix/change in DotNetCore.
Maintainers Releasing (`docs/maintainers/releasing.md`)
PowerShell Core Releasing Process
Release Steps
When creating a release milestone, you should send meeting invites to maintainers to book the release day and the previous day.
This is to make sure they have enough time to work on the release.
The following release procedure should be started on the previous day of the target release day.
This is to make sure we have enough buffer time to publish the release on the target day.
Before starting the following release procedure, open an issue and list all those steps as to-do tasks.
Check the task when you finish one.
This is to help track the release preparation work.
Note: Step 2, 3 and 4 can be done in parallel.
- Create a branch named
release-<Release Tag>in our private repository.
All release related changes should happen in this branch. - Prepare packages
- Build release packages.
- Sign the MSI packages and DEB/RPM packages.
- Install and verify the packages.
- Update documentation, scripts and Dockerfiles
- Summarize the changelog for the release. It should be reviewed by PM(s) to make it more user-friendly.
- Update CHANGELOG.md with the finalized changelog draft.
- Update other documents and scripts to use the new package names and links.
- Verify the release Dockerfiles.
- Create NuGet packages and publish them to powershell-core feed.
- Create the release tag and push the tag to
PowerShell/PowerShellrepository. - Create the draft and publish the release in GitHub.
- Merge the
release-<Release Tag>branch tomasterinpowershell/powershelland delete therelease-<Release Tag>branch. - Publish Linux packages to Microsoft YUM/APT repositories.
- Update the homebrew formula for the macOS package.
This task usually will be taken care of by the community,
so we can wait for one day or two and see if the homebrew formula has already been updated,
and only do the update if it hasn't.
Building Packages
Note: Linux and Windows packages are taken care of by our release build pipeline in VSTS,
while the macOS package needs to be built separately on a macOS.
The release build should be started based on the release branch.
The release Git tag won't be created until all release preparation tasks are done,
so the to-be-used release tag should be passed to the release build as an argument.
When creating the packages, please ensure that the file path does not contain user names.
That is, clone to/PowerShellon Unix, andC:\PowerShellfor Windows.
The debug symbols include the absolute path to the sources when built,
so it should appear/PowerShell/src/powershell/System.Management.Automation,
not/home/username/src/PowerShell/....
Packaging Overview
The tools/packaging module contains a Start-PSPackage function to build packages.
It requires that PowerShell Core has been built via Start-PSBuild from the build.psm1 module.
Windows
Start-PSPackage supports creating ZIP and MSIX packages for Windows.
When called without -Type on Windows, it defaults to creating both ZIP and MSIX packages.
The packages must be published in release mode,
so make sure -Configuration Release is specified when running Start-PSBuild.
Note that PowerShell is always self-contained, thus using it does not require installing it.
The output of Start-PSBuild includes a pwsh.exe executable which can simply be launched.
Linux / macOS
The Start-PSPackage function delegates to New-UnixPackage.
For Linux (Debian-based distributions), it relies on the Effing Package Management project,
which makes building packages a breeze.
For macOS, it uses native packaging tools (pkgbuild and productbuild) from Xcode Command Line Tools,
eliminating the need for Ruby or fpm.
For Linux (Red Hat-based distributions), it uses rpmbuild directly.
The PowerShell man-page is generated from the Markdown-like fileassets/pwsh.1.ronn using Ronn.
The function Start-PSBootstrap -Package will install these tools.
To modify any property of the packages, edit the New-UnixPackage function.
Please also refer to the function for details on the package properties
(such as the description, maintainer, vendor, URL,
license, category, dependencies, and file layout).
Note that the only configuration on Linux and macOS is
Linux,
which is release (i.e. not debug) configuration.
To support side-by-side Unix packages, we use the following design:
We will maintain a powershell package
which owns the /usr/bin/pwsh symlink,
is the latest version, and is upgradeable.
This is the only package named powershell
and similarly is the only package owning any symlinks,
executables, or man-pages named powershell.
Until we have a package repository,
this package will contain actual PowerShell bits
(i.e. it is not a meta-package).
These bits are installed to /opt/microsoft/powershell/6.0.0-alpha.8/,
where the version will change with each update
(and is the prerelease version).
On macOS, the prefix is /usr/local,
instead of /opt/microsoft because it is derived from BSD.
When we have access to package repositories where dependencies can be properly resolved,
thispowershellpackage can become a meta-package which auto-installs the latest package,
and so only owns the symlink.
For explicitly versioned packages, say for PowerShell 6.0,
we will maintain separate packages named in the form powershell6.0,
which owns the binary powershell6.0, the symlink powershell6.0,
the man-page powershell6.0,
and is installed to /opt/microsoft/powershell/6.0/.
Specifically this package owns nothing named powershell,
as only the powershell package owns those files.
This package is upgradeable, but should only be updated with hot-fixes.
This is a necessary consequence of Unix package managers,
as files among packages cannot conflict.
From a user-experience perspective,
if the user requires a specific version of PowerShell,
they should not be required to use an absolute path,
and instead should be given a binary with the version in the name.
This pattern is followed by many other languages
(Python being the most obvious example).
This same pattern can be followed for versions 6.1, 7.0, etc.,
and can be used for patch version (e.g. 6.0.1).
Use Start-PSPackage -Name powershell6.0 to generate
the versioned powershell6.0 package.
Without -Name specified, the primary powershell
package will instead be created.
Build and Packaging Examples
On macOS or a supported Linux distro, run the following commands:
# Install dependencies
Start-PSBootstrap -Package
# Build for v6.0.0-beta.1 release
Start-PSBuild -Clean -Crossgen -PSModuleRestore -ReleaseTag v6.0.0-beta.1
# Create package for v6.0.0-beta.1 release
Start-PSPackage -ReleaseTag v6.0.0-beta.1On Windows, the -Runtime parameter should be specified for Start-PSBuild to indicate what version of OS the build is targeting.
# Install dependencies
Start-PSBootstrap -Package
# Build for v6.0.0-beta.1 release targeting Windows universal package, set -Runtime to win7-x64
Start-PSBuild -Clean -CrossGen -PSModuleRestore -Runtime win7-x64 -Configuration Release -ReleaseTag v6.0.0-beta.1# Create packages for v6.0.0-beta.1 release targeting Windows universal package.
# 'win7-x64' / 'win7-x86' should be used for -WindowsRuntime.
Start-PSPackage -Type zip -ReleaseTag v6.0.0-beta.1 -WindowsRuntime 'win7-x64'
Start-PSPackage -Type msix -ReleaseTag v6.0.0-beta.1 -WindowsRuntime 'win7-x64'NuGet Packages
The NuGet packages for hosting PowerShell for Windows and non-Windows are being built-in our release build pipeline.
The assemblies from the individual Windows and Linux builds are consumed and packed into NuGet packages.
These are then released to powershell-core feed.
Release Tag
PowerShell releases use Semantic Versioning.
Until we hit 6.0, each sprint results in a bump to the build number,
so v6.0.0-alpha.7 goes to v6.0.0-alpha.8.
When a particular commit is chosen as a release,
we create an annotated tag that names the release.
An annotated tag has a message (like a commit),
and is not the same as a lightweight tag.
Create one with git tag -a v6.0.0-alpha.7 -m <message-here>,
and use the release changelogs as the message.
Our convention is to prepend the v to the semantic version.
The summary (first line) of the annotated tag message should be the full release title,
e.g. 'v6.0.0-alpha.7 release of PowerShellCore'.
When the annotated tag is finalized, push it with git push --tags.
GitHub will see the tag and present it as an option when creating a new release.
Start the release, use the annotated tag's summary as the title,
and save the release as a draft while you upload the binary packages.
Homebrew
After the release, update homebrew formula.
You need macOS to do it.
There are 2 homebrew formulas: main and preview.
Main
Update it on stable releases.
- Wait for a PR to show up in https://github.com/powershell/homebrew-tap, review and merge it.
Preview
Update it on preview releases.
- Wait for a PR to show up in https://github.com/powershell/homebrew-tap, review and merge it.
Testing Guidelines PowerShellCoreTestStatus (`docs/testing-guidelines/PowerShellCoreTestStatus.md`)
PowerShell Core Test Status
Testing has been a major part of PowerShell since its inception and PowerShell Core is no exception to that. We will continue to add tests to the repository over time, but we did not want to wait for the
Test Information
Here are some statistics about our current test coverage:
- More than 3000 tests have been created and are part of the repository
- More than 2200 tests are run as part of our CI system
- More than 1000 tests have been created to validate the PowerShell Core Engine and Language
- More than 1200 tests have been created to validate the PowerShell Core cmdlets
PowerShell Cmdlets
The follow table represents the test coverage of the PowerShell Core Cmdlets in relation to the delivery platform as of 2016-08-17:
| Name | Linux | Windows | Test Coverage |
|---|---|---|---|
| Get-Acl | delivered | ||
| Set-Acl | delivered | ||
| Export-Alias | delivered | delivered | yes |
| Get-Alias | delivered | delivered | adhoc |
| Import-Alias | delivered | delivered | yes |
| New-Alias | delivered | delivered | yes |
| Set-Alias | delivered | delivered | yes |
| Compress-Archive | delivered | delivered | yes |
| Expand-Archive | delivered | delivered | yes |
| Register-ArgumentCompleter | delivered | delivered | yes |
| Get-AuthenticodeSignature | delivered | ||
| Set-AuthenticodeSignature | delivered | ||
| Export-BinaryMiLog | delivered | ||
| Import-BinaryMiLog | delivered | ||
| Get-ChildItem | delivered | delivered | yes |
| Get-CimAssociatedInstance | delivered | ||
| Get-CimClass | delivered | yes | |
| Register-CimIndicationEvent | delivered | ||
| Get-CimInstance | delivered | ||
| New-CimInstance | delivered | ||
| Remove-CimInstance | delivered | ||
| Set-CimInstance | delivered | ||
| Invoke-CimMethod | delivered | ||
| Get-CimSession | delivered | ||
| New-CimSession | delivered | ||
| Remove-CimSession | delivered | ||
| New-CimSessionOption | delivered | ||
| Export-Clixml | delivered | delivered | yes |
| Import-Clixml | delivered | delivered | yes |
| Find-Command | delivered | delivered | adhoc |
| Get-Command | delivered | delivered | yes |
| Invoke-Command | delivered | delivered | yes |
| Measure-Command | delivered | delivered | yes |
| Rename-Computer | delivered | ||
| Restart-Computer | delivered | ||
| Stop-Computer | delivered | ||
| Get-ComputerInfo | delivered | ||
| Test-Connection | delivered | ||
| Add-Content | delivered | delivered | yes |
| Clear-Content | delivered | delivered | yes |
| Get-Content | delivered | delivered | yes |
| Set-Content | delivered | delivered | yes |
| Get-Credential | delivered | delivered | adhoc |
| ConvertFrom-Csv | delivered | delivered | yes |
| ConvertTo-Csv | delivered | delivered | yes |
| Export-Csv | delivered | delivered | yes |
| Import-Csv | delivered | delivered | yes |
| Get-Culture | delivered | delivered | yes |
| Format-Custom | delivered | delivered | yes |
| Get-Date | delivered | delivered | yes |
| Set-Date | delivered | delivered | |
| Write-Debug | delivered | delivered | yes |
| Wait-Debugger | delivered | delivered | |
| Out-Default | delivered | delivered | adhoc |
| Find-DscResource | delivered | delivered | |
| Register-EngineEvent | delivered | delivered | yes |
| Write-Error | delivered | delivered | yes |
| Get-Event | delivered | delivered | yes |
| New-Event | delivered | delivered | yes |
| Remove-Event | delivered | delivered | yes |
| Unregister-Event | delivered | delivered | yes |
| Wait-Event | delivered | delivered | yes |
| Get-EventSubscriber | delivered | delivered | yes |
| Get-ExecutionPolicy | delivered | delivered | yes |
| Set-ExecutionPolicy | delivered | delivered | yes |
| Invoke-Expression | delivered | delivered | yes |
| Out-File | delivered | delivered | yes |
| Unblock-File | delivered | ||
| New-FileCatalog | delivered | yes | |
| Test-FileCatalog | delivered | yes | |
| Get-FileHash | delivered | delivered | yes |
| Export-FormatData | delivered | delivered | yes |
| Get-FormatData | delivered | delivered | yes |
| Update-FormatData | delivered | delivered | yes |
| New-Guid | delivered | delivered | yes |
| Get-Help | delivered | delivered | yes |
| Save-Help | delivered | delivered | adhoc |
| Update-Help | delivered | delivered | adhoc |
| Format-Hex | delivered | delivered | yes |
| Add-History | delivered | delivered | adhoc |
| Clear-History | delivered | delivered | adhoc |
| Get-History | delivered | delivered | adhoc |
| Invoke-History | delivered | delivered | adhoc |
| Get-Host | delivered | delivered | yes |
| Out-Host | delivered | delivered | yes |
| Read-Host | delivered | delivered | adhoc |
| Write-Host | delivered | delivered | yes |
| Write-Information | delivered | delivered | adhoc |
| Get-InstalledModule | delivered | delivered | yes |
| Get-InstalledScript | delivered | delivered | |
| Clear-Item | delivered | delivered | adhoc |
| Copy-Item | delivered | delivered | yes |
| Get-Item | delivered | delivered | yes |
| Invoke-Item | delivered | delivered | yes |
| Move-Item | delivered | delivered | adhoc |
| New-Item | delivered | delivered | yes |
| Remove-Item | delivered | delivered | yes |
| Rename-Item | delivered | delivered | adhoc |
| Set-Item | delivered | delivered | adhoc |
| Clear-ItemProperty | delivered | delivered | adhoc |
| Copy-ItemProperty | delivered | delivered | adhoc |
| Get-ItemProperty | delivered | delivered | yes |
| Move-ItemProperty | delivered | delivered | |
| New-ItemProperty | delivered | delivered | adhoc |
| Remove-ItemProperty | delivered | delivered | adhoc |
| Rename-ItemProperty | delivered | delivered | adhoc |
| Set-ItemProperty | delivered | delivered | yes |
| Get-ItemPropertyValue | delivered | delivered | |
| Debug-Job | delivered | delivered | adhoc |
| Get-Job | delivered | delivered | adhoc |
| Receive-Job | delivered | delivered | yes |
| Remove-Job | delivered | delivered | adhoc |
| Start-Job | delivered | delivered | yes |
| Stop-Job | delivered | delivered | adhoc |
| Wait-Job | delivered | delivered | adhoc |
| ConvertFrom-Json | delivered | delivered | yes |
| ConvertTo-Json | delivered | delivered | yes |
| Format-List | delivered | delivered | yes |
| Get-LocalGroup | delivered | ||
| New-LocalGroup | delivered | ||
| Remove-LocalGroup | delivered | ||
| Rename-LocalGroup | delivered | ||
| Set-LocalGroup | delivered | ||
| Add-LocalGroupMember | delivered | ||
| Get-LocalGroupMember | delivered | ||
| Remove-LocalGroupMember | delivered | ||
| Import-LocalizedData | delivered | delivered | yes |
| Disable-LocalUser | delivered | ||
| Enable-LocalUser | delivered | ||
| Get-LocalUser | delivered | ||
| New-LocalUser | delivered | ||
| Remove-LocalUser | delivered | ||
| Rename-LocalUser | delivered | ||
| Set-LocalUser | delivered | ||
| Get-Location | delivered | delivered | yes |
| Pop-Location | delivered | delivered | yes |
| Push-Location | delivered | delivered | yes |
| Set-Location | delivered | delivered | yes |
| Get-LogProperties | delivered | ||
| Set-LogProperties | delivered | ||
| Add-Member | delivered | delivered | yes |
| Get-Member | delivered | delivered | yes |
| Find-Module | delivered | delivered | adhoc |
| Get-Module | delivered | delivered | yes |
| Import-Module | delivered | delivered | yes |
| Install-Module | delivered | delivered | adhoc |
| New-Module | delivered | delivered | yes |
| Publish-Module | delivered | delivered | |
| Remove-Module | delivered | delivered | yes |
| Save-Module | delivered | delivered | adhoc |
| Uninstall-Module | delivered | delivered | |
| Update-Module | delivered | delivered | |
| New-ModuleManifest | delivered | delivered | yes |
| Test-ModuleManifest | delivered | delivered | |
| Update-ModuleManifest | delivered | delivered | |
| Export-ModuleMember | delivered | delivered | yes |
| Out-Null | delivered | delivered | yes |
| Compare-Object | delivered | delivered | yes |
| ForEach-Object | delivered | delivered | yes |
| Group-Object | delivered | delivered | yes |
| Measure-Object | delivered | delivered | yes |
| New-Object | delivered | delivered | yes |
| Select-Object | delivered | delivered | yes |
| Sort-Object | delivered | delivered | yes |
| Tee-Object | delivered | delivered | yes |
| Where-Object | delivered | delivered | yes |
| Register-ObjectEvent | delivered | delivered | yes |
| Write-Output | delivered | delivered | yes |
| Find-Package | delivered | delivered | yes |
| Get-Package | delivered | delivered | yes |
| Install-Package | delivered | delivered | yes |
| Save-Package | delivered | delivered | yes |
| Uninstall-Package | delivered | delivered | yes |
| Find-PackageProvider | delivered | delivered | yes |
| Get-PackageProvider | delivered | delivered | yes |
| Import-PackageProvider | delivered | delivered | yes |
| Install-PackageProvider | delivered | delivered | yes |
| Get-PackageSource | delivered | delivered | yes |
| Register-PackageSource | delivered | delivered | yes |
| Set-PackageSource | delivered | delivered | yes |
| Unregister-PackageSource | delivered | delivered | yes |
| Convert-Path | delivered | delivered | adhoc |
| Join-Path | delivered | delivered | yes |
| Resolve-Path | delivered | delivered | adhoc |
| Split-Path | delivered | delivered | yes |
| Test-Path | delivered | delivered | yes |
| Import-PowerShellDataFile | delivered | delivered | yes |
| Debug-Process | delivered | delivered | |
| Get-Process | delivered | delivered | yes |
| Start-Process | delivered | delivered | yes |
| Stop-Process | delivered | delivered | yes |
| Wait-Process | delivered | delivered | |
| Write-Progress | delivered | delivered | yes |
| Disable-PSBreakpoint | delivered | delivered | |
| Enable-PSBreakpoint | delivered | delivered | |
| Get-PSBreakpoint | delivered | delivered | yes |
| Remove-PSBreakpoint | delivered | delivered | yes |
| Set-PSBreakpoint | delivered | delivered | yes |
| Get-PSCallStack | delivered | delivered | adhoc |
| Set-PSDebug | delivered | delivered | yes |
| Get-PSDrive | delivered | delivered | yes |
| New-PSDrive | delivered | delivered | yes |
| Remove-PSDrive | delivered | delivered | yes |
| Enter-PSHostProcess | delivered | delivered | yes |
| Exit-PSHostProcess | delivered | delivered | yes |
| Get-PSHostProcessInfo | delivered | delivered | fails on Linux |
| Get-PSProvider | delivered | delivered | yes |
| Get-PSReadlineKeyHandler | delivered | delivered | yes |
| Remove-PSReadlineKeyHandler | delivered | delivered | |
| Set-PSReadlineKeyHandler | delivered | delivered | yes |
| Get-PSReadlineOption | delivered | delivered | yes |
| Set-PSReadlineOption | delivered | delivered | yes |
| Get-PSRepository | delivered | delivered | |
| Register-PSRepository | delivered | delivered | |
| Set-PSRepository | delivered | delivered | |
| Unregister-PSRepository | delivered | delivered | |
| New-PSRoleCapabilityFile | delivered | delivered | |
| Connect-PSSession | delivered | delivered | adhoc |
| Disconnect-PSSession | delivered | delivered | adhoc |
| Enter-PSSession | delivered | delivered | adhoc |
| Exit-PSSession | delivered | delivered | adhoc |
| Get-PSSession | delivered | delivered | adhoc |
| New-PSSession | delivered | delivered | yes |
| Receive-PSSession | delivered | delivered | adhoc |
| Remove-PSSession | delivered | delivered | yes |
| Get-PSSessionCapability | delivered | delivered | |
| Disable-PSSessionConfiguration | delivered | delivered | |
| Enable-PSSessionConfiguration | delivered | delivered | |
| Get-PSSessionConfiguration | delivered | delivered | |
| Register-PSSessionConfiguration | delivered | delivered | yes |
| Set-PSSessionConfiguration | delivered | delivered | |
| Unregister-PSSessionConfiguration | delivered | delivered | yes |
| New-PSSessionConfigurationFile | delivered | delivered | yes |
| Test-PSSessionConfigurationFile | delivered | delivered | |
| New-PSSessionOption | delivered | delivered | |
| Disable-PSTrace | delivered | ||
| Enable-PSTrace | delivered | ||
| New-PSTransportOption | delivered | delivered | |
| Disable-PSWSManCombinedTrace | delivered | ||
| Enable-PSWSManCombinedTrace | delivered | ||
| Get-Random | delivered | delivered | yes |
| Invoke-RestMethod | delivered | delivered | yes |
| Find-RoleCapability | delivered | delivered | |
| Debug-Runspace | delivered | delivered | |
| Get-Runspace | delivered | delivered | yes |
| Disable-RunspaceDebug | delivered | delivered | |
| Enable-RunspaceDebug | delivered | delivered | |
| Get-RunspaceDebug | delivered | delivered | yes |
| Find-Script | delivered | delivered | |
| Install-Script | delivered | delivered | |
| Publish-Script | delivered | delivered | |
| Save-Script | delivered | delivered | |
| Uninstall-Script | delivered | delivered | |
| Update-Script | delivered | delivered | |
| New-ScriptFileInfo | delivered | delivered | |
| Test-ScriptFileInfo | delivered | delivered | |
| Update-ScriptFileInfo | delivered | delivered | |
| ConvertFrom-SddlString | delivered | ||
| ConvertFrom-SecureString | delivered | delivered | yes |
| ConvertTo-SecureString | delivered | delivered | yes |
| Get-Service | delivered | ||
| New-Service | delivered | ||
| Restart-Service | delivered | ||
| Resume-Service | delivered | ||
| Set-Service | delivered | ||
| Start-Service | delivered | ||
| Stop-Service | delivered | ||
| Suspend-Service | delivered | ||
| Start-Sleep | delivered | delivered | yes |
| Set-StrictMode | delivered | delivered | yes |
| Out-String | delivered | delivered | yes |
| Select-String | delivered | delivered | yes |
| ConvertFrom-StringData | delivered | delivered | yes |
| Format-Table | delivered | delivered | yes |
| New-TemporaryFile | delivered | delivered | yes |
| New-TimeSpan | delivered | delivered | yes |
| Get-TimeZone | delivered | yes | |
| Set-TimeZone | delivered | yes | |
| Start-Trace | delivered | ||
| Stop-Trace | delivered | ||
| Start-Transcript | delivered | delivered | yes |
| Stop-Transcript | delivered | delivered | yes |
| Add-Type | delivered | delivered | yes |
| Get-TypeData | delivered | delivered | yes |
| Remove-TypeData | delivered | delivered | yes |
| Update-TypeData | delivered | delivered | yes |
| Get-UICulture | delivered | delivered | yes |
| Get-Unique | delivered | delivered | yes |
| Clear-Variable | delivered | delivered | yes |
| Get-Variable | delivered | delivered | yes |
| New-Variable | delivered | delivered | yes |
| Remove-Variable | delivered | delivered | yes |
| Set-Variable | delivered | delivered | yes |
| Write-Verbose | delivered | delivered | yes |
| Write-Warning | delivered | delivered | yes |
| Invoke-WebRequest | delivered | delivered | yes |
| Format-Wide | delivered | delivered | yes |
| Get-WinEvent | delivered | yes | |
| New-WinEvent | delivered | ||
| Connect-WSMan | delivered | ||
| Disconnect-WSMan | delivered | ||
| Test-WSMan | delivered | ||
| Invoke-WSManAction | delivered | ||
| Disable-WSManCredSSP | delivered | ||
| Enable-WSManCredSSP | delivered | ||
| Get-WSManCredSSP | delivered | ||
| Get-WSManInstance | delivered | ||
| New-WSManInstance | delivered | ||
| Remove-WSManInstance | delivered | ||
| Set-WSManInstance | delivered | ||
| Set-WSManQuickConfig | delivered | ||
| New-WSManSessionOption | delivered | ||
| Disable-WSManTrace | delivered | ||
| Enable-WSManTrace | delivered | ||
| ConvertTo-Xml | delivered | delivered | yes |
| Select-xml | delivered | delivered | yes |
Testing Guidelines TestRoadmap (`docs/testing-guidelines/TestRoadmap.md`)
Overview
While we have done a fairly good job providing adequate coverage for the highest priority areas of PowerShell Core, we still have lots of gaps when we compare what is available in the OSS project vs what we have available as part of the Windows release test infrastructure.
This is to be expected as that infrastructure was built up over more than a decade.
It is not clear, however, how many of the current tests actually provide value, and how much duplication we have.
In any event, there is still a large difference in what we have in PowerShell Core and our older tests.
Data Visibility
In both PowerShell Core or our proprietary tests we can't actually determine how and what cmdlets and engine functions (language/scripting/debugging/remoting) are being tested because we do not gather any usage telemetry on what our tests are doing during test execution.
We have no way via automation to determine how thoroughly we are testing our code except by inspecting the actual tests or looking at code coverage data (which isn't gathered often and only available on full PowerShell).
Further, this doesn't provide us data with regard to the test quality, only that we've covered a specific block of code.
The areas of greatest risk are those where we have no data at all, by collecting coverage data we can illuminate those code paths that are not being used and fill those gaps with new tests.
Telemetry and Logging
While we have some telemetry feeds on Windows, we have no telemetry capabilities on non-Windows platforms.
We should enable telemetry on PowerShell Core on all platforms and review the usage data as soon as possible.
This will provide us much needed visibility in how PowerShell Core is being used and can help us identify areas we should track more closely.
We already have infrastructure in place to allow us see how PowerShell Core is being used, by collecting telemetry from PowerShell Core, we can improve our confidence as we drive to production quality.
Logging
The code which on Windows create ETW logging has been completely stubbed out on Linux/macOS.
We should take advantage of the native logging mechanisms on Linux/macOS and implement a logger similar to the ETW logger on Windows using Syslog (or equivalent).
We could use this data during test runs to identify test gaps.
Simply by capturing the cmdlets and their parameters which are invoked during test would illuminate the gaps we have in our current tests, and allow us to easily fill them.
It is not sufficient to support only one platform because we have many tests which determine at runtime whether or not it should run based on OS, so data from Windows will not be the same as that from Linux or MacOS.
We can also determine engine coverage gaps by measuring test of operators and other language elements.
Without that data, we are simply shooting in the dark.
Code coverage
Even in our lab (STEX) environment we run tests to get code coverage data irregularly, and PowerShell Core has no tools to gather code coverage data.
We need to investigate possible solutions for code coverage on PowerShell Core.
There are a small number of solutions available:
- OpenCover
- OpenCover is currently used by corefx to produce code coverage data, also visualization is available via CoverAlls.
We should investigateOpenCoverand determine it's feasibility for us.
Unfortunately I haven't been able to find a solution for Linux
- OpenCover is currently used by corefx to produce code coverage data, also visualization is available via CoverAlls.
- DotCover
- I have contacted
JetBrainsto see if they have any solutions which may be used with .NET Core.
- I have contacted
If we can get code coverage on PowerShell Core on Windows, we would at least be able to have some data to illuminate our test gaps.
Running code coverage more often on full PowerShell is something that we should consider, if it will help us close existing test coverage gaps, but the issue is not test coverage for full PowerShell, but rather PowerShell Core where we have only a small percentage of tests in comparison.
Daily Test Runs
We currently run only those tests which are tagged CI excluding the tag SLOW as part of our continuous integration systems.
This means roughly 1/3rd of our github tests are not being run on any regular schedule.
In order to provide us with higher confidence in our code, we should be running ALL of our tests on a regular basis.
However, running the tests is only the first step, we need an easy way to be notified of test failures, and to track progress of those runs over time.
Tracking this over time affords us the ability to see how our test count increases, implying an improvement in coverage.
It also provides us mechanism whereby we can see trends in instability.
Pending and Skipped Tests
We currently have approximately 300 tests which are marked either as skipped or pending.Pending tests represent those tests which should be run but are not currently being executed, usually because the underlying functionality is not present.
Over time, the number of Pending tests should drive to zero, and we should be tracking the list of tests which are marked pending and track those to be sure that they do not spend too long in this state.Skipped tests due to platform applicability are certainly valid, and it is important that we track how many tests are skipped and for what reason.
A test which is skipped for all platforms should be considered carefully as to whether it should exist at all.
In either case, pending or skipped tests should be tracked over time so we can determine whether we are improving our ability to measure quality.
In the best case, the total number of tests is the same across all platforms.
The count of Skipped/Pending tests would naturally be different as not all tests will be applicable to every platform, but if we can have consistency of test count across platforms, it will be easier to compare test results on one platform vs another.
Remoting Considerations
Given that one of the targeted scenarios is to manage heterogeneous environments remotely, we need a test environment which encompasses the available platforms and protocols.
Our current test infrastructure does not have comprehensive support for remote testing except for loopback operations.
We need a matrix of protocols and platforms to ensure that we have adequate coverage to ensure quality.
In addition to loopback tests using both WSMan and SSH protocols, we should have the following minimum matrix of client/server connections for both WSMan and SSH (where available) protocols.
- Windows Client->Nano Server
- Windows Client->Linux Server
- Linux Client -> Windows Server
- macOS Client -> Nano Client
- PowerShell Core Client -> Full PowerShell Server
- Full PowerShell Client -> PowerShell Core Server
- Downlevel Full PowerShell Client -> PowerShell Core Server
Challenges
- On Windows, we have cmdlets to enable and disable remote access, those cmdlets do not exist on non-Windows platforms (they rely on configuration stored in the registry), nor do we support configuration for the SSH protocol.
We need to be sure that we can easily enable remoting for the non-Windows platforms and support both WSMan and SSH configurations. - Our current multi-machine tests do not test the connection code, they simply execute test code remotely and retrieve results and assume a good connection.
The infrastructure used for these tests is STEX which is not an open environment.
We will need to create automation to create and configure the test systems in the test matrix and then invoke tests on them.
It is not clear that our current CI systems can accommodate our needs here as Azure DevOps can supply us with all of the OS images needed.
We may need to create our own heterogeneous environment in Azure, or look to other teams (MS Build Lab/Jenkins) for assistance.
We need to investigate whether there are solutions available, and if not, design/implement an environment to meet our needs.
Reporting
Currently, we report against the simplest of KPI:
- is the CI build error free (which is part of the PR/Merge process - and reported on our landing page)
There are a number of KPIs which we could report on:
- Code KPIs
- What is the coverage (% blocks covered) of our
CItests - What is the coverage of all tests
- How long is the CI system taking
- build time
- test time
- What is the percentage of pull requests pass
CItests - What is the trend for
pendingtests
- What is the coverage (% blocks covered) of our
- Process KPIs
- What is the time delta between a posted PR (with no test errors) and merge
- How many revisions are needed before a PR is accepted
- How many PRs are rejected
- Product KPIs
- What is the usage of PowerShell Core on the various platforms
- What are the common operations performed by PowerShell Core (cmdlet usage)
- How many remote connections are created in a session
Reporting Priorities
- As a baseline, we should report code coverage on current Full PowerShell from the latest Windows release
- We should report on code coverage as soon as possible.
This is the tool that will enable us to at least determine where we have no data and illuminate test gaps. - We should track our CI execution time, which will allow us to keep an eye on our code submission workflow and how much our developers are waiting for builds
Public Dashboard
Because we collect the test results as a build artifact, it is possible to collect and collate this data and provide a number of dash-board reports.
Since we are an OSS project, it makes sense that our reports should be also public.
A public dashboard provides evidence that we are not collecting PII, increasing trust in the project and shows that we are using data to drive product decisions.
We could easily create a web presence in Azure which would enable us to provide reporting on the current and historical state of our tests.
Now that we are running all of our tests on a nightly basis, we should be communicating the results of those test runs.
PowerBI could be used to create the visualizations, which would reduce the time and effort.
In order to achieve this we need to:
- Designate an Azure instance to run services and populate Azure tables:
- Create tools to retrieve and transform git data
- Create tools to retrieve and transform the test data
- Create PowerBI queries to visualize our KPIs
Release Criteria
We must start defining the release criteria for a production ready release of PowerShell Core, as an initial proposal:
- No open issues for the release
- 80% code coverage of high use cmdlets (cmdlets used by 70% of users, as captured via telemetry)
- 90% code coverage of language elements (coverage error code paths may not be 100%)
- 60% code Coverage on Windows via Github tests
- 100% of our minimum remoting matrix tested
- Acceptance by 50% PowerShell MVPs (via Survey)
- Acceptance by Partners (via Survey)
A couple of assumptions have been made for this list:
- we actually know the high use cmdlets - this implies we have telemetry to determine our cmdlet use
- we have designated our Partners
Microsoft Release Process
If PowerShell Core is targeted to be released as part of Windows Server or Nano, we will have additional requirements which must be met with regard to acceptance by the Windows org of the PowerShell Core package.
This represents additional work which needs planning and allocated resources.
Replace STEX tests with PowerShell Core tests
Currently we have two distinct set of test artifacts, those used in PowerShell Core and our traditional lab (STEX) based tests.
We are creating new tests for PowerShell Core using the existing tests as guidance, but this is creating duplication between the two sets so we need to find ways to invoke our current PowerShell Core tests as part of our STEX based runs.
When we do this, we can start to delete our existing tests and reduce the maintenance burden due to duplicated tests.
Proposal
- Create a way to invoke our github based tests in lab runs
Historical Tests
The PowerShell team created roughly 90,000 tests since the beginning of the project starting in 2003.
Initially tests were created in a C# framework which was used internally at Microsoft.
After that, a number of script based frameworks were created to run script based tests.
We have decided not to release those for a number of reasons:
- All the frameworks have a core assumption of running in the Microsoft Lab environment and rely on internal Microsoft tools
- All the frameworks rely on specific logging file formats used by internal Microsoft reporting tools
- Some of the frameworks have limitations which we can avoid by recasting/recreating them as
PesterorxUnittests
While, these reasons are not insurmountable, releasing these tests with their custom frameworks does not help us with our desire to promulgate open solutions.
Further, by creating/releasing xUnit and Pester tests we can take the opportunity to review existing tests and improve them, or eliminate them if they are poor, or duplicate tests.
It is obvious that the review of more than 90,000 tests is an enormous undertaking, and will take some time, and we are committed to creating a set of tests which can provide confidence that PowerShell Core has the same quality as earlier releases of PowerShell.
Challenges
While there is a test gap between what is available on Github vs what is available via our internal proprietary tests, it may be possible to use our existing tests (and frameworks) against PowerShell Core.
This is an attractive potential because we have very high confidence of our current tests.
Moreover, we could also collect code coverage data during a test pass.
It would be expected to have a number of test failures because different functionality between PowerShell Core and Full PowerShell but collecting this data would enable us to enumerate those difference.
Unfortunately, the current logging mechanism used in our in-lab tests are not available in PowerShell Core, and a new logging mechanism would need to be created
Proposal
- Investigate the effort to run our historical tests in a PowerShell Core environment
Action Plan
This document represents a number of initiatives, all of which will take resources.
Below is my suggestion for prioritization to reduce risk and improve confidence in our overall quality:
- Implement telemetry feeds for PowerShell Core
- Implement data collection feeds from our CI systems
- Create a public dashboard to visualize our KPIs
- including data from 1, 2 above
- Implement Code Coverage on PowerShell Core
- Design/Implement remoting test infrastructure
- Replace in-lab tests with PowerShell Core tests
- Investigate feasibility of running current in-lab tests on PowerShell Core
These are tracked as issues
Testing Guidelines WritingPesterTests (`docs/testing-guidelines/WritingPesterTests.md`)
Writing Pester Tests
Note that this document does not replace the documents found in the Pester project.
This is just some quick tips and suggestions for creating Pester tests for this project.
The Pester community is vibrant and active, if you have questions about Pester or creating tests, the Pester Wiki has a lot of great information.
As of January 2018, PowerShell Core is using Pester version 4 which has some changes from earlier versions.
See Migrating from Pester 3 to Pester 4 for more information.
When creating tests, keep the following in mind:
- Tests should not be overly complicated and test too many things.
- Boil down your tests to their essence, test only what you need.
- Tests should be as simple as they can.
- Tests should generally not rely on any other test.
Examples
Here's the simplest of tests:
Describe "A variable can be assigned and retrieved" {
It "Creates a variable and makes sure its value is correct" {
$a = 1
$a | Should -Be 1
}
}If you need to do type checking, that can be done as well:
Describe "One is really one" {
It "Compare 1 to 1" {
$a = 1
$a | Should -Be 1
}
It "1 is really an int" {
$i = 1
$i | Should -BeOfType System.Int32
}
}If you are checking for proper errors, use the Should -Throw -ErrorId Pester syntax.
It checks against FullyQualifiedErrorId property, which is recommended because it does not change based on culture as an error message might.
...
It "Get-Item on a nonexisting file should have error PathNotFound" {
{ Get-Item "ThisFileCannotPossiblyExist" -ErrorAction Stop } | Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand"
}Note that if Get-Item were to succeed, the test will fail.
However, if you need to check the InnerException or other members of the ErrorRecord, you should use -PassThru parameter:
It "InnerException sample" {
$e = { Invoke-WebRequest https://expired.badssl.com/ } | Should -Throw -ErrorId "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" -PassThru
$e.Exception.InnerException.NativeErrorCode | Should -Be 12175
}Describe/Context/It
For creation of PowerShell tests, the Describe block is the level of granularity suggested and one of three tags should be used: CI, Feature, or Scenario.
If the tag is not provided, the build process will fail.
Describe
Creates a logical group of tests.
All Mocks and TestDrive contents defined within a Describe block are scoped to that Describe;
they will no longer be present when the Describe block exits.
A Describe block may contain any number of Context and It blocks.
Context
Provides logical grouping of It blocks within a single Describe block. Any Mocks defined inside a Context are removed at the end of the Context scope, as are any files or folders added to the TestDrive during the Context block's execution.
Any BeforeEach or AfterEach blocks defined inside a Context also only apply to tests within that Context.
It
The It block is intended to be used inside of a Describe or Context block. If you are familiar with the AAA pattern (Arrange-Act-Assert), the body of the It block is the appropriate location for an assert.
The convention is to assert a single expectation for each It block. The code inside of the It block should throw a terminating error if the expectation of the test is not met and thus cause the test to fail.
The name of the It block should expressively state the expectation of the test.
Admin privileges in tests
Tests that require admin privileges on Windows must be additionally marked with RequireAdminOnWindows Pester tag.
In the Azure DevOps Windows CI, we run two different passes:
- The pass with exclusion of
RequireAdminOnWindowstagged tests. - The pass where only
RequireAdminOnWindowstagged tests are being executed.
In each case, tests are executed with appropriate privileges.
Tests that need to be run with sudo on Unix systems must be additionally marked with RequireSudoOnUnix Pester tag.
RequireSudoOnUnix tag takes precedence over all other tags like CI, Feature, etc. (which are ignored when RequireSudoOnUnix is present).
Tests tagged with RequireSudoOnUnix will run as a separate pass for any Unix test.
Selected Features
Test Drive
A PSDrive is available for file activity during a test and this drive is limited to the scope of a single Describe block. The contents of the drive are cleared when a Context block is exited.
A test may need to work with file operations and validate certain types of file activities. It is usually desirable not to perform file activity tests that will produce side effects outside of an individual test.
Pester creates a PSDrive inside the user's temporary drive that is accessible via TestDrive: or $TestDrive. Pester will remove this drive after the test completes.
You may use this drive to isolate the file operations of your test to a temporary store.
The following example illustrates the feature:
function Add-Footer($path, $footer) {
Add-Content $path -Value $footer
}
Describe "Add-Footer" {
$testPath="TestDrive:\test.txt"
Set-Content $testPath -value "my test text."
Add-Footer $testPath "-Footer"
$result = Get-Content $testPath
It "adds a footer" {
(-join $result) | Should -BeExactly "my test text.-Footer"
}
}When this test completes, the contents of the TestDrive: will be removed.
Parameter Generation
$testCases = @(
@{ a = 0; b = 1; ExpectedResult = 1 }
@{ a = 1; b = 0; ExpectedResult = 1 }
@{ a = 1; b = 1; ExpectedResult = 0 }
@{ a = 0; b = 0; ExpectedResult = 0 }
)
Describe "A test" {
It "<a> -xor <b> should be <expectedresult>" -TestCases $testCases {
param ($a, $b, $ExpectedResult)
$a -xor $b | Should -Be $ExpectedResult
}
}Mocking
Mocks the behavior of an existing command with an alternate implementation. This creates new behavior for any existing command within the scope of a Describe or Context block.
The function allows you to specify a script block that will become the command's new behavior.
The following example illustrates simple use:
Context "Get-Random is not random" {
Mock Get-Random { return 3 }
It "Get-Random returns 3" {
Get-Random | Should -Be 3
}
}More information may be found here.
Free Code in a Describe block
Code execution in Pester can be very subtle and can cause issues when executing test code. The execution of code which lays outside of the usual code blocks may not happen as you expect. Consider the following:
Describe it {
Write-Host -For DarkRed "Before Context"
Context "subsection" {
Write-Host -for DarkRed "Before BeforeAll"
BeforeAll { write-host -for Blue "In Context BeforeAll" }
Write-Host -for DarkRed "After BeforeAll"
Write-Host -for DarkRed "Before AfterAll"
AfterAll { Write-Host -for Blue "In Context AfterAll" }
Write-Host -for DarkRed "After AfterAll"
BeforeEach { Write-Host -for Blue "In BeforeEach" }
AfterEach { Write-Host -for Blue "In AfterEach" }
Write-Host -for DarkRed "Before It"
It "should not be a surprise" {
1 | should -Be 1
}
Write-Host -for DarkRed "After It"
}
Write-Host -for DarkRed "After Context"
Write-Host -for DarkGreen "Before Describe BeforeAll"
BeforeAll { Write-Host -for DarkGreen "In Describe BeforeAll" }
AfterAll { Write-Host -for DarkGreen "In Describe AfterAll" }
}Now, when run, you can see the execution schedule:
PS# invoke-pester c:\temp\pester.demo.tests.ps1
Describing it
In Describe BeforeAll
Before Context
Context subsection
In Context BeforeAll
Before BeforeAll
After BeforeAll
Before AfterAll
After AfterAll
Before It
In BeforeEach
[+] should not be a surprise 79ms
In AfterEach
After It
In Context AfterAll
After Context
Before Describe BeforeAll
In Describe AfterAll
Tests completed in 79ms
Passed: 1 Failed: 0 Skipped: 0 Pending: 0The Describe - BeforeAll block is executed before any other code even though it was at the bottom of the Describe block.
So if some state is set elsewhere in the Describe block, that state will not yet be visible (as the code will not yet been run).
Notice, too, that the BeforeAll block in Context is executed before any other code in that block.
Generally, you should have code reside in one of the code block elements of BeforeAll, BeforeEach, AfterEach and/or AfterAll, especially if those blocks rely on some state set by free code elsewhere in the block.
Skipping Tests in Bulk
Sometimes it is beneficial to skip all the tests in a particular Describe block.
For example, tests which are not applicable to a platform could be skipped, and they would be reported as skipped.
The following is an example of how this may be done:
Describe "Should not run these tests on non-Windows platforms" {
BeforeAll {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
if ( ! $IsWindows ) {
$PSDefaultParameterValues["it:skip"] = $true
}
}
AfterAll {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
}
Context "Block 1" {
It "This block 1 test 1" {
1 | should -Be 1
}
It "This is block 1 test 2" {
1 | should -Be 1
}
}
Context "Block 2" {
It "This block 2 test 1" {
2 | should -Be 1
}
It "This is block 2 test 2" {
2 | should -Be 1
}
}
}Here is the output when run on a Linux distribution:
Describing Should not run these tests on non-Windows platforms
Context Block 1
[!] This block 1 test 1 691ms
[!] This is block 1 test 2 114ms
Context Block 2
[!] This block 2 test 1 73ms
[!] This is block 2 test 2 6msand here is the output when run on a Windows distribution:
Describing Should not run these tests on non-Windows platforms
Context Block 1
[+] This block 1 test 1 86ms
[+] This is block 1 test 2 33ms
Context Block 2
[-] This block 2 test 1 52ms
Expected: {1}
But was: {2}
22: 2 | should -Be 1
at <ScriptBlock>, <No file>: line 22
[-] This is block 2 test 2 77ms
Expected: {1}
But was: {2}
25: 2 | should -Be 1
at <ScriptBlock>, <No file>: line 25This technique uses the $PSDefaultParameterValues feature of PowerShell to temporarily set the It block parameter -skip to true (or in the case of Windows, it is not set at all)
Multi-line strings
You may want to have a test like:
It 'tests multi-line string' {
Get-MultiLineString | Should -Be @'
first line
second line
'@
}There are problems with using multi-line strings with verifying the output results.
The reason for it are line-ends.
They cause problems for two reasons:
- They are different on different platforms (
\r\non Windows and\non Unix). - Even on the same system, they depend on the way how the repo was cloned (local git configuration).
Particularly, in the default Azure DevOps CI Windows image, you will get \n line ends in all your files.
That causes problems, because at runtime Get-MultiLineString would likely produce \r\n line ends on Windows.
Some workaround could be added, but they are sub-optimal and make reading test code harder.
function normalizeEnds([string]$text)
{
$text -replace "`r`n?|`n", "`r`n"
}
It 'tests multi-line string' {
normalizeEnds (Get-MultiLineString) | Should -Be (normalizeEnds @'
first line
second line
'@)
}When appropriate, you can avoid creating multi-line strings at the first place.
These commands create an array of strings:
Get-ContentOut-String -Stream
Pester Do and Don't
Do
- Name your file
<descriptive_test_name>.tests.ps1. - Keep tests simple:
- Test only what you need.
- Reduce dependencies.
- Be sure to tag your
Describeblocks based on their purpose:- Tag
CIindicates that it will be run as part of the continuous integration process. These should be unit test like, and generally take less than a second. - Tag
Featureindicates a higher level feature test (we will run these on a regular basis), for example, tests which go to remote resources, or test broader functionality. - Tag
Scenarioindicates tests of integration with other features (these will be run on a less regular basis and test even broader functionality than feature tests.
- Tag
- Make sure that
Describe/Context/Itdescriptions are useful.- The error message should not be the place where you describe the test.
- Use
Contextto group tests.- Multiple
Contextblocks can help you group your test suite into logical sections.
- Multiple
- Use
BeforeAll/BeforeEach/AfterEach/AfterAllinstead of custom initiators. - Use
Should -Throw -ErrorIdto check for expected errors. - Use
-TestCaseswhen iterating over multipleItblocks. - Use code coverage functionality where appropriate.
- Use
Mockfunctionality when you don't have your entire environment. - Avoid free code in a
Describeblock.- Use
BeforeAll/BeforeEach/AfterEach/AfterAll. - See Free Code in a Describe block
- Use
- Avoid creating or using test files outside of
TESTDRIVE:.TESTDRIVE:has automatic clean-up.
- Keep in mind that we are creating cross platform tests.
- Avoid using the registry.
- Avoid using COM.
- Avoid being too specific about the count of a resource as these can change platform to platform.
- Example: Avoid checking for the count of loaded format files, but rather check for format data for a specific type.
Don't
- Don't have too many evaluations in a single
Itblock.- The first
Shouldfailure will stop that block.
- The first
- Don't use
Shouldoutside of anItBlock. - Don't use the word "Error" or "Fail" to test a positive case.
- Example: Rephrase the negative sentence
"Get-ChildItem TESTDRIVE: shouldn't fail"to the following positive case"Get-ChildItem should be able to retrieve file listing from TESTDRIVE".
- Example: Rephrase the negative sentence
Testing Guidelines Getting Code Coverage (`docs/testing-guidelines/getting-code-coverage.md`)
Getting Code Coverage Analysis for PowerShell
Note: Code coverage is currently only supported on Windows, since we use OpenCover.
The PowerShell code base is configured to build with code coverage support using OpenCover.
You can see the testing coverage of the current master branch build at any time at codecov.io.
To run test coverage analysis of PowerShell on your own branch/machine,
you will need to take the following steps
(and be aware that running the code coverage analysis can take as long as 8 hours).
Running tests with code coverage analysis
First: Open PowerShell in an elevated session.
OpenCover needs elevated privileges to work.
Now, in PowerShell:
# Go to your PowerShell build directory root
PS> Set-Location "C:\Path\to\powershell\build\dir"
# Import the PowerShell build module
PS> Import-Module .\build.psm1
# Build PowerShell. You may need to add other flags here like
# -ResGen or -Restore
PS> Start-PSBuild -Configuration CodeCoverage -Clean -PsModuleRestore
# Now ensure Pester is installed
PS> Restore-PSPester
# We also need to build the test executor
PS> Publish-PSTestTools
# Import the OpenCover module
PS> Import-Module $PWD\test\tools\OpenCover
# Install OpenCover to a temporary directory
PS> Install-OpenCover -TargetDirectory $env:TEMP -Force
# Finally, run the tests with code coverage analysis.
# If you want to run only the continuous integration tests,
# add -CIOnly, which will take less time
PS> Invoke-OpenCover -OutputLog coverage.xml -OpenCoverPath $env:TEMP\OpenCoverExamining the code coverage data
Once the code coverage test run is done, you'll want to examine the data:
# Collect the coverage data using Get-CodeCoverage from the OpenCover
# module that was imported above. This operation is generally expensive
# to compute, so worth storing in a variable
PS> $coverageData = Get-CodeCoverage .\coverage.xml
# Take a look at a summary of the results
PS> $coverageData.CoverageSummary
NumSequencePoints : 298237
VisitedSequencePoints : 125949
NumBranchPoints : 101477
VisitedBranchPoints : 39389
SequenceCoverage : 42.23
BranchCoverage : 38.82
MaxCyclomaticComplexity : 393
MinCyclomaticComplexity : 1
VisitedClasses : 1990
NumClasses : 3187
VisitedMethods : 15115
NumMethods : 32517
# You can also view results by assembly
PS> $coverageData.Assembly | Format-Table AssemblyName,Branch,Sequence
AssemblyName Branch Sequence
------------ ------ --------
pwsh 100 100
Microsoft.PowerShell.ConsoleHost 21.58 23.32
System.Management.Automation 41.22 45.01
Microsoft.PowerShell.CoreCLR.Eventing 1.88 2.03
Microsoft.PowerShell.Security 17.32 20.09
Microsoft.PowerShell.Commands.Utility 20.14 21.39
Microsoft.PowerShell.Commands.Management 43.05 43.39
Microsoft.WSMan.Management 52.58 56.98
Microsoft.WSMan.Runtime 80.95 80.33
Microsoft.PowerShell.Commands.Diagnostics 0 0If you have made changes to tests or code
and run a second code coverage run,
you can also compare code coverage results:
PS> $cov1 = Get-CodeCoverage ./coverage1.xml
PS> $cov2 = Get-CodeCoverage ./coverage2.xml
PS> Compare-CodeCoverage -Run1 $cov1 -Run2 $cov2
AssemblyName Sequence SequenceDelta Branch BranchDelta
------------ -------- ------------- ------ -----------
Microsoft.PowerShell.Security 20.09 -30.12 17.32 -31.63
Microsoft.PowerShell.Commands.Management 43.39 9.10 43.05 11.59
System.Management.Automation 45.04 -10.63 41.23 -11.07
Microsoft.PowerShell.Commands.Utility 21.39 -47.22 20.14 -46.47
Microsoft.PowerShell.Commands.Diagnostics 0 -51.91 0 -48.62
Microsoft.PowerShell.ConsoleHost 23.32 -22.28 21.58 -22.47
pwsh 100 0.00 100 0.00
Microsoft.WSMan.Management 57.73 48.23 53.02 43.22
Microsoft.WSMan.Runtime 80.33 -19.67 80.95 -19.05
Microsoft.PowerShell.CoreCLR.Eventing 2.03 -32.74 1.88 -26.01To get file-specific coverage data,
you can use Compare-FileCoverage:
PS> Compare-FileCoverage -ReferenceCoverage $cov2 -DifferenceCoverage $cov1 -FileName LanguagePrimitives.cs
FileName ReferenceCoverage DifferenceCoverage CoverageDelta
-------- ----------------- ------------------ -------------
LanguagePrimitives.cs 53.68 69.03 15.34You can see more ways to use Compare-CodeCoverage and Compare-FileCoverage
by running:
PS> Get-Help Compare-CodeCoverage -Full
# Or
PS> Get-Help Compare-FileCoverage -FullVisualizing code coverage
For a more detailed, graphical representation of the code coverage results,
you can use the ReportGenerator package.
This generates an HTML report of the coverage from the XML file
and will provide much more detail about the coverage analysis.
The package is available on NuGet,
and you can install and run it as follows:
# Install ReportGenerator
PS> Find-Package ReportGenerator `
>> -ProviderName Nuget `
>> -Source "https://nuget.org/api/v2" `
>> | Install-Package -Scope CurrentUser
# Get the ReportGenerator executable path
# Make sure use the appropriate version number in the path
$ReportGenExe = "$HOME\AppData\Local\PackageManagement\NuGet\Packages\ReportGenerator.<version>\tools\ReportGenerator.exe"
# Run ReportGenerator
& $ReportGenExe -report:coverage.xml -targetdir:C:\temp\Coverage
# Finally, open the report in your browser
Invoke-Item C:\temp\Coverage\index.htmThis should open a screen in the browser like this:
The main report, which is below the summary and risk hot spots, has
a filter functionality as well (when "Enable Filtering" is clicked on):
Testing Guidelines Testing Guidelines (`docs/testing-guidelines/testing-guidelines.md`)
Testing Guidelines
Testing is a critical and required part of the PowerShell project.
The Microsoft PowerShell team created nearly 100,000 tests over the last 12 years which we run as part of the release process for Windows PowerShell.
Having all of those tests available for the initial release of PowerShell was not feasible,
and we have targeted those tests which we believe will provide us the ability to catch regressions in the areas which have had the largest changes for PowerShell.
It is our intent to continue to release more and more of our tests until we have the coverage we need.
For creating new tests, please review the documents on how to create tests for PowerShell.
There is a best practices document for writing Pester tests.
When adding new tests, place them in the directories as outlined below.
CI System
We use Azure DevOps as a continuous integration (CI) system for Windows
and non-Windows platforms.
In the README.md at the top of the repository, you can see Azure CI badge.
It indicates the last build status of master branch.
Hopefully, it's green:
This badge is clickable; you can open corresponding build page with logs, artifacts, and tests results.
From there you can easily navigate to the build history.
Getting CI Results
CI System builds and runs tests on every pull request and provides quick feedback about it.
These green check boxes and red crosses are clickable as well.
They will bring you to the corresponding page with details.
Test Frameworks
Pester
Our script-based test framework is Pester.
This is the framework which we are using internally at Microsoft for new script-based tests,
and a large number of the tests which are part of the PowerShell project have been migrated from that test base.
Pester tests can be used to test most of PowerShell behavior (even some API operations can easily be tested in Pester).
Substantial changes were required, to get Pester executing on non-Windows systems.
These changes are not yet in the official Pester code base.
Some features of Pester may not be available or may have incorrect behavior.
Please make sure to create issues in PowerShell/PowerShell (not Pester) for anything that you find.
Test Tags
The Pester framework allows Describe blocks to be tagged, and our CI system relies on those tags to invoke our tests.
One of the following tags must be used:
CI- this tag indicates that the tests in theDescribeblock will be executed as part of the CI/PR processScenario- this tag indicates a larger scale test interacting with multiple areas of functionality and/or remote resources, these tests are also run daily.Feature- tests with this tag will not be executed as part of the CI/PR process,
but they will be executed on a daily basis as part of acrondriven build.
They indicate that the test will be validating more behavior,
or will be using remote network resources (ex: package management tests)
Additionally, the tag:
SLOWindicates that the test takes a somewhat longer time to execute (97% of ourCItests take 100ms or less), a test which takes longer than 1 second should be considered as a candidate for being taggedSlow
Requesting additional tests for a PR
In our CI systems, we normally run only run tests tagged with CI.
If in the first line of the last (most recent) commit description you add [Feature],
we will ensure that we will also run the tests tagged with Feature.
When you would want to do this:
- You have added or changed a
Featuretest. - A maintainer asks you to run the
Featuretests. - Based on experience, you are confident that a maintainer will ask you to run the
Featuretests.
Validating packaging changes for a PR
By default, our CI system does a build and run tests for a PR and does not exercise code to create a package.
If your PR includes changes to packaging, you can have the CI system exercise the packaging code by
using [Package] as the first line in the commit message.
When you would want to do this:
- You made change to PowerShell Core packaging
- A maintainer asks you to run as
[Package]
xUnit
For those tests which are not easily run via Pester, we have decided to use xUnit as the test framework.
Currently, we have a minuscule number of tests which are run by using xUnit.
Running tests outside of CI
When working on new features or fixes, it is natural to want to run those tests locally before making a PR.
These helper functions are part of the build.psm1 module to help with that:
Start-PSPesterwill execute all Pester tests which are run by the CI systemStart-PSxUnitwill execute the available xUnit tests run by the CI system
Our CI system runs these as well; there should be no difference between running these on your dev system, versus in CI.
When running tests in this way, be sure that you have started PowerShell with -noprofile as some tests will fail if the
environment is not the default or has any customization.
For example, to run all the Pester tests for CI (assuming you are at the root of the PowerShell repo):
Import-Module ./build.psm1
Start-PSPesterIf you wish to run specific tests, that is possible as well:
Start-PSPester -Path test/powershell/engine/ApiOr a specific Pester test file:
Start-PSPester -Path test/powershell/engine/Api/XmlAdapter.Tests.ps1What happens after your PR?
When your PR has successfully passed the CI test gates, your changes will be used to create PowerShell binaries which can be run
in Microsoft's internal test frameworks.
The tests that you created for your change and the library of historical tests will be run to determine if any regressions are present.
If these tests find regressions, you'll be notified that your PR is not ready, and provided with enough information to investigate why the failure happened.
Test Layout
We have taken a functional approach to the layout of our Pester tests and you should place new tests in their appropriate location.
If you are making a fix to a cmdlet in a module, the test belongs in the module directory.
If you are unsure, you can make it part of your PR, or create an issue.
The current layout of tests is:
- test/powershell/engine
- test/powershell/engine/Api
- test/powershell/engine/Basic
- test/powershell/engine/ETS
- test/powershell/engine/Help
- test/powershell/engine/Logging
- test/powershell/engine/Module
- test/powershell/engine/ParameterBinding
- test/powershell/engine/Remoting
- test/powershell/engine/Runspace
- test/powershell/engine/Logging/MessageAnalyzer
- test/powershell/Host
- test/powershell/Host/ConsoleHost
- test/powershell/Host/TabCompletion
- test/powershell/Language
- test/powershell/Modules
- test/powershell/Provider
- test/powershell/SDK
- test/powershell/Security
- test/powershell/Language/Classes
- test/powershell/Language/Interop
- test/powershell/Language/Operators
- test/powershell/Language/Parser
- test/powershell/Language/Interop/DotNet
- test/powershell/Language/Scripting
- test/powershell/Language/Scripting/Debugging
- test/powershell/Language/Scripting/NativeExecution
- test/powershell/Modules/Microsoft.PowerShell.Archive
- test/powershell/Modules/Microsoft.PowerShell.Core
- test/powershell/Modules/Microsoft.PowerShell.Diagnostics
- test/powershell/Modules/Microsoft.PowerShell.Management
- test/powershell/Modules/Microsoft.PowerShell.Security
- test/powershell/Modules/Microsoft.PowerShell.Utility
- test/powershell/Modules/PSReadLine
Host Powershell Sample NuGet.Config (`docs/host-powershell/sample/NuGet.config.md`)
Nuget.config creation
Create a filed called nuget.config at this location with this content:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>--- METRICS ---
- Files Extracted: 58
- Estimated Token Budget: ~67161 tokens
- Canonical Reference: https://codewiki.google/github.com/PowerShell/powershell