Actions/Target/WinRM.ps1

<#
.SYNOPSIS
Target action: evaluate resources against a remote computer over WinRM (#57 §4).
 
.DESCRIPTION
Builds both a CimSession and a PSSession for the same remote computer, so either engine can use
whichever session shape it needs without the caller having to know which one in advance.
 
The PSSession is what both engine actions use to reach the far side today - DscV2 runs
Invoke-DscResource there and DscV3 runs dsc.exe there, both via Invoke-Command. The CimSession is
kept because it is the only remote shape Windows PowerShell 5.1's Invoke-DscResource accepts
(-CimSession), and because a CIM query against the target is useful in its own right; PowerShell
7's PSDesiredStateConfiguration 2.x dropped that parameter, so it is a fallback rather than the
primary path (Actions/Engine/DscV2.ps1).
 
'ConfigurationName' selects which WinRM endpoint the PSSession lands on, and it matters for DSC
v2. New-PSSession without it connects to the host's default endpoint, which on Windows is Windows
PowerShell 5.1 - and a current Windows build no longer carries an in-box Invoke-DscResource there,
so a remote DSC v2 evaluation has nothing to run (the self-hosted runner, Windows 10.0.26100,
reports exactly that). Naming the PowerShell 7 endpoint - 'PowerShell.7', registered by
Enable-PSRemoting under pwsh - lands the session where PSDesiredStateConfiguration 2.x is
installed. It is passed to New-PSSession only: a CimSession is a WS-Man/CIM connection with no
PowerShell endpoint to choose.
 
The identity matters as much as the endpoint. New-CimSession/New-PSSession authenticate as the
credential when the Target context carries one, and otherwise as the account the runner process
itself runs as - on a self-hosted agent that is the agent service account, which installs as
NT AUTHORITY\NETWORK SERVICE and reaches a target as the computer account. That account has to
be a member of the target's Remote Management Users group to connect at all, and of local
Administrators to apply DSC (Invoke-DscResource drives the CIM/DSC subsystem) or to be restarted
after a RebootRequired. WikiSource/Remote-Targets-and-Credentials.md documents the full set.
 
Validated at two levels: the unit suite mocks New-CimSession/New-PSSession to assert dispatch and
parameter passing, and Tests/.../Integration/WinRMTarget.Integration.tests.ps1 opens real sessions
against a live WinRM listener on the self-hosted Windows runner (DscV2-SelfHosted.yml) and proves
the shipped DscV2 engine action completes a real evaluation over the session.
 
.PARAMETER Context
Hashtable with:
  ComputerName [string] - required, the remote computer name or IP.
  Credential [PSCredential] - optional, resolved beforehand via the Credential hook (#57 §5).
  ConfigurationName [string] - optional, the remote WinRM endpoint for the PSSession.
 
.OUTPUTS
[hashtable] @{ ComputerName; IsRemote = $true; CimSession; PSSession }
#>

param(
    [hashtable]$Context = @{}
)

if ([string]::IsNullOrWhiteSpace([string]$Context.ComputerName)) {
    throw "[Actions/Target/WinRM] 'ComputerName' is required in the Target context."
}

$sessionParams = @{ ComputerName = [string]$Context.ComputerName }
if ($Context.Credential) {
    $sessionParams.Credential = $Context.Credential
}

$cimSession = New-CimSession @sessionParams

# Only the PSSession takes an endpoint name, so it is added to a copy rather than to the shared
# hashtable - New-CimSession has no -ConfigurationName and would fail to bind it.
$psSessionParams = @{} + $sessionParams
if (-not [string]::IsNullOrWhiteSpace([string]$Context.ConfigurationName)) {
    $psSessionParams.ConfigurationName = [string]$Context.ConfigurationName
}

$psSession = New-PSSession @psSessionParams

return @{
    ComputerName = [string]$Context.ComputerName
    IsRemote     = $true
    CimSession   = $cimSession
    PSSession    = $psSession
}