This guide is the deep reference for administrators, operators, security engineers, architects, and developers who deploy, configure, secure, and extend PowerShell 7+ (the pwsh executable). It covers the engine architecture, the complete configuration surface, the security model, enterprise deployment, operations, and the internals of the source repository itself.
Note
Audience split: the companion User Guide covers interactive and scripting usage. This Admin Guide covers everything operational and internal — architecture,
powershell.config.json, the security model, remoting endpoints, logging, deployment, and the codebase. Where the User Guide showed how to use a feature, this guide explains how it works and how to govern it.
Table of Contents
- Executive Summary
- Architecture
- The Engine:
System.Management.Automation - Component & Assembly Inventory
- System Requirements
- Installation & Deployment
- Configuration Mastery:
powershell.config.json - Module Path and Module Loading
- Security Model
- Logging, Auditing & Diagnostics
- Remoting Architecture & Endpoints
- Operations
- Performance Tuning
- Repository Structure (For Developers)
- Building From Source
- Testing Strategy
- Extending PowerShell
- Troubleshooting (Operational)
- Potential Improvements & Technical Debt
- Quick Reference
1. Executive Summary
PowerShell is a cross-platform automation engine built on .NET. It is three things at once:
- A command-line shell with an interactive host (
Microsoft.PowerShell.ConsoleHost). - An object-oriented scripting language with its own parser, AST, compiler, and runtime (in
System.Management.Automation). - A cmdlet processing framework — an extensible command and provider model that hosts both in-box and third-party commands.
Its defining characteristic is the object pipeline: commands pass live .NET objects (not text) between each other, eliminating the parse-reparse cycle of text-based shells and making it ideal for structured data, REST APIs, and configuration management.
| Attribute | Value |
|---|---|
| Project | PowerShell (PowerShell 7+) |
| Executable | pwsh / pwsh.exe |
| Edition | Core ($PSEdition = Core) |
| Platform support | Windows, macOS, Linux (x64/ARM64) |
| Runtime | .NET (the repo at this commit targets the .NET 11 preview SDK toolchain) |
| Primary language | C# (plus PowerShell, C++ for native shims) |
| License | MIT (logos/trademarks excluded) |
| Source scale | ~2,492 files, ~8.7M source characters, 93 high-priority source folders |
| Relationship to Windows PowerShell 5.1 | Forked origin; changes are not ported back to 5.1; side-by-side install |
Important
PowerShell 7 (
pwsh) and Windows PowerShell 5.1 (powershell.exe) are separate products with separate executables, separate$PSHOMEdirectories, separate profiles, separate module paths, and separate configuration files. Treat them as distinct in every deployment and audit.
2. Architecture
PowerShell is a layered system. At the bottom is the .NET runtime; above it sits the automation engine; above that, the command and provider framework; and at the top, host applications that drive a runspace.
┌──────────────────────────────────────────────┐
USER / AUTOMATION│ pwsh CLI args · scripts · REST · remoting │
└───────────────────────┬──────────────────────┘
│
┌───────────────────────────────────▼───────────────────────────────┐
│ HOST LAYER │
│ Microsoft.PowerShell.ConsoleHost │
│ • CommandLineParameterParser • ConsoleHost / Host UI │
│ • PSReadLine integration • transcription │
└───────────────────────────────────┬───────────────────────────────┘
│ creates / drives
┌───────────────────────────────────▼───────────────────────────────┐
│ ENGINE: System.Management.Automation │
│ ┌────────────┐ ┌──────────────┐ ┌───────────────┐ ┌───────────┐ │
│ │ Language │ │ Command │ │ Runtime / │ │ Provider │ │
│ │ parser+AST │→│ discovery + │→│ binding + │ │ model │ │
│ │ +compiler │ │ processor │ │ pipeline │ │ (drives) │ │
│ └────────────┘ └──────────────┘ └───────────────┘ └───────────┘ │
│ Runspace · ExecutionContext · SessionState · ETS · Format engine │
│ Remoting (PSRP) · Security/AMSI · Subsystems · Debugger │
└───────────────────────────────────┬───────────────────────────────┘
│ hosts
┌───────────────────────────────────▼───────────────────────────────┐
│ COMMAND MODULES (in-box) │
│ Utility · Management · Security · Diagnostics · CimCmdlets · │
│ WSMan.Management · Host · LocalAccounts │
└───────────────────────────────────┬───────────────────────────────┘
│
┌───────────────────────────────────▼───────────────────────────────┐
│ .NET RUNTIME + native shims (libpsl-native, powershell-native) │
└────────────────────────────────────────────────────────────────────┘
Process startup flow
- Native/managed entry.
pwshstarts atsrc/powershell/Program.cs(ManagedPSEntry.Main). On Unix, it first checks for login-shell invocation and, if so, re-execs itself through/bin/sh -l(or/bin/zshinsh-emulation on macOS) so login profiles are sourced. It then callsUnmanagedPSEntry.Start. - Command-line parsing.
CommandLineParameterParser(inMicrosoft.PowerShell.ConsoleHost) parses thepwshswitches (-File,-Command,-ExecutionPolicy,-NoProfile, etc.). - Configuration load. The engine reads
powershell.config.json(machine-wide from$PSHOME, then per-user), unless overridden by-SettingsFile. - Runspace creation. The host creates a Runspace containing an
ExecutionContextandSessionState, applies the language mode and execution policy, and loads profiles (unless-NoProfile). - Pipeline execution. Commands are discovered, their parameters bound, and the pipeline runs, streaming objects from cmdlet to cmdlet.
Note
A Runspace is the fundamental unit of execution state — variables, functions, loaded modules, drives, and history. A host can create multiple runspaces; remoting and background jobs each get their own.
3. The Engine: System.Management.Automation
System.Management.Automation (often abbreviated SMA) is the heart of PowerShell. It is by far the largest assembly and contains the language, the command framework, the runtime, remoting, and security. Its top-level layout:
| Folder | Responsibility |
|---|---|
engine/parser |
Tokenizer, parser, and Abstract Syntax Tree (AST) for the PowerShell language. |
engine/runtime |
The compiled-expression runtime, binding (engine/runtime/Binding), and language operations (engine/runtime/Operations). |
engine/lang |
Language semantics and the interface layer. |
engine (root) |
Command discovery (CommandDiscovery.cs, CommandSearcher.cs), the command processor (CommandProcessor.cs), parameter binding (CmdletParameterBinderController.cs), CmdletInfo, SessionState, providers, drives, and the Extended Type System (ETS) adapters (CoreAdapter.cs, CodeMethods.cs). |
engine/remoting |
The PowerShell Remoting Protocol (PSRP): client, server, host, fanin, common/WireDataFormat, and the remoting cmdlets (Invoke-Command, *-PSSession). |
engine/Subsystem |
The pluggable subsystem model — CommandCompletion, PredictionSubsystem, FeedbackSubsystem, DscSubsystem. |
engine/debugger |
The script debugger. |
engine/CommandCompletion |
Tab-completion and IntelliSense engine. |
engine/Modules |
Module loading, manifests, and $PSModulePath resolution. |
engine/ComInterop / engine/COM |
COM interop (refreshed from dotnet/runtime, kept as a minimal diff). |
engine/interpreter |
The DLR-derived interpreter used before/while compiling hot paths. |
FormatAndOutput |
The formatting and output engine: Format-Table/-List/-Wide/-Custom, the display database, and Out-* targets. |
security |
Execution policy (SecuritySupport.cs), AMSI integration, code-signing/Authenticode, and Win32 security shims. |
logging |
Engine event logging (ETW on Windows, syslog on Linux). |
cimSupport |
CDXML-based cmdletization — generating cmdlets from CIM/WMI class definitions. |
namespaces |
Built-in providers (FileSystem, Registry, Environment, Variable, Alias, Function, Certificate). |
help |
The help subsystem (Get-Help, updatable help). |
SourceGenerators |
Roslyn source generators (e.g. PSVersionInfoGenerator). |
Tip
When tracing a bug from "I typed a command" to "this happened," the path is roughly: parser (text → AST) → compiler/interpreter (AST → executable) → command discovery (resolve the name) → parameter binder (bind args) → command processor (invoke) → pipeline (stream objects) → format engine (render for the host).
The Extended Type System (ETS)
PowerShell augments raw .NET objects with synthetic members (NoteProperty, ScriptProperty, ScriptMethod, AliasProperty) defined in *.types.ps1xml and *.format.ps1xml files. This is why Get-Process objects display friendly columns and expose convenience members that the underlying System.Diagnostics.Process type does not. The ETS adapters live in the engine root (CoreAdapter.cs, CodeMethods.cs); the format database lives under FormatAndOutput/common/DisplayDatabase.
4. Component & Assembly Inventory
The product is composed of several assemblies, each a folder under src/:
| Assembly / project | Role |
|---|---|
System.Management.Automation |
The engine: language, runtime, providers, remoting, security, formatting. |
Microsoft.PowerShell.ConsoleHost |
The interactive console host, command-line parsing, transcription, host UI. |
Microsoft.PowerShell.Commands.Utility |
Utility cmdlets: objects, formatting, JSON/CSV/XML, web cmdlets, strings, math, Out-GridView. |
Microsoft.PowerShell.Commands.Management |
Filesystem, processes, services, items, locations, registry, content. |
Microsoft.PowerShell.Commands.Diagnostics |
Event-log and performance-counter cmdlets (Windows). |
Microsoft.PowerShell.Security |
Execution policy, Authenticode signing, credentials, secure strings. |
Microsoft.PowerShell.LocalAccounts |
Local user/group management cmdlets (Windows). |
Microsoft.Management.Infrastructure.CimCmdlets |
CIM/WMI cmdlets (Get-CimInstance, Invoke-CimMethod, …). |
Microsoft.WSMan.Management |
WS-Management / WinRM configuration cmdlets and provider. |
Microsoft.WSMan.Runtime |
WSMan runtime support. |
Microsoft.PowerShell.CoreCLR.Eventing |
ETW eventing on .NET. |
Microsoft.Management.UI.Internal |
GUI helpers (Show-Command, help window, management list). |
Microsoft.PowerShell.SDK |
The meta-package/SDK surface for hosting PowerShell in your own .NET apps. |
Microsoft.PowerShell.GlobalTool.Shim |
Shim that makes PowerShell installable as a .NET global tool. |
powershell |
The pwsh executable entry point (Program.cs). |
powershell-native / libpsl-native |
Native (C/C++) shims for platform interop. |
powershell-unix / powershell-win-core |
Platform-specific build glue. |
PowerShell.Core.Instrumentation |
Native instrumentation/telemetry manifests. |
ResGen / ResxGen / TypeCatalogGen |
Build-time code generators (resources, type catalog). |
Bundled modules (shipped, by platform)
| Module | Windows | Linux/macOS |
|---|---|---|
Microsoft.PowerShell.Management |
✓ | ✓ |
Microsoft.PowerShell.Utility |
✓ | ✓ |
Microsoft.PowerShell.Security |
✓ | ✓ |
Microsoft.PowerShell.Host |
✓ | ✓ |
Microsoft.PowerShell.Diagnostics |
✓ | — |
CimCmdlets |
✓ | — |
Microsoft.WSMan.Management |
✓ | — |
PSDiagnostics |
✓ | — |
Note
The platform split is enforced by the build (
src/Modules/Shared,src/Modules/Windows,src/Modules/Unix). On non-Windows hosts, the Windows-only modules simply aren't present, so cmdlets likeGet-CimInstanceorGet-WinEventwon't resolve.
5. System Requirements
| Concern | Requirement |
|---|---|
| OS (Windows) | Supported Windows client/server releases (per Microsoft's current support matrix). |
| OS (Linux) | Supported distributions published to packages.microsoft.com (Ubuntu, Debian, RHEL, etc.). |
| OS (macOS) | Supported macOS releases (x64 and Apple Silicon/ARM64). |
| Architecture | x64 and ARM64. (A lighter ARM64 build is an open community request — see issue #27555.) |
| Runtime | Self-contained installs bundle the required .NET runtime; framework-dependent installs (e.g. the .NET global tool) require a matching .NET SDK/runtime. |
| Disk | A few hundred MB for a self-contained install. |
| Privileges | Per-user installs need no elevation; machine-wide installs (MSI, package managers) require admin/root. |
Important
Most official packages are self-contained: they carry their own copy of the .NET runtime, so you do not need .NET installed separately. The exception is the .NET global tool (
dotnet tool install --global PowerShell), which is framework-dependent and needs the .NET SDK present.
6. Installation & Deployment
Per-platform install (summary)
| Platform | Method | Command |
|---|---|---|
| Windows | winget | winget install --id Microsoft.PowerShell --source winget |
| Windows | MSI | Run the downloaded .msi (supports unattended msiexec /qn) |
| macOS | Homebrew | brew install --cask powershell |
| Linux | apt | sudo apt-get install -y powershell (after adding the MS repo) |
| Any | .NET tool | dotnet tool install --global PowerShell |
| Any | Docker | docker run -it mcr.microsoft.com/powershell |
Unattended Windows MSI deployment
The MSI supports standard msiexec properties for silent, policy-driven deployment, for example:
msiexec.exe /package PowerShell-7.x-win-x64.msi /quiet `
ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 `
ENABLE_PSREMOTING=1 `
REGISTER_MANIFEST=1 `
USE_MU=1 ADD_PATH=1
Tip
For fleet deployment, prefer winget (with the Microsoft Store/winget repository) or a configuration-management tool (DSC, Ansible, Intune). Pin to PowerShell-LTS on Linux when you need a slower, supported release cadence.
Containers
Official images are published to the Microsoft Container Registry (mcr.microsoft.com/powershell). The repository's docker/ directory and tools/releaseBuild/ define how release images and packages are produced. A minimal Dockerfile to layer PowerShell tooling onto your own image typically starts FROM mcr.microsoft.com/powershell.
Side-by-side and channels
- Stable vs Preview vs LTS can coexist; each installs to a distinct
$PSHOME. - The executable is always
pwsh; previews may install aspwsh-preview. - Because PowerShell 7 never overwrites Windows PowerShell 5.1, you can roll it out without touching existing 5.1 automation.
Warning
A known packaging gap on Linux: the PowerShell-LTS apt package in the Microsoft repository may lag the latest minor version for a given distro (see issue #27552). Verify the exact available version before standardizing on LTS for a rollout.
7. Configuration Mastery: powershell.config.json
PowerShell's persistent, machine- and user-level configuration lives in a JSON file named powershell.config.json. There are two locations:
| Scope | Location | Applies to |
|---|---|---|
| All users (machine) | $PSHOME/powershell.config.json |
Every user of this PowerShell install |
| Current user | The user configuration directory (e.g. ~/.config/powershell/ on Unix; the per-user PowerShell data directory on Windows) |
Just that user |
Override the file used for a single session with pwsh -SettingsFile <path>.
Recognized keys
These keys are read by the engine's configuration layer (PowerShellConfig). All are optional; omit what you don't need.
| Key | Type | Purpose |
|---|---|---|
Microsoft.PowerShell:ExecutionPolicy |
string | Sets the execution policy for this PowerShell install (the namespaced form is used in $PSHOME configs). |
ExecutionPolicy |
string | Execution policy (user-scope config form). |
PSModulePath |
string | Overrides/augments the module search path for this install or user. |
ExperimentalFeatures |
string[] | List of experimental features to enable (see below). |
DisableImplicitWinCompat |
bool | Disables the implicit Windows PowerShell compatibility auto-loading of 5.1 modules. |
WindowsPowerShellCompatibilityModuleDenyList |
string[] | Modules that must never be loaded via the Windows-compat shim. |
ScriptBlockLogging |
object | Script-block logging settings (see Logging). |
ProtectedEventLogging |
object | Protected event logging (encrypted log payloads). |
Transcription |
object | Over-the-shoulder transcription settings. |
ScriptBlockLogging sub-keys
| Sub-key | Type | Meaning |
|---|---|---|
EnableScriptBlockLogging |
bool | Log the text of script blocks as they are compiled/executed. |
EnableScriptBlockInvocationLogging |
bool | Additionally log invocation start/stop events. |
Transcription sub-keys
| Sub-key | Type | Meaning |
|---|---|---|
EnableTranscripting |
bool | Turn on automatic session transcription. |
EnableInvocationHeader |
bool | Write a header before each command in the transcript. |
OutputDirectory |
string | Where transcript files are written. |
ProtectedEventLogging sub-keys
| Sub-key | Type | Meaning |
|---|---|---|
EnableProtectedEventLogging |
bool | Encrypt logged event payloads. |
EncryptionCertificate |
string[] | Certificate(s) whose public key encrypts the log data. |
Example powershell.config.json
{
"Microsoft.PowerShell:ExecutionPolicy": "RemoteSigned",
"PSModulePath": "/opt/company/psmodules",
"ExperimentalFeatures": [
"PSFeedbackProvider",
"PSSubsystemPluginModel"
],
"DisableImplicitWinCompat": true,
"ScriptBlockLogging": {
"EnableScriptBlockLogging": true,
"EnableScriptBlockInvocationLogging": false
},
"Transcription": {
"EnableTranscripting": true,
"EnableInvocationHeader": true,
"OutputDirectory": "/var/log/pwsh-transcripts"
}
}
Caution
A malformed
powershell.config.jsoncan prevent PowerShell from starting. There is an open request (issue #27370) to fall back to defaults when the user config can't be loaded; until then, keep a known-good backup and validate JSON before deploying. Recover withpwsh -SettingsFile <known-good>.
Note
On Windows, several of these settings (execution policy, script-block logging, transcription, protected event logging) can also be enforced via Group Policy, which takes precedence over the JSON file. See Security Model.
8. Module Path and Module Loading
PowerShell discovers modules along $env:PSModulePath (a path-separated list). The default contains:
- The user modules directory (e.g.
~/.local/share/powershell/Moduleson Unix;Documents\PowerShell\Moduleson Windows). - The all-users shared modules directory.
- The
$PSHOME/Modulesdirectory (the in-box modules). - On Windows, optionally the Windows PowerShell 5.1 module path (for the compatibility shim).
$env:PSModulePath -split [IO.Path]::PathSeparator
Override or extend the path durably via the PSModulePath key in powershell.config.json (preferred) or by editing the environment variable in a profile (per-session).
Windows PowerShell compatibility
On Windows, PowerShell 7 can transparently load many Windows PowerShell 5.1 modules through an implicit compatibility session. Govern this with:
DisableImplicitWinCompat: true— turn the auto-loading off entirely.WindowsPowerShellCompatibilityModuleDenyList— block specific modules from being loaded through the shim (useful when a 5.1 module misbehaves under 7).
Tip
In hardened or non-Windows environments, set
DisableImplicitWinCompat: trueso module resolution is deterministic and there are no surprise cross-edition loads.
9. Security Model
PowerShell's security features are defensive controls for safety and auditability, layered with the OS. Understand what each does — and does not — protect against.
Execution policy
Controls whether script files run. Defined values (from SecuritySupport.cs):
| Policy | Numeric | Behavior |
|---|---|---|
Unrestricted |
0 | All scripts run; warn on internet-origin files. |
RemoteSigned |
1 | Local scripts run; internet-origin scripts must be signed. |
AllSigned |
2 | All scripts must be signed by a trusted publisher. |
Restricted |
3 | No scripts run (the engine's Default). |
Bypass |
4 | Everything runs, no checks, no prompts. |
Undefined |
5 | Unset at this scope; fall through. |
Scopes, in precedence order (later overrides earlier in the resolved value, but Group Policy wins overall): MachinePolicy and UserPolicy (Group Policy) → Process → CurrentUser → LocalMachine.
Get-ExecutionPolicy -List
Set-ExecutionPolicy -Scope LocalMachine RemoteSigned
Important
Execution policy is not a security boundary. It stops accidents, not adversaries. Real isolation comes from OS permissions, AppLocker/WDAC, constrained language mode, and JEA — not from execution policy alone.
Note
On Linux/macOS the execution policy is fixed at
Unrestricted; filesystem permissions are the control. Enforce signing/allow-listing at the OS layer there.
Authenticode code signing
Microsoft.PowerShell.Security provides Get-AuthenticodeSignature and Set-AuthenticodeSignature to sign and verify .ps1/.ps1xml/module files. Under AllSigned/RemoteSigned, PowerShell validates these signatures and (per issue #27556) consults the publisher trust store. The signing pipeline for the product itself lives in src/signing/.
AMSI (Antimalware Scan Interface)
On Windows, PowerShell sends script content to AMSI so installed antivirus/EDR can scan it before execution — including dynamically generated and obfuscated code. This integration lives in the engine security/ folder. AMSI is a key reason script-block logging + AMSI together give defenders visibility even into in-memory code.
Constrained Language Mode (CLM)
$ExecutionContext.SessionState.LanguageMode can be FullLanguage, ConstrainedLanguage, RestrictedLanguage, or NoLanguage. ConstrainedLanguage blocks risky operations (arbitrary .NET type access, COM, Add-Type) and is automatically engaged under application-control policies (WDAC/AppLocker). Use it with allow-listing to limit what untrusted scripts can do.
Just Enough Administration (JEA) & session configurations
Remoting endpoints can be locked down with session configuration (.pssc) files that constrain which commands/parameters a connecting user may run, optionally under a privileged run-as identity (-ConfigurationName, -ConfigurationFile). This delegates specific administrative tasks without granting full shell access.
Local accounts
Microsoft.PowerShell.LocalAccounts (Windows) manages local users and groups (Get-LocalUser, New-LocalUser, Add-LocalGroupMember, …) — useful in build/security hardening scripts.
Caution
Defense-in-depth for PowerShell on Windows generally means: WDAC/AppLocker (what can run) + Constrained Language Mode (what scripts can do) + script-block & module logging (what ran) + AMSI (live scanning) + JEA (least-privilege endpoints). Execution policy is the least important of these.
10. Logging, Auditing & Diagnostics
PowerShell offers rich, tamper-evident logging — essential for security operations.
| Mechanism | Configured by | What it captures |
|---|---|---|
| Script-block logging | ScriptBlockLogging in config / Group Policy |
The actual code executed, including de-obfuscated/dynamic blocks. |
| Module logging | Group Policy / per-module | Pipeline execution events for specified modules. |
| Transcription | Transcription in config / Group Policy |
A full "over-the-shoulder" text record of input and output. |
| Protected event logging | ProtectedEventLogging in config |
Encrypts logged payloads with a certificate's public key (decrypt centrally). |
| Engine ETW / EventLog | engine/logging |
Engine lifecycle events (Windows EventLog; syslog on Linux). |
Transcription example (config-driven)
{
"Transcription": {
"EnableTranscripting": true,
"EnableInvocationHeader": true,
"OutputDirectory": "/var/log/pwsh-transcripts"
}
}
Cross-platform logging
- Windows: events flow to the Windows Event Log (PowerShell operational/admin channels) and AMSI; ETW manifests are in
PowerShell.Core.Instrumentation. - Linux: the engine logs to syslog (and journald); see
src/System.Management.Automation/logging. - macOS: logs via the unified logging facility.
Tip
For a SIEM pipeline, enable script-block logging + module logging + transcription, ship the Event Log / syslog stream centrally, and consider protected event logging so transcripts/logs are encrypted at rest until they reach the collector.
PSDiagnostics and performance tracing
The PSDiagnostics module (Windows) wraps ETW tracing for diagnosing engine behavior. The repository's test/perf and tools/performance trees define BenchmarkDotNet micro-benchmarks (Start-Benchmarking, Compare-BenchmarkResult) used to detect regressions.
11. Remoting Architecture & Endpoints
PowerShell remoting is implemented as the PowerShell Remoting Protocol (PSRP) over a pluggable transport. The engine code lives in engine/remoting (client, server, host, fanin, common/WireDataFormat).
Transports
| Transport | Scope | Notes |
|---|---|---|
| SSH | Cross-platform | Recommended for mixed fleets. Remote runs pwsh -SSHServerMode as an SSH subsystem. |
| WinRM / WS-Management | Windows ↔ Windows | Built on Microsoft.WSMan.Management; uses HTTP/HTTPS listeners. |
Configuring SSH-based remoting (server side)
-
Install an SSH server (OpenSSH) and PowerShell 7 on the target.
-
Register a PowerShell subsystem in the SSH daemon configuration so it launches
pwsh -sshs(-SSHServerMode). Conceptually:Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -
Restart the SSH daemon. Clients then connect with
-HostName/-UserName/-KeyFilePath.
# Client side, once the subsystem is registered on server01:
Invoke-Command -HostName server01 -UserName admin -ScriptBlock { $PSVersionTable.OS }
Enter-PSSession -HostName server01 -UserName admin -KeyFilePath ~/.ssh/id_ed25519
Session configurations / JEA
WinRM endpoints are registered with Register-PSSessionConfiguration and can apply a .pssc to constrain the endpoint (JEA). Connect to a specific endpoint with -ConfigurationName.
Note
Enter-PSHostProcess(with-CustomPipeName) attaches to a local runningpwshprocess over a named pipe — invaluable for debugging a stuck or background PowerShell process without a network transport.
Warning
SSH subsystem details (the exact
sshd_configline, key management, and platform paths) differ by OS and OpenSSH version. Treat the snippet above as the shape of the configuration, and validate against your platform's OpenSSH documentation.
12. Operations
Identifying the install
$PSVersionTable # version, edition, OS, protocol versions
$PSHOME # install directory of this pwsh
Get-Process pwsh # running instances
Managing experimental features fleet-wide
Enable features declaratively in powershell.config.json (so they survive across sessions and are auditable), rather than per-session:
{ "ExperimentalFeatures": [ "PSFeedbackProvider", "PSSubsystemPluginModel" ] }
Shipped features in this build: PSFeedbackProvider, PSLoadAssemblyFromNativeCode, PSNativeWindowsTildeExpansion, PSProfileDSCResource, PSSerializeJSONLongEnumAsNumber, PSRedirectToVariable, PSSubsystemPluginModel. Changes require a restart.
Updatable help on servers
Update-Help -Scope AllUsers -Force # requires elevation; needs internet
Save-Help -DestinationPath \\share\pshelp # for air-gapped: stage centrally
Update-Help -SourcePath \\share\pshelp # then pull from the share
All-users profiles
Place organization-wide startup logic in the All-Users profile ($PROFILE.AllUsersAllHosts, located under $PSHOME). Keep it minimal and fast — it runs for every user, every session.
DSC-based profile management
The repo ships a DSC resource for the profile (dsc/pwsh.profile.dsc.resource.json, dsc/pwsh.profile.resource.ps1) and the PSProfileDSCResource experimental feature — letting you manage the PowerShell profile declaratively through Desired State Configuration.
Health checks
pwsh -NoProfile -Command '$PSVersionTable | ConvertTo-Json -Compress'
pwsh -NoProfile -Command 'Get-Module -ListAvailable | Measure-Object'
Tip
Bake
pwsh -NoProfile -NonInteractiveinto all health checks and automation so results are independent of any user's profile and never hang on a prompt.
13. Performance Tuning
| Lever | Effect |
|---|---|
-NoProfile |
Skips profile scripts — the single biggest startup win in most environments. |
-NoProfileLoadTime |
Suppresses the profile-load-time banner line. |
| Lazy module import | Import heavy modules inside functions, not at profile top level. |
| ReadyToRun / self-contained build | Official builds are AOT-precompiled (ReadyToRun) for faster startup. |
Avoid Format-* mid-pipeline |
Formatting objects are display-only; keep raw objects until the end. |
Use the pipeline, not += in loops |
Repeated array += reallocates; prefer pipeline output or List<T>. |
| Stream instead of buffering | Let cmdlets stream objects rather than collecting with @(...) when possible. |
Measuring
Measure-Command { . $PROFILE } # profile cost
pwsh -NoProfile -Command 'Measure-Command { 1..1e5 | ForEach-Object { $_ } }'
Benchmark harness (engine developers)
The test/perf/benchmarks project uses BenchmarkDotNet:
dotnet run -c Release -f net6.0 --filter *script* --list flat
Or via the repo's helper module tools/perf.psm1:
Start-Benchmarking -Filter *script* -Artifacts C:\tmp\current\
Compare-BenchmarkResult -BaseResultPath C:\tmp\7.1.3\ -DiffResultPath C:\tmp\current\ -Threshold 1%
Note
Slow interactive startup is most often a profile or module-import problem, not the engine. Always compare against
pwsh -NoProfilebefore assuming an engine regression.
14. Repository Structure (For Developers)
PowerShell/
├── src/ # all product assemblies
│ ├── System.Management.Automation/ # THE ENGINE
│ │ ├── engine/ (parser, runtime, remoting, Subsystem, debugger, ...)
│ │ ├── FormatAndOutput/ security/ logging/ namespaces/ help/
│ │ ├── cimSupport/ (CDXML cmdletization)
│ │ └── SourceGenerators/
│ ├── Microsoft.PowerShell.ConsoleHost/ # console host + CLI parsing
│ ├── Microsoft.PowerShell.Commands.Utility/ # utility cmdlets + web cmdlets
│ ├── Microsoft.PowerShell.Commands.Management/ # fs/process/service cmdlets
│ ├── Microsoft.PowerShell.Commands.Diagnostics/ # event log / perf counters
│ ├── Microsoft.PowerShell.Security/ # exec policy, signing, credentials
│ ├── Microsoft.PowerShell.LocalAccounts/ # local users/groups (Win)
│ ├── Microsoft.Management.Infrastructure.CimCmdlets/ # CIM/WMI
│ ├── Microsoft.WSMan.Management/ Microsoft.WSMan.Runtime/ # WinRM
│ ├── Microsoft.Management.UI.Internal/ # Show-Command, help window
│ ├── Microsoft.PowerShell.SDK/ # hosting SDK meta-package
│ ├── Microsoft.PowerShell.GlobalTool.Shim/ # dotnet global tool
│ ├── powershell/ # pwsh entry point (Program.cs)
│ ├── powershell-native/ libpsl-native/ # native shims (C/C++)
│ ├── Modules/ (Shared / Windows / Unix bundled modules)
│ ├── ResGen/ ResxGen/ TypeCatalogGen/ # build-time generators
│ └── Schemas/ # PSMaml and other schemas
├── test/
│ ├── powershell/ # Pester (PowerShell-language) tests
│ ├── xUnit/ # C# unit tests
│ ├── tools/WebListener/ # ASP.NET test server for web cmdlets
│ ├── perf/ # BenchmarkDotNet micro-benchmarks
│ ├── packaging/ fuzzing/ shebang/ SSHRemoting/ hosting/ nanoserver/
├── tools/ # build, packaging, release, linting
│ ├── packaging/ releaseBuild/ buildCommon/ performance/
│ ├── credScan/ guardian/ ComponentGovernance/ clearlyDefined/ # security/compliance
│ └── super-linter/ ResxGen/ Xml/
├── docs/ # contributor & maintainer docs
├── assets/ # logos (trademarked), manpage, Group Policy
├── docker/ # container build assets
├── dsc/ # DSC resource for the profile
├── build.psm1 # the master build module
├── PowerShell.Common.props # shared MSBuild props (versioning)
├── global.json # pinned .NET SDK version
└── PowerShell.sln # the solution
Note
global.jsonpins the .NET SDK used to build. At this commit it targets an 11.0.100-preview SDK — the product tracks recent .NET previews during development and ships on the corresponding stable .NET runtime.
15. Building From Source
PowerShell builds itself with PowerShell, via the build.psm1 module at the repo root.
# From a clone of the repo, in an existing pwsh (or Windows PowerShell on Windows):
Import-Module ./build.psm1
Start-PSBootstrap # install prerequisites (.NET SDK, native deps)
Start-PSBuild # build pwsh
Run the freshly built shell, then test:
$pwsh = Get-PSOutput # path to the built pwsh
& $pwsh -NoProfile -Command '$PSVersionTable'
Important
The build is self-hosting and bootstrap-heavy:
Start-PSBootstrapinstalls the exact.NET SDKpinned inglobal.jsonplus platform native toolchains. Use it on a clean machine/CI agent rather than hand-installing prerequisites.
Build-time code generation:
ResGen/ResxGenturn.resxresource files into strongly-typed C#.TypeCatalogGengenerates the type catalog used for assembly/type resolution.SourceGenerators(Roslyn) generate version info and other compile-time artifacts.
16. Testing Strategy
Tests are organized by testing language (see test/README.md):
| Suite | Language | Location | Purpose |
|---|---|---|---|
| Pester | PowerShell | test/powershell/ |
Behavioral/feature tests written in the PowerShell language (the bulk of the suite). |
| xUnit | C# | test/xUnit/ |
Lower-level unit tests of engine internals. |
| shebang | shell | test/shebang/ |
Validates Unix shebang (#!) execution. |
| WebListener | C# (ASP.NET) | test/tools/WebListener/ |
A real HTTP/HTTPS server exercising the web cmdlets (auth, compression, redirects, retries, pagination, multipart, client certs). |
| SSHRemoting | mixed | test/SSHRemoting/ |
SSH-based remoting end-to-end. |
| perf | C# | test/perf/ |
BenchmarkDotNet regression benchmarks. |
| fuzzing | C# | test/fuzzing/ |
Fuzz inputs against the parser/engine. |
| packaging | mixed | test/packaging/ |
Validates produced packages on each platform. |
Run the Pester suite through the build module:
Import-Module ./build.psm1
Start-PSPester # full Pester run against the built pwsh
Start-PSxUnit # the C# unit tests
Tip
The WebListener harness is the canonical way to test HTTP behavior without hitting the public internet — it spins up local HTTP/HTTPS endpoints (including self-signed TLS and client-cert auth) that mirror real-world responses. Start it with
Start-WebListenerfrom its module.
17. Extending PowerShell
Binary modules (C# cmdlets)
Author a class deriving from Cmdlet or PSCmdlet, decorate it with [Cmdlet(VerbsCommon.Get, "Thing")], expose [Parameter] properties, and override BeginProcessing/ProcessRecord/EndProcessing. Reference Microsoft.PowerShell.SDK to build against the engine.
Script modules
A .psm1 plus a .psd1 manifest packages functions, with Export-ModuleMember controlling the public surface. This is the most common extension path.
CDXML / cmdletization
cimSupport/cmdletization turns CIM/WMI class definitions into full cmdlets from XML (CDXML) — no C# required. Many Windows management modules are generated this way.
Pluggable subsystems
The engine/Subsystem model (gated by PSSubsystemPluginModel) lets you register custom command predictors (PSReadLine suggestions), feedback providers (suggestions when a command fails), and completion/DSC subsystems without modifying the engine.
Hosting PowerShell in your app
Reference Microsoft.PowerShell.SDK, create a Runspace/RunspacePool, build a PowerShell object, add commands/scripts, and Invoke(). This embeds the full engine inside any .NET application (test/hosting/ and docs/host-powershell/ show working samples).
18. Troubleshooting (Operational)
PowerShell won't start after a config change
Cause: Invalid powershell.config.json.
Resolution: pwsh -SettingsFile <known-good.json>; validate the JSON; restore from backup. Track issue #27370 for an automatic fallback.
Scripts blocked by execution policy on Windows servers
Cause: Group Policy (MachinePolicy) enforces a restrictive policy that overrides local settings.
Resolution: Check Get-ExecutionPolicy -List — if MachinePolicy/UserPolicy is set, it must be changed in Group Policy, not with Set-ExecutionPolicy.
A Windows PowerShell 5.1 module misbehaves under 7
Cause: Implicit WinCompat loaded a 5.1-only module that doesn't work cross-edition.
Resolution: Add it to WindowsPowerShellCompatibilityModuleDenyList, or set DisableImplicitWinCompat: true and load a 7-native equivalent.
Native command exit codes not honored
Cause: $PSNativeCommandUseErrorActionPreference may not apply when the native error stream is redirected/discarded (issue #27543).
Resolution: Test $LASTEXITCODE explicitly after native calls in automation.
Dependency on user32.dll on Windows Server Core
Cause: Some code paths pull in user32.dll (issue #27323), which can be problematic on minimal/headless installs.
Resolution: Track the issue; test PowerShell on your exact Server Core/Nano image (test/nanoserver/ exercises minimal images).
Encoding issues piping to native tools
Cause: PowerShell's output encoding may not match [Console]::OutputEncoding (issue #25698), garbling output to tools like more.
Resolution: Set $OutputEncoding/[Console]::OutputEncoding to UTF-8 consistently in your profile/CI.
Diagnosing a hung pwsh
Resolution: Attach to it locally without a network transport:
Enter-PSHostProcess -Id <pid> # or -CustomPipeName <name>
Get-Runspace
Debug-Runspace -Id <runspaceId>
19. Potential Improvements & Technical Debt
These are observations drawn from the source and the community issue tracker, offered as a backlog-style view for maintainers and downstream adopters.
| Area | Observation | Reference |
|---|---|---|
| Startup resilience | No automatic fallback to defaults when powershell.config.json fails to load — a single bad config bricks startup. |
#27370 |
| ARM64 footprint | Demand for a lighter "PowerShell Lite" build for ARM64. | #27555 |
| Windows dependency surface | user32.dll dependency complicates headless/Server Core scenarios. |
#27323 |
| Native command error handling | $PSNativeCommandUseErrorActionPreference edge case with discarded error streams. |
#27543 |
| Default output encoding | Pipe-to-native encoding mismatch vs [Console]::OutputEncoding. |
#25698 |
| Signing trust scope | AllSigned should also check LocalMachine\TrustedPublisher. |
#27556 |
| Help UI | Get-Help -ShowWindow truncates some topics to the first section. |
#17015 |
| COM interop maintenance | engine/ComInterop is intentionally kept as a minimal diff against dotnet/runtime, requiring careful three-way merges on refresh — fragile and manual by design. |
engine/ComInterop/README.md |
| LTS packaging lag | Linux PowerShell-LTS package can trail the current minor version. | #27552 |
Note
The engine deliberately mirrors upstream
dotnet/runtimeComInterop code to keep diffs reviewable. This is a maintainability trade-off, not a defect: it eases future .NET refreshes at the cost of manual three-way comparisons. Don't "clean it up" without understanding that constraint.
20. Quick Reference
Key paths
| Item | Location |
|---|---|
| Install dir | $PSHOME |
| Machine config | $PSHOME/powershell.config.json |
| User config | user PowerShell config dir (~/.config/powershell/ on Unix) |
| In-box modules | $PSHOME/Modules |
| User modules | user modules dir (on $env:PSModulePath) |
| User profile | $PROFILE ($PROFILE.CurrentUserCurrentHost) |
| All-users profile | $PROFILE.AllUsersAllHosts (under $PSHOME) |
Governance cheat-sheet
| Goal | Control |
|---|---|
| Restrict which scripts run | Execution policy (+ Group Policy on Windows) |
| Restrict what scripts can do | Constrained Language Mode + WDAC/AppLocker |
| Audit what ran | Script-block logging + module logging + transcription |
| Encrypt logs | Protected event logging (EncryptionCertificate) |
| Least-privilege remoting | JEA session configurations (.pssc, -ConfigurationName) |
| Deterministic module loading | PSModulePath config key + DisableImplicitWinCompat |
| Live malware scanning (Windows) | AMSI (built in) |
Diagnostic one-liners
$PSVersionTable # version & build
Get-ExecutionPolicy -List # policy at every scope
$env:PSModulePath -split [IO.Path]::PathSeparator # module search path
Get-ExperimentalFeature # feature flags
Get-Module -ListAvailable | Measure-Object # installed module count
pwsh -NoProfile -Command '$PSVersionTable | ConvertTo-Json -Compress' # clean probe
Config skeleton
{
"Microsoft.PowerShell:ExecutionPolicy": "RemoteSigned",
"PSModulePath": "",
"ExperimentalFeatures": [],
"DisableImplicitWinCompat": false,
"WindowsPowerShellCompatibilityModuleDenyList": [],
"ScriptBlockLogging": { "EnableScriptBlockLogging": true, "EnableScriptBlockInvocationLogging": false },
"Transcription": { "EnableTranscripting": true, "EnableInvocationHeader": true, "OutputDirectory": "" },
"ProtectedEventLogging": { "EnableProtectedEventLogging": false, "EncryptionCertificate": [] }
}