225 lines
7.0 KiB
Markdown
Executable File
225 lines
7.0 KiB
Markdown
Executable File
---
|
||
note type:
|
||
- ess
|
||
- note
|
||
date: 2026-06-11
|
||
done:
|
||
---
|
||
## Reference script (TMC `Session.ps1`)
|
||
|
||
### 1. What is the pool key?
|
||
|
||
The **server name** string (`$serverName`).
|
||
|
||
```mermaid
|
||
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?
|
||
|
||
```mermaid
|
||
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-PSSession` is 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 |
|
||
|
||
```mermaid
|
||
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`](deploy/scripts/modules/capabilities/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 and `New-PSSession` target.
|
||
- **`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.
|
||
|
||
```mermaid
|
||
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`](deploy/scripts/modules/core/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
|
||
|
||
```mermaid
|
||
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:
|
||
|
||
```mermaid
|
||
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`
|
||
|
||
```mermaid
|
||
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-Log`** was 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`](deploy/tests/modules/capabilities/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 |
|
||
|