Jesse A. Green / notebook

PowerShell Administrator & Architecture Guide

Generated June 2026 from PowerShell/PowerShell at c23c421

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

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

  1. Executive Summary
  2. Architecture
  3. The Engine: System.Management.Automation
  4. Component & Assembly Inventory
  5. System Requirements
  6. Installation & Deployment
  7. Configuration Mastery: powershell.config.json
  8. Module Path and Module Loading
  9. Security Model
  10. Logging, Auditing & Diagnostics
  11. Remoting Architecture & Endpoints
  12. Operations
  13. Performance Tuning
  14. Repository Structure (For Developers)
  15. Building From Source
  16. Testing Strategy
  17. Extending PowerShell
  18. Troubleshooting (Operational)
  19. Potential Improvements & Technical Debt
  20. Quick Reference

1. Executive Summary

PowerShell is a cross-platform automation engine built on .NET. It is three things at once:

  1. A command-line shell with an interactive host (Microsoft.PowerShell.ConsoleHost).
  2. An object-oriented scripting language with its own parser, AST, compiler, and runtime (in System.Management.Automation).
  3. 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 $PSHOME directories, 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

  1. Native/managed entry. pwsh starts at src/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/zsh in sh-emulation on macOS) so login profiles are sourced. It then calls UnmanagedPSEntry.Start.
  2. Command-line parsing. CommandLineParameterParser (in Microsoft.PowerShell.ConsoleHost) parses the pwsh switches (-File, -Command, -ExecutionPolicy, -NoProfile, etc.).
  3. Configuration load. The engine reads powershell.config.json (machine-wide from $PSHOME, then per-user), unless overridden by -SettingsFile.
  4. Runspace creation. The host creates a Runspace containing an ExecutionContext and SessionState, applies the language mode and execution policy, and loads profiles (unless -NoProfile).
  5. 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 like Get-CimInstance or Get-WinEvent won'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 as pwsh-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.json can 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 with pwsh -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:

  1. The user modules directory (e.g. ~/.local/share/powershell/Modules on Unix; Documents\PowerShell\Modules on Windows).
  2. The all-users shared modules directory.
  3. The $PSHOME/Modules directory (the in-box modules).
  4. 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: true so 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)

  1. Install an SSH server (OpenSSH) and PowerShell 7 on the target.

  2. Register a PowerShell subsystem in the SSH daemon configuration so it launches pwsh -sshs (-SSHServerMode). Conceptually:

    Subsystem powershell /usr/bin/pwsh -sshs -NoLogo
    
  3. 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 running pwsh process over a named pipe — invaluable for debugging a stuck or background PowerShell process without a network transport.

Warning

SSH subsystem details (the exact sshd_config line, 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 -NonInteractive into 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 -NoProfile before 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.json pins 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-PSBootstrap installs the exact .NET SDK pinned in global.json plus platform native toolchains. Use it on a clean machine/CI agent rather than hand-installing prerequisites.

Build-time code generation:

  • ResGen/ResxGen turn .resx resource files into strongly-typed C#.
  • TypeCatalogGen generates 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-WebListener from 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/runtime ComInterop 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": [] }
}