7.0 KiB
Executable File
note type, date, done
| note type | date | done | ||
|---|---|---|---|---|
|
2026-06-11 |
Reference script (TMC Session.ps1)
1. What is the pool key?
The server name string ($serverName).
flowchart LR
serverName["serverName: APP01"]
pool["remoteSessionPool hashtable"]
session["PSSession object"]
serverName -->|"used as key"| pool
pool --> session
Same server name → same pool entry. Different server → different entry.
Caveat for your task: if the same server could be reached with different credentials, keying only on server name can return the wrong session. For a first version, matching the reference (key = server name) is fine if credentials are consistent per server.
2. What happens on the second call for the same server?
sequenceDiagram
participant Caller
participant GetRemoteSession as Get-RemoteSession
participant Pool
participant NewPSSession as New-PSSession
Caller->>GetRemoteSession: serverName = APP01
GetRemoteSession->>Pool: lookup APP01
Pool-->>GetRemoteSession: session found
GetRemoteSession-->>Caller: existing session
Note over NewPSSession: not called
- Pool lookup hits.
- Logs something like “Using existing remote session”.
- Returns the cached session.
New-PSSessionis not called — that’s the whole point of the pool.
3. When is New-PSSession called vs skipped?
| Condition | Action |
|---|---|
Pool has an entry for $serverName |
Skip New-PSSession, return cached session |
Pool has no entry for $serverName |
Call New-PSSession, store result in pool, return it |
flowchart TD
start[Get-RemoteSession called]
lookup{Pool contains serverName?}
reuse[Log: using existing session]
create[Log: creating session]
newPs[New-PSSession]
store[Store in pool]
return[Return session]
start --> lookup
lookup -->|yes| reuse --> return
lookup -->|no| create --> newPs --> store --> return
Important: Get-RemoteSession creates/reuses the session. Invoke-Command -Session $session is a separate step the caller does afterward (e.g. Test-Path on the remote box).
4. What happens when session creation fails? Does that fit ESS?
In the reference: if $session is falsy after New-PSSession, it logs an error and runs exit (ends the whole PowerShell process).
In ESS capabilities: that does not fit. Look at Remove-Folder.psm1 — failures use throw, not exit.
| TMC reference | ESS pattern | |
|---|---|---|
| Failure | exit |
throw "message" |
| Why | Script runner stops everything | Pester, pipelines, and orchestrators can catch/handle it |
For your module: log with Write-Log -Level ERROR, then throw so deployment fails cleanly and tests can use Should -Throw.
Design decisions (for your implementation)
Parameters
Match the reference, adapted to ESS style:
ServerName(mandatory) — pool key andNew-PSSessiontarget.Credential(optional) — pass through when present; omit when using default/current-user auth.
Do not put remote script logic (e.g. Test-Path) inside Get-RemoteSession. Callers get a session, then run Invoke-Command themselves.
Pool scope — where does the hashtable live?
At module scope in the .psm1, above the function — not inside the function body.
flowchart TB
subgraph psm1 [Get-RemoteSession.psm1]
poolVar["script-scoped pool hashtable"]
func[Get-RemoteSession function]
poolVar --> func
end
call1[First call] --> func
call2[Second call] --> func
func --> poolVar
If the pool is inside the function, it is recreated every call and caching never works.
Use $script: scope so the pool belongs to the module instance, not the global session (unless you deliberately want global — you don’t for this task).
Naming: Get-RemoteSession vs New-RemoteSession
| Verb | Meaning here |
|---|---|
New-* |
Always creates something new |
Get-* |
Retrieves existing or obtains one if needed |
Your function is “get from pool, or create and cache” → Get-RemoteSession is the right approved verb.
Logging
Use Write-Log (from Core.Logging.psm1), not TMC’s Logging.
Worth logging:
- INFO — “Using existing…” vs “Creating…”
- INFO — elapsed time for creation only (optional but matches the reference and helps debug slow WinRM)
- ERROR — before you throw on failure
Failure behavior
flowchart LR
fail[New-PSSession fails or returns nothing]
log[Write-Log ERROR]
throwNode[throw with clear message]
fail --> log --> throwNode
No exit. Callers and Pester expect exceptions, not process termination.
Pool key edge case (same server, different credentials)
Reference keys only on server name. Implication:
flowchart TD
callA["Call: APP01 + CredA"] --> pool1[Pool stores APP01 → sessionA]
callB["Call: APP01 + CredB"] --> pool2[Pool returns sessionA]
pool2 --> wrong[Wrong cred — sessionA reused]
For learning / v1: same as reference is OK if each server always uses one credential set. If that’s not true later, the key would need to include something credential-related — out of scope unless your manifests require it.
How this connects to Invoke-Command
sequenceDiagram
participant Step as Deployment step
participant Get as Get-RemoteSession
participant IC as Invoke-Command
Step->>Get: ServerName, Credential
Get-->>Step: $session
Step->>IC: -Session $session -ScriptBlock { Test-Path ... }
IC-->>Step: $fileExists
Get-RemoteSession = connection management.
Invoke-Command = run code on the remote machine.
Testing note (relevant to your attached snippet)
For “fails when session cannot be created”, assert like other capabilities:
{ Get-RemoteSession ... } | Should -Throw— proves the function throws.- Optionally assert
Write-Logwas called with-Level ERROR.
Should -Invoke Throw is not valid Pester — throw is a language statement, not a mockable cmdlet. Same idea as Remove-Folder.Tests.ps1: use Should -Throw on the script block, and Should -Invoke Write-Log for the error log line.
For pool behavior tests: mock New-PSSession, call the function twice with the same server, expect New-PSSession invoked once.
One-line summary
| Question | Answer |
|---|---|
| Pool key | Server name |
| Second call | Return cached session; no New-PSSession |
| When to create | Only when key missing from pool |
| On failure | Reference: exit → you: Write-Log + throw |
| Pool location | Module-level $script: hashtable, not inside the function |