Skip to content

Added support for Windows RSAT Modules#136

Open
justinainsworth wants to merge 8 commits into
PowerShellOrg:mainfrom
justinainsworth:RSATDependencies
Open

Added support for Windows RSAT Modules#136
justinainsworth wants to merge 8 commits into
PowerShellOrg:mainfrom
justinainsworth:RSATDependencies

Conversation

@justinainsworth
Copy link
Copy Markdown

@justinainsworth justinainsworth commented Dec 15, 2021

Adds a WindowsRSAT DependencyType for installing Windows RSAT modules
via Add-WindowsCapability (Workstation) or Install-WindowsFeature
(Server). Originally authored by @justinainsworth.

Fixes verified against live catalogs

Change Verified on
BitLocket -> BitLocker typo in Server feature name Server 2022
Huper-V -> Hyper-V typo in Server feature name Server 2022
GroupPolicy Server feature: ''RSAT'' umbrella -> ''GPMC'' (targeted) Server 2022
Drop BitsTransfer mapping (module ships in-box on Server and client) Server 2022 + Win 11

All 11 other mapping entries verified present in the Server 2022 catalog
and (where applicable) the Win 11 Workstation capability catalog.

Reliability fixes surfaced by testing

  • Mockable admin check. Inline WindowsPrincipal.IsInRole(Administrator)
    extracted to PSDepend/Private/Test-Administrator.ps1 so the install
    branch can be exercised in unit tests without elevation.
  • Strict-mode handling for unknown modules. The original
    $RSAT_MODULE_MAP[$ModuleName][$Type] chain threw "Cannot index into
    a null array" under Set-StrictMode -Version 2+ before the explicit
    "Unknown Module" message could fire. Added a ContainsKey guard.

Tests

12 Pester tests in Tests/WindowsRSAT.Type.Tests.ps1 (follows the
Chocolatey.Type.Tests.ps1 skip pattern) covering Test/Install dispatch
on Server and Workstation, mapping correctness for the fixed entries,
unknown-module handling, the admin guard, and the Test, Install
short-circuit.

Local run on Win 11 (non-admin): 12 passed, 0 failed, 0 skipped.

Out of scope (follow-ups)

  • Per-entry pruning of non-RSAT mappings on client (Hyper-V,
    IISAdministration)
  • Clearer error when a module is in the table but has no entry for the
    current OS path (currently reuses "Unknown Module")
  • Support for Win 11''s newer RSAT capabilities (IPAM, WSUS, ServerManager,
    etc.)
  • Verification on Server 2016/2019 (provider effectively targets
    Server 2022+; Server 2016 lacks several of the targeted feature names)

Refs #160.

Co-Authored-By: @justinainsworth

@HeyItsGilbert
Copy link
Copy Markdown
Member

Hey @justinainsworth I'm not opposed to this. Let me know if you're still interested in a review. If not (or I don't hear from you by next Fri), we'll close this out and try again a later.

tablackburn and others added 6 commits May 15, 2026 23:07
The original PR contained two typos in WindowsFeature names that would
cause Install-WindowsFeature to fail on Windows Server:

- 'BitLocket' -> 'BitLocker' in RSAT-Feature-Tools-BitLocker-RemoteAdminTool
- 'Huper-V'   -> 'Hyper-V'   in RSAT-Hyper-V-Tools

Verified against Windows Server 2022 catalog via Get-WindowsFeature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mapping pointed to 'RSAT', the umbrella Remote Server Administration
Tools feature. Installing it pulls in every RSAT sub-feature on Server,
not just Group Policy management. The targeted feature is GPMC
("Group Policy Management"), which installs the GroupPolicy module
without the rest of RSAT.

Verified against Windows Server 2022 catalog via Get-WindowsFeature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The BitsTransfer module ships in-box on both Windows Server and Windows
client; no RSAT install is required for its cmdlets. The mapping pointed
to 'RSAT-Bits-Server' (BITS Server Extensions Tools), which manages
remote BITS server endpoints rather than the BitsTransfer cmdlets. With
the in-box module present, the Get-Module -ListAvailable check at the
top of the script short-circuits before reaching the install branch, so
the mapping was both wrong and effectively dead code.

No Rsat.Bits.* capability (Win 11) or RSAT-BitsTransfer-* feature
(Server 2022) exists to map this to, so removing the entry.

Verified against Server 2022 catalog and Win 11 capability list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves the inline WindowsPrincipal/WindowsBuiltInRole.Administrator check
into PSDepend/Private/Test-Administrator.ps1. The module loader already
dot-sources everything under Private/ (see PSDepend.psm1), so the helper
becomes a module-scope function alongside the existing 20 private helpers.

This makes the Install branch's admin guard mockable from Pester via
InModuleScope PSDepend { Mock Test-Administrator }, so the dispatch
tests in the upcoming test file run on any developer machine regardless
of elevation rather than skipping on non-admin hosts.

No behavior change: the helper performs the same check as the inline
expression it replaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original guard used $RSAT_MODULE_MAP[$ModuleName][$Type] to detect
an unknown module and relied on the chain returning $null. Under
PowerShell strict mode (Set-StrictMode -Version 2+), indexing into a
$null array throws "Cannot index into a null array" before the explicit
throw is reached -- making the "Unknown Module" error unreachable
whenever the module name was missing from the map entirely.

Adds a ContainsKey guard before the index chain so the explicit error
message is reached under strict and non-strict modes alike. Surfaced by
the new "Throws when the module name is not in the mapping table" test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Tests/WindowsRSAT.Type.Tests.ps1 with 12 tests covering:

- Test-only action: returns $false when missing, $true when available
- Install on Server (ProductType=3): dispatches to Install-WindowsFeature
  with the correct mapped name for ActiveDirectory (baseline) plus
  BitLocker, Hyper-V, and GroupPolicy/GPMC (the three Server-side fixes
  from the preceding commits)
- Install on Workstation (ProductType=1): dispatches to
  Add-WindowsCapability with the correct mapped name
- Unknown module name throws "Unknown Module"
- Install gated by admin check: Test-Administrator -> $false throws
- Test, Install short-circuits when the module is already available

Test-PSDependTypeSupportedHere skips the entire Describe on non-Windows.
The script's admin check is mocked via Test-Administrator (extracted in
the preceding refactor commit) so all 12 tests run regardless of the
test runner's elevation status.

Install-WindowsFeature ships only on Windows Server (ServerManager
module). The BeforeAll injects stub functions into the PSDepend module
scope when the real cmdlets are missing, so Pester's Mock has a command
to attach to on hosts that don't ship them (e.g. Win 11 client when
testing the Server dispatch path locally).

Verified locally on Win 11 (non-admin): 12 passed, 0 failed, 0 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 16, 2026 03:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new WindowsRSAT dependency type so PSDepend can install/import RSAT-related PowerShell modules through Windows features on servers or Windows capabilities on workstations.

Changes:

  • Added WindowsRSAT.ps1 dependency script with RSAT module-to-feature/capability mappings.
  • Registered WindowsRSAT in PSDependMap.psd1.
  • Added administrator detection and WindowsRSAT tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
PSDepend/PSDependScripts/WindowsRSAT.ps1 Implements RSAT dependency handling and installation dispatch.
PSDepend/PSDependMap.psd1 Registers the new WindowsRSAT dependency type.
PSDepend/Private/Test-Administrator.ps1 Adds an admin-role helper used before installing RSAT components.
Tests/WindowsRSAT.Type.Tests.ps1 Adds tests for WindowsRSAT test/install behavior and platform dispatch.
Comments suppressed due to low confidence (1)

Tests/WindowsRSAT.Type.Tests.ps1:15

  • This second import references the same missing Tests/Shared/TestHelpers.psm1 file. Even if discovery setup is fixed or removed, BeforeAll will fail here until the helper module is added or the tests use existing repository setup patterns.
    Import-Module (Join-Path $PSScriptRoot 'Shared/TestHelpers.psm1') -Force

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +6
#requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }

BeforeDiscovery {
Import-Module (Join-Path $PSScriptRoot 'Shared/TestHelpers.psm1') -Force
$script:SkipUnsupported = -not (Test-PSDependTypeSupportedHere -DependencyType 'WindowsRSAT')
}
Comment on lines +4 to +5
Import-Module (Join-Path $PSScriptRoot 'Shared/TestHelpers.psm1') -Force
$script:SkipUnsupported = -not (Test-PSDependTypeSupportedHere -DependencyType 'WindowsRSAT')
[OutputType([bool])]
param()

([Security.Principal.WindowsPrincipal]::new(
$null = install-windowsfeature -name $RSAT_MODULE_MAP[$ModuleName][$Type]
}
else {
$null = Add-WindowsCapability -Online -Name $RSAT_MODULE_MAP[$ModuleName][$Type]
Comment on lines +10 to +11
& "$PSScriptRoot\..\build.ps1" -Task 'Build'
}
}

if ($null -eq $RSAT_MODULE_MAP[$ModuleName][$type]) {
throw "Unknown Module $ModuleName"
…n PS 5.1

The previous BeforeAll only injected stub functions when Get-Command
found no real Install-WindowsFeature / Add-WindowsCapability. On the
Windows PowerShell 5.1 CI matrix entry, ServerManager auto-loads on
Server 2022 and the real Install-WindowsFeature is present, so the stub
was skipped. Pester then mocked the CDXML cmdlet, but the mock silently
did not intercept calls from the script under test -- all four
Server-dispatch tests reported "was called 0 times" while the rest
passed. (PS 7 matrix entries are unaffected since ServerManager does
not load on PowerShell Core, so the stub path was always taken.)

PowerShell resolves functions before cmdlets in the same scope, so
injecting the stub unconditionally lets it take precedence over the real
cmdlet, and Pester reliably mocks the function across all matrix entries.

Verified locally on Win 11 in both PowerShell 7 and Windows PowerShell
5.1: 12 passed, 0 failed, 0 skipped on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants