From 9b2bcb0977a487d32afd6c6c854983e98f1f81d4 Mon Sep 17 00:00:00 2001 From: Demitrius Nelon Date: Wed, 17 Jun 2026 10:31:32 -0700 Subject: [PATCH 1/3] Add spec: PowerShell Module Full Parity (#6288) Specification for achieving full functional parity between the WinGet PowerShell module and the CLI, including SYSTEM context support, new cmdlets for configure/pin/download/repair/settings operations, and enhanced source management. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../#6288 - PowerShell Module Full Parity.md | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 doc/specs/#6288 - PowerShell Module Full Parity.md diff --git a/doc/specs/#6288 - PowerShell Module Full Parity.md b/doc/specs/#6288 - PowerShell Module Full Parity.md new file mode 100644 index 0000000000..04799c5b0c --- /dev/null +++ b/doc/specs/#6288 - PowerShell Module Full Parity.md @@ -0,0 +1,280 @@ +--- +author: Demitrius Nelon denelon, GitHub Copilot Copilot +created on: 2026-06-17 +last updated: 2026-06-17 +issue id: 6288 +--- + +# PowerShell Module Full Parity with WinGet CLI + +For [#6288](https://github.com/microsoft/winget-cli/issues/6288). + +## Abstract + +The WinGet PowerShell module (`Microsoft.WinGet.Client`) must achieve full functional parity with the WinGet CLI. Several CLI capabilities have no PowerShell equivalent today, blocking adoption by enterprise automation workflows that operate exclusively in PowerShell contexts (Intune, SCCM, Azure Automation, DSC). + +## Inspiration + +IT decision makers evaluating WinGet for large-scale deployment cite incomplete PowerShell support as a blocking issue. Enterprise automation tooling operates in PowerShell-only contexts where shelling out to `winget.exe` is unreliable — especially in SYSTEM context ([#5991](https://github.com/microsoft/winget-cli/issues/5991)) — or architecturally inappropriate. + +Key gaps identified by enterprise customers: + +- No PowerShell equivalent for `winget configure` operations (test, validate, show) +- Missing cmdlets for pin management (`winget pin add/remove/list/reset`) +- No cmdlet for `winget download` ([#658](https://github.com/microsoft/winget-cli/issues/658) added CLI support) +- Missing `winget repair` support in the module ([#148](https://github.com/microsoft/winget-cli/issues/148) added CLI support) +- No PowerShell equivalent for `winget settings` management +- Limited source management beyond basic add/remove +- COM API cannot activate in elevated/SYSTEM context ([#6042](https://github.com/microsoft/winget-cli/issues/6042)) + +## Solution Design + +### Phase 1: Core Cmdlet Gaps + +Add the following cmdlets to `Microsoft.WinGet.Client`: + +| CLI Command | Proposed Cmdlet | Verb-Noun Justification | +|-------------|----------------|------------------------| +| `winget configure test` | `Test-WinGetConfiguration` | `Test` = validate state compliance | +| `winget configure validate` | `Assert-WinGetConfiguration` | `Assert` = validate schema/syntax | +| `winget configure show` | `Get-WinGetConfigurationDetails` | `Get` = retrieve information | +| `winget download` | `Save-WinGetPackage` | `Save` = download without install (PS convention) | +| `winget repair` | `Repair-WinGetPackage` | `Repair` = fix broken install | +| `winget pin add` | `Add-WinGetPin` | `Add` = create resource | +| `winget pin remove` | `Remove-WinGetPin` | `Remove` = delete resource | +| `winget pin list` | `Get-WinGetPin` | `Get` = list/retrieve | +| `winget pin reset` | `Reset-WinGetPin` | `Reset` = restore defaults | +| `winget settings export` | `Export-WinGetSettings` | `Export` = serialize to file | +| `winget settings set` | `Set-WinGetSetting` | `Set` = modify value | + +### Phase 2: Enhanced Source Management + +Extend `Get-WinGetSource`, `Add-WinGetSource`, `Remove-WinGetSource`: + +- Full parameter parity (custom headers, certificates, authentication tokens) +- `Update-WinGetSource` for refresh operations (equivalent to `winget source update`) +- Source group policy awareness — report when sources are policy-managed with a `PolicyManaged` property + +### Phase 3: SYSTEM Context Support + +Address the fundamental blocker of WinGet operating in SYSTEM context: + +- The COM API (`Microsoft.Management.Deployment`) must properly initialize in SYSTEM context +- Package operations must work without a logged-in user session +- Source management must work in SYSTEM context for Intune and SCCM deployments +- Related to [#6042](https://github.com/microsoft/winget-cli/issues/6042) and [#5991](https://github.com/microsoft/winget-cli/issues/5991) + +### Architecture + +All new cmdlets use the existing COM API rather than wrapping the CLI executable: + +``` +┌──────────────────────────────────┐ +│ PowerShell Cmdlet │ +│ (Microsoft.WinGet.Client) │ +└──────────────┬───────────────────┘ + │ COM Interop + ▼ +┌──────────────────────────────────┐ +│ Microsoft.Management.Deployment │ +│ (WinRT COM API) │ +└──────────────┬───────────────────┘ + │ + ▼ +┌──────────────────────────────────┐ +│ WindowsPackageManagerServer │ +│ (WinGetDev.exe / winget.exe) │ +└──────────────────────────────────┘ +``` + +Benefits: +- Proper error propagation as PowerShell exceptions (`ErrorRecord` with `HRESULT`) +- Structured output objects (not parsed text) +- Cancellation support via `Ctrl+C` / `StopProcessing()` +- Progress reporting via `WriteProgress` +- Pipeline support for batch operations + +### COM API Surface Additions + +New interfaces needed in `Microsoft.Management.Deployment`: + +```idl +// Pin management +interface IPackagePinManager +{ + IAsyncOperation> GetPinsAsync(); + IAsyncOperationWithProgress AddPinAsync(AddPinOptions options); + IAsyncOperationWithProgress RemovePinAsync(RemovePinOptions options); + IAsyncOperationWithProgress ResetPinsAsync(ResetPinOptions options); +} + +// Settings management +interface ISettingsManager +{ + String ExportSettings(); + void SetSetting(String settingPath, String value); + String GetSetting(String settingPath); +} +``` + +### PowerShell Cmdlet Details + +#### Save-WinGetPackage + +```powershell +Save-WinGetPackage + [-Id] + [-Version ] + [-Source ] + [-Architecture ] + [-InstallerType ] + [-Scope ] + [-OutputDirectory ] # Required + [-AcceptSourceAgreements] + [-AcceptPackageAgreements] + [-Force] +``` + +Output: `WinGetDownloadResult` object with `InstallerPath`, `InstallerType`, `Sha256`, `Status`. + +#### Test-WinGetConfiguration + +```powershell +Test-WinGetConfiguration + [-File] + [-AcceptConfigurationAgreements] +``` + +Output: `WinGetConfigurationTestResult` object with per-resource compliance status: + +```powershell +$result = Test-WinGetConfiguration -File .\config.dsc.yaml +$result.UnitResults | Format-Table ResourceName, State, InDesiredState +``` + +#### Add-WinGetPin / Remove-WinGetPin / Get-WinGetPin + +```powershell +# Pin to prevent upgrades +Add-WinGetPin -Id "Microsoft.VisualStudioCode" [-Version ] [-Blocking] + +# List pins +Get-WinGetPin [-Id ] + +# Remove pin +Remove-WinGetPin -Id "Microsoft.VisualStudioCode" + +# Reset all pins +Reset-WinGetPin [-Force] +``` + +### Settings Changes + +No new settings required. Existing settings continue to apply to both CLI and PowerShell module operations identically. + +### Validation Pipeline Impact + +No impact on `winget-pkgs` validation. Validation already uses the COM API for non-interactive operation. + +## UI/UX Design + +### Interactive Mode + +```powershell +PS> Save-WinGetPackage -Id "Git.Git" -OutputDirectory "C:\Packages" + +Id : Git.Git +Version : 2.45.1 +Path : C:\Packages\Git-2.45.1-64-bit.exe +Sha256 : abc123... +Status : Ok +``` + +### Pipeline Support + +```powershell +# Download multiple packages +"Git.Git", "Python.Python.3.12" | ForEach-Object { + Save-WinGetPackage -Id $_ -OutputDirectory "C:\Packages" +} + +# Test configuration and filter failures +$result = Test-WinGetConfiguration -File .\config.dsc.yaml +$result.UnitResults | Where-Object { -not $_.InDesiredState } +``` + +### Non-Interactive / Automation + +```powershell +# Intune remediation script +$pins = Get-WinGetPin +if ($pins | Where-Object { $_.Id -eq "Microsoft.Edge" -and $_.IsBlocking }) { + Write-Output "Edge is pinned - compliant" + exit 0 +} else { + Add-WinGetPin -Id "Microsoft.Edge" -Blocking + exit 1 +} +``` + +### -Force Parameter Behavior + +All new cmdlets support `-Force` to suppress confirmation prompts, consistent with existing cmdlets. + +## Capabilities + +### Accessibility + +No direct impact — PowerShell modules inherit the accessibility of the terminal/host application. All output is text-based and works with screen readers through the PowerShell host. + +### Security + +- SYSTEM context support must not bypass UAC for operations that require elevation in user context +- COM API activation in SYSTEM context validates caller identity +- Settings cmdlets respect Group Policy overrides (throw terminating error when attempting to modify policy-managed settings) +- Token/credential parameters are `SecureString` where applicable + +### Reliability + +- Eliminates the unreliable pattern of `Start-Process winget.exe` with text parsing +- COM API provides structured error codes via `HRESULT` mapped to PowerShell `ErrorRecord` +- Proper cancellation via `StopProcessing()` prevents orphaned installer processes +- Retry logic for transient COM activation failures + +### Compatibility + +- No breaking changes to existing cmdlets +- New cmdlets follow established naming patterns in the module +- Minimum PowerShell version remains 7.2+ (consistent with current module) +- Windows PowerShell 5.1 is NOT supported (consistent with current module) + +### Performance, Power, and Efficiency + +- COM API calls avoid process startup overhead (no `winget.exe` launch per operation) +- Pipelining enables batch operations without repeated COM activation +- `Save-WinGetPackage` supports parallel downloads when used with `ForEach-Object -Parallel` + +## Potential Issues + +1. **SYSTEM context COM activation** — The WinGet COM server (`WindowsPackageManagerServer`) may require architectural changes to support activation outside a user session. This is the highest-risk item. +2. **Configuration cmdlets depend on DSC runtime** — `Test-WinGetConfiguration` requires the DSC v3 runtime to be available. If unavailable, the cmdlet should throw a clear error with installation guidance. +3. **Backwards compatibility testing** — Enterprises may pin to older module versions. New cmdlets must not destabilize existing functionality via assembly loading changes. +4. **COM interface versioning** — New interfaces must be additive to avoid breaking existing COM consumers. Use new interface IIDs. +5. **Scope parameter interactions** — `Save-WinGetPackage` with `-Scope machine` in a non-elevated session should fail early with a clear error, not after download. + +## Future Considerations + +- Full parity enables DSC resources to delegate directly to PowerShell cmdlets +- Opens the path for WinGet to be fully operable as a PowerShell-native tool without the CLI +- Enables richer Intune remediation scripts and proactive remediations +- Azure Automation runbooks can manage packages across fleets +- Potential for a `WinGet` PowerShell drive provider (`Get-ChildItem WinGet:\installed\`) + +## Resources + +- Current module: https://www.powershellgallery.com/packages/Microsoft.WinGet.Client +- COM API source: `src/Microsoft.Management.Deployment/` +- PowerShell module source: `src/PowerShell/Microsoft.WinGet.Client/` +- SYSTEM context issue: https://github.com/microsoft/winget-cli/issues/5991 +- Elevated COM issue: https://github.com/microsoft/winget-cli/issues/6042 +- Scope parameter issue: https://github.com/microsoft/winget-cli/issues/4787 From 334bd64f620ee792de892e3dee23d737409832ac Mon Sep 17 00:00:00 2001 From: Demitrius Nelon Date: Wed, 17 Jun 2026 13:01:59 -0700 Subject: [PATCH 2/3] Clarify two-module architecture in PS parity spec WinGet ships two separate PowerShell modules: - Microsoft.WinGet.Client (package management) - Microsoft.WinGet.Configuration (configuration/DSC operations) Update spec to clearly delineate which cmdlets belong to which module, add Configuration-specific gaps (history, abort, delete), update the architecture diagram, and note module consolidation as a future consideration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../#6288 - PowerShell Module Full Parity.md | 76 +++++++++++++------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/doc/specs/#6288 - PowerShell Module Full Parity.md b/doc/specs/#6288 - PowerShell Module Full Parity.md index 04799c5b0c..e489ce3139 100644 --- a/doc/specs/#6288 - PowerShell Module Full Parity.md +++ b/doc/specs/#6288 - PowerShell Module Full Parity.md @@ -11,7 +11,7 @@ For [#6288](https://github.com/microsoft/winget-cli/issues/6288). ## Abstract -The WinGet PowerShell module (`Microsoft.WinGet.Client`) must achieve full functional parity with the WinGet CLI. Several CLI capabilities have no PowerShell equivalent today, blocking adoption by enterprise automation workflows that operate exclusively in PowerShell contexts (Intune, SCCM, Azure Automation, DSC). +WinGet ships two PowerShell modules — `Microsoft.WinGet.Client` (package management) and `Microsoft.WinGet.Configuration` (configuration/DSC operations). Both must achieve full functional parity with the WinGet CLI. Several CLI capabilities have no PowerShell equivalent today, blocking adoption by enterprise automation workflows that operate exclusively in PowerShell contexts (Intune, SCCM, Azure Automation, DSC). ## Inspiration @@ -19,25 +19,34 @@ IT decision makers evaluating WinGet for large-scale deployment cite incomplete Key gaps identified by enterprise customers: -- No PowerShell equivalent for `winget configure` operations (test, validate, show) +- No PowerShell equivalent for `winget configure` operations (test, validate, show) — these belong in `Microsoft.WinGet.Configuration` - Missing cmdlets for pin management (`winget pin add/remove/list/reset`) - No cmdlet for `winget download` ([#658](https://github.com/microsoft/winget-cli/issues/658) added CLI support) - Missing `winget repair` support in the module ([#148](https://github.com/microsoft/winget-cli/issues/148) added CLI support) - No PowerShell equivalent for `winget settings` management - Limited source management beyond basic add/remove - COM API cannot activate in elevated/SYSTEM context ([#6042](https://github.com/microsoft/winget-cli/issues/6042)) +- `Microsoft.WinGet.Configuration` lacks cmdlets for configuration history, abort, and delete operations ## Solution Design -### Phase 1: Core Cmdlet Gaps +### Module Architecture + +WinGet ships two separate PowerShell modules with distinct responsibilities: + +| Module | Responsibility | Gallery Link | +|--------|---------------|--------------| +| `Microsoft.WinGet.Client` | Package lifecycle — install, upgrade, uninstall, search, source, pin, download, settings | [PSGallery](https://www.powershellgallery.com/packages/Microsoft.WinGet.Client) | +| `Microsoft.WinGet.Configuration` | Configuration operations — apply, test, validate, show, history | [PSGallery](https://www.powershellgallery.com/packages/Microsoft.WinGet.Configuration) | + +Both modules communicate with the WinGet COM API (`Microsoft.Management.Deployment`) but surface different subsets of functionality. This spec covers parity gaps in **both** modules. + +### Phase 1: Core Cmdlet Gaps — Microsoft.WinGet.Client Add the following cmdlets to `Microsoft.WinGet.Client`: | CLI Command | Proposed Cmdlet | Verb-Noun Justification | |-------------|----------------|------------------------| -| `winget configure test` | `Test-WinGetConfiguration` | `Test` = validate state compliance | -| `winget configure validate` | `Assert-WinGetConfiguration` | `Assert` = validate schema/syntax | -| `winget configure show` | `Get-WinGetConfigurationDetails` | `Get` = retrieve information | | `winget download` | `Save-WinGetPackage` | `Save` = download without install (PS convention) | | `winget repair` | `Repair-WinGetPackage` | `Repair` = fix broken install | | `winget pin add` | `Add-WinGetPin` | `Add` = create resource | @@ -47,6 +56,19 @@ Add the following cmdlets to `Microsoft.WinGet.Client`: | `winget settings export` | `Export-WinGetSettings` | `Export` = serialize to file | | `winget settings set` | `Set-WinGetSetting` | `Set` = modify value | +### Phase 1b: Core Cmdlet Gaps — Microsoft.WinGet.Configuration + +Add or enhance the following cmdlets in `Microsoft.WinGet.Configuration`: + +| CLI Command | Proposed Cmdlet | Verb-Noun Justification | +|-------------|----------------|------------------------| +| `winget configure test` | `Test-WinGetConfiguration` | `Test` = validate state compliance | +| `winget configure validate` | `Assert-WinGetConfiguration` | `Assert` = validate schema/syntax | +| `winget configure show` | `Get-WinGetConfigurationDetails` | `Get` = retrieve information | +| `winget configure list` | `Get-WinGetConfigurationHistory` | `Get` = list previous runs | +| `winget configure abort` | `Stop-WinGetConfiguration` | `Stop` = cancel in-progress operation | +| `winget configure delete` | `Remove-WinGetConfigurationHistory` | `Remove` = delete record | + ### Phase 2: Enhanced Source Management Extend `Get-WinGetSource`, `Add-WinGetSource`, `Remove-WinGetSource`: @@ -66,25 +88,26 @@ Address the fundamental blocker of WinGet operating in SYSTEM context: ### Architecture -All new cmdlets use the existing COM API rather than wrapping the CLI executable: +Both modules use the existing COM API rather than wrapping the CLI executable: ``` -┌──────────────────────────────────┐ -│ PowerShell Cmdlet │ -│ (Microsoft.WinGet.Client) │ -└──────────────┬───────────────────┘ - │ COM Interop - ▼ -┌──────────────────────────────────┐ -│ Microsoft.Management.Deployment │ -│ (WinRT COM API) │ -└──────────────┬───────────────────┘ - │ - ▼ -┌──────────────────────────────────┐ -│ WindowsPackageManagerServer │ -│ (WinGetDev.exe / winget.exe) │ -└──────────────────────────────────┘ +┌──────────────────────────────────┐ ┌──────────────────────────────────┐ +│ Microsoft.WinGet.Client │ │ Microsoft.WinGet.Configuration │ +│ (Package management cmdlets) │ │ (Configuration/DSC cmdlets) │ +└──────────────┬───────────────────┘ └──────────────┬───────────────────┘ + │ COM Interop │ COM Interop + └───────────────┬─────────────────────┘ + ▼ + ┌──────────────────────────────────┐ + │ Microsoft.Management.Deployment │ + │ (WinRT COM API) │ + └──────────────┬───────────────────┘ + │ + ▼ + ┌──────────────────────────────────┐ + │ WindowsPackageManagerServer │ + │ (WinGetDev.exe / winget.exe) │ + └──────────────────────────────────┘ ``` Benefits: @@ -269,12 +292,15 @@ No direct impact — PowerShell modules inherit the accessibility of the termina - Enables richer Intune remediation scripts and proactive remediations - Azure Automation runbooks can manage packages across fleets - Potential for a `WinGet` PowerShell drive provider (`Get-ChildItem WinGet:\installed\`) +- **Module consolidation**: Evaluate whether `Microsoft.WinGet.Client` and `Microsoft.WinGet.Configuration` should eventually merge into a single `Microsoft.WinGet` module. Keeping them separate today reduces install footprint for users who only need package management, but the split adds complexity for users who need both. A unified module could be considered once the configuration surface stabilizes. ## Resources -- Current module: https://www.powershellgallery.com/packages/Microsoft.WinGet.Client +- Current Client module: https://www.powershellgallery.com/packages/Microsoft.WinGet.Client +- Current Configuration module: https://www.powershellgallery.com/packages/Microsoft.WinGet.Configuration - COM API source: `src/Microsoft.Management.Deployment/` -- PowerShell module source: `src/PowerShell/Microsoft.WinGet.Client/` +- PowerShell Client module source: `src/PowerShell/Microsoft.WinGet.Client/` +- PowerShell Configuration module source: `src/PowerShell/Microsoft.WinGet.Configuration/` - SYSTEM context issue: https://github.com/microsoft/winget-cli/issues/5991 - Elevated COM issue: https://github.com/microsoft/winget-cli/issues/6042 - Scope parameter issue: https://github.com/microsoft/winget-cli/issues/4787 From 72468210874e44065e1264df83bf968b5363755a Mon Sep 17 00:00:00 2001 From: Demitrius Nelon Date: Wed, 17 Jun 2026 14:40:18 -0700 Subject: [PATCH 3/3] Address review feedback from Trenly - Remove detailed IDL interface definitions (too implementation-level for a spec; keep high-level description of needed COM surface) - Use PinType property instead of IsBlocking for future expandability (supports checking gating pins too) - Add PR #6190 (Configuration module improvements) to Resources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../#6288 - PowerShell Module Full Parity.md | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/doc/specs/#6288 - PowerShell Module Full Parity.md b/doc/specs/#6288 - PowerShell Module Full Parity.md index e489ce3139..5bcb341c36 100644 --- a/doc/specs/#6288 - PowerShell Module Full Parity.md +++ b/doc/specs/#6288 - PowerShell Module Full Parity.md @@ -119,26 +119,7 @@ Benefits: ### COM API Surface Additions -New interfaces needed in `Microsoft.Management.Deployment`: - -```idl -// Pin management -interface IPackagePinManager -{ - IAsyncOperation> GetPinsAsync(); - IAsyncOperationWithProgress AddPinAsync(AddPinOptions options); - IAsyncOperationWithProgress RemovePinAsync(RemovePinOptions options); - IAsyncOperationWithProgress ResetPinsAsync(ResetPinOptions options); -} - -// Settings management -interface ISettingsManager -{ - String ExportSettings(); - void SetSetting(String settingPath, String value); - String GetSetting(String settingPath); -} -``` +New COM interfaces will be needed in `Microsoft.Management.Deployment` for pin management and settings. These should expose async operations consistent with the existing API patterns (e.g., `IAsyncOperationWithProgress` for pin add/remove/reset, string-based settings get/set/export). Detailed interface design will be determined during implementation. ### PowerShell Cmdlet Details @@ -231,7 +212,7 @@ $result.UnitResults | Where-Object { -not $_.InDesiredState } ```powershell # Intune remediation script $pins = Get-WinGetPin -if ($pins | Where-Object { $_.Id -eq "Microsoft.Edge" -and $_.IsBlocking }) { +if ($pins | Where-Object { $_.Id -eq "Microsoft.Edge" -and $_.PinType -eq "Blocking" }) { Write-Output "Edge is pinned - compliant" exit 0 } else { @@ -304,3 +285,4 @@ No direct impact — PowerShell modules inherit the accessibility of the termina - SYSTEM context issue: https://github.com/microsoft/winget-cli/issues/5991 - Elevated COM issue: https://github.com/microsoft/winget-cli/issues/6042 - Scope parameter issue: https://github.com/microsoft/winget-cli/issues/4787 +- Configuration module improvements PR: https://github.com/microsoft/winget-cli/pull/6190