Hosts, Sessions, Runspaces, Processes, and the Execution Model
Build a practical execution model from terminal and PowerShell host through pwsh process, runspace, session state, command parsing and binding, output streams, and native child processes.
Learning objectives
By the end of this lesson
- Differentiate a terminal, PowerShell host, pwsh process, runspace, and session state.
- Explain why interactive terminals and non-interactive CI runners can behave differently.
- Inspect host, PID, working directory, process path, and process environment state.
- Describe the high-level parse → resolve → bind → execute → emit execution path.
- Predict which state persists inside one session and which state crosses or disappears at a new-process boundary.
1. Build the execution model from the outside inward
When you type a PowerShell command, several layers cooperate. Beginners often call all of them “PowerShell,” which makes failures hard to localize. A useful mental model starts outside the engine and moves inward.
A terminal is the interface presenting the session.
A PowerShell host is the program or component that
connects the PowerShell engine to an environment such as a console,
editor, or application. A normal pwsh launch creates an
operating-system process: an isolated running
instance with its own process ID, environment, memory, and lifetime.
Inside a PowerShell process, code executes in a
runspace. A runspace is a PowerShell execution
environment containing the engine state needed to run commands. It
has session state: variables, functions, aliases,
loaded modules, current location, and other state visible to that
runspace. Advanced applications can host multiple runspaces in one
process; Chapter 17 studies that in detail. For now, one interactive
pwsh process with one main runspace is the right model.
Commands executed in that runspace can remain inside PowerShell or
start a child process when you invoke a native
executable such as git, docker, or another
pwsh.
2. Follow one command through the engine
flowchart TD A["Terminal or automation runner"] --> B["PowerShell host / pwsh process"] B --> C["Runspace and session state"] C --> D["Parse input"] D --> E["Resolve command name"] E --> F["Bind arguments to parameters"] F --> G["Execute command"] G --> H["Emit objects and streams"] H --> I["Pipeline continues or host renders output"] G --> J["Native child process when invoked"]
Terminal or automation runner: a human terminal
sends keystrokes interactively, while a CI runner may start
pwsh with a script file and no human attached.
Host and process: the host presents engine
services, while the operating system owns the
pwsh process, gives it a PID, and controls resources
and environment inheritance.
Runspace and session state: this is where PowerShell variables, functions, modules, aliases, providers, and current state live for execution.
Parse: PowerShell reads the input and determines its syntactic structure. Parsing is why quotation marks, parentheses, variables, and operators have defined meanings rather than being arbitrary text.
Resolve: PowerShell determines what command name refers to—a cmdlet, function, alias, script, application, or another command type. Chapter 02 teaches the resolution rules and discovery tools.
Bind: PowerShell matches supplied arguments and pipeline input to command parameters. Chapter 03 explores pipeline parameter binding in depth.
Execute and emit: the command runs and can write success output objects or other streams such as errors, warnings, verbose messages, and information. Later chapters examine those streams formally.
Render or continue: output can move into another pipeline command or eventually reach a host that formats and displays it. If the command launches a native executable, part of the work crosses into a separate process with different conventions.
3. Inspect the current process, host, location, and environment
PowerShell exposes each layer through different evidence.
$Host tells you about the PowerShell host interface; it
does not tell you the operating system. $PID identifies
the current operating-system process.
Get-Location reports the current PowerShell location.
$env:NAME accesses a process environment variable.
$Host | Select-Object Name, Version
Get-Process -Id $PID |
Select-Object Id, ProcessName, Path, StartTime
Get-Location
[pscustomobject]@{
ProcessId = $PID
HostName = $Host.Name
UserName = [System.Environment]::UserName
MachineName = [System.Environment]::MachineName
PSHome = $PSHOME
PathEntries = ($env:PATH -split [IO.Path]::PathSeparator).Count
}
These values have different lifetimes. The process ID exists only for this process. A PowerShell variable normally lives in session state and disappears when the process ends. Environment variables exist in the process environment; a child process normally receives a copy of the parent's current environment when it starts. The working directory is also normally inherited when a child process is created, although applications can deliberately change it.
“Session state” and “machine state” are not the same thing.
Assigning $Name = 'value' changes a PowerShell
variable in the current runspace. Assigning
$env:NAME = 'value' changes the environment of the
current process and can affect subsequently launched child
processes. Neither assignment automatically creates a permanent
machine-wide setting.
4. Interactive and non-interactive execution have different assumptions
An interactive session expects a person to be present. It may provide command history, tab completion, predictions, colored output, prompts, and profile customizations. You can inspect an error and immediately try another command.
A non-interactive execution is launched to perform work without a conversation. CI/CD runners, scheduled jobs, containers, deployment agents, and remote automation frequently start PowerShell this way. They may begin in a clean workspace, use a service account, omit your profile, inject environment variables, impose a timeout, collect streams into logs, and destroy the worker after the job finishes.
This is why “it worked in my terminal” is incomplete evidence. Your
personal session may contain a module imported by your profile, a
modified PATH, a credential cached by a developer tool,
a variable created ten minutes ago, or a working directory that a CI
runner never uses.
# Inspect some state that often differs between an interactive workstation and CI.
[pscustomobject]@{
HostName = $Host.Name
ProcessId = $PID
WorkingDirectory = (Get-Location).Path
HasUserProfile = Test-Path $PROFILE
CI = $env:CI
PathEntryCount = ($env:PATH -split [IO.Path]::PathSeparator).Count
}
$env:CI is not a universal PowerShell variable; many CI
systems set an environment variable with that name, but you must
read your provider's contract. The lesson is to inspect supplied
state rather than assume it.
5. Command resolution happens before execution
Suppose you type Get-Process. PowerShell first parses
the command and then resolves the name to something executable. If
the name is unknown, execution cannot begin. If several command
types share a name, precedence rules determine which one wins. That
is why production code should be understandable about what it
invokes.
Get-Command Get-Process |
Select-Object Name, CommandType, Source, Version
You will use Get-Command extensively in Chapter 02. For
now, notice that discovery is a distinct phase from running the
command. A reliable diagnostic question is:
What did PowerShell resolve this name to in this environment?
After resolution, PowerShell performs
parameter binding. Parameters are named inputs such
as -Id in Get-Process -Id $PID. The engine
converts and associates supplied values with a valid parameter set
before the command body performs its work. Binding errors therefore
happen before the requested operation can succeed.
6. Commands can emit more than one kind of output
PowerShell commands can produce normal success output and several diagnostic streams. You will later study the success, error, warning, verbose, debug, information, and progress channels in detail. The high-level point in Chapter 01 is that “what appeared in the terminal” may combine information with different purposes.
Write-Output "success output"
Write-Warning "warning output"
Write-Verbose "verbose output" -Verbose
Success output is usually the data intended for pipeline processing. Warning and verbose output carry diagnostic meaning. A CI system may capture or display these streams differently. Designing automation becomes easier when you do not treat every visible line as one undifferentiated text stream.
7. Native executables create a process boundary
When PowerShell invokes a native executable, the operating system starts another process. That child does not receive PowerShell variables, functions, aliases, or in-memory objects. It receives operating-system process inputs such as command-line arguments, inherited environment variables, standard input/output/error handles, and a working directory.
This boundary explains many cross-shell bugs. PowerShell may hold a rich object, but a native program normally expects strings as command-line arguments. A native program may return text and a numeric exit code rather than a PowerShell error object. Chapter 02 examines that boundary carefully.
# This starts another PowerShell process, making the boundary easy to observe.
"Parent PID: $PID"
pwsh -NoProfile -Command '"Child PID: $PID"'
The two PIDs should differ. The child process exists temporarily, performs the command, and exits. The parent PowerShell process continues.
8. Experiment: what persists, what is inherited, and what disappears?
This experiment distinguishes three kinds of state: a PowerShell variable, a process environment variable, and the working directory. It makes no permanent configuration changes.
$SessionMarker = "created in parent PID $PID"
$env:PS_ACADEMY_SESSION_TEST = "parent-environment"
$parentLocation = (Get-Location).Path
"--- parent before child ---"
"SessionMarker: $SessionMarker"
"Environment marker: $env:PS_ACADEMY_SESSION_TEST"
"Location: $parentLocation"
"--- child process ---"
pwsh -NoProfile -Command @'
"Child PID: $PID"
$marker = Get-Variable SessionMarker -ErrorAction SilentlyContinue
"SessionMarker exists: $([bool]$marker)"
"Environment marker: $env:PS_ACADEMY_SESSION_TEST"
"Location: $((Get-Location).Path)"
$env:PS_ACADEMY_SESSION_TEST = "changed-in-child"
"Child changed marker to: $env:PS_ACADEMY_SESSION_TEST"
'@
"--- parent after child ---"
"SessionMarker: $SessionMarker"
"Environment marker: $env:PS_ACADEMY_SESSION_TEST"
"Location: $((Get-Location).Path)"
Three observations matter. First, $SessionMarker does
not appear in the child because ordinary PowerShell variables live
in the parent's session state. Second, the child normally
does see PS_ACADEMY_SESSION_TEST because
environment variables are inherited when the process starts. Third,
when the child changes its copy of that environment variable, the
parent's value remains parent-environment. Child
processes cannot normally rewrite the already-running parent's
process environment.
The child also normally begins in the parent's current working directory. That is inherited process context, not shared live session state. If the child changes its location, the parent's location does not change.
Clean up the temporary environment variable from the current process after the experiment:
Remove-Item Env:PS_ACADEMY_SESSION_TEST
Remove-Variable SessionMarker, parentLocation
9. Apply the model to a CI runner
Imagine a pipeline step launches
pwsh -NoProfile -File ./deploy.ps1. The runner process
prepares a workspace and environment variables, then starts a
PowerShell child process. PowerShell creates session state, parses
the script, resolves commands, binds parameters, executes commands,
emits data and diagnostics, and eventually exits with a process
status.
If the next CI step launches a new pwsh process,
ordinary variables from the first step are gone. To transfer state,
the pipeline must use a documented mechanism: an artifact, a file in
a preserved workspace, a CI output variable, a cache, a database, an
external service, or another explicit channel. Session state is not
a durable database.
This model is the foundation for later topics such as jobs, remoting, runspaces, containers, and CI orchestration. Each technology introduces a boundary; reliable automation makes those boundaries visible.
10. Common execution-model mistakes
Confusing $Host with the operating system.
The host represents the interface through which PowerShell is
running. Use runtime/platform information for the OS.
Expecting a PowerShell variable to exist in a new process. Ordinary variables are session state. Pass required inputs explicitly.
Assuming environment variables are globally shared live state. A child generally inherits a snapshot. Later changes in the child do not update its parent.
Assuming CI starts in your preferred directory. Use
explicit paths or inspect Get-Location; do not depend
on an interactive workstation's current location.
Assuming native tools participate in PowerShell's object model automatically. Native execution crosses a process boundary with its own argument, text-stream, and exit-code conventions.
11. Hands-on lab: map your current execution environment
Run the following read-only report in an interactive session, then run the same report through a clean child process. Compare the results instead of expecting them to match exactly.
function Show-ExecutionContext {
[pscustomobject]@{
PID = $PID
Host = $Host.Name
PowerShell = $PSVersionTable.PSVersion.ToString()
Edition = $PSVersionTable.PSEdition
WorkingDirectory = (Get-Location).Path
ProcessPath = (Get-Process -Id $PID).Path
Runtime = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
}
}
"Current session:"
Show-ExecutionContext | Format-List
"Clean child process:"
pwsh -NoProfile -Command @'
[pscustomobject]@{
PID = $PID
Host = $Host.Name
PowerShell = $PSVersionTable.PSVersion.ToString()
Edition = $PSVersionTable.PSEdition
WorkingDirectory = (Get-Location).Path
ProcessPath = (Get-Process -Id $PID).Path
Runtime = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
} | Format-List
'@
The PIDs must differ. The engine version and executable may be the
same if pwsh resolves to the same installation. The
working directory is normally inherited. Profile-dependent state is
intentionally absent in the child because
-NoProfile was used.
Verification checklist
12. Knowledge check
Question 1. What is a runspace at a beginner-friendly level?
Question 2. Does a new pwsh process
automatically inherit ordinary PowerShell variables from its
parent?
Question 3. Why can a child process see an environment variable set by its parent?
Question 4. What happens before a resolved command actually performs its operation?
Question 5. Why can CI behavior differ from a personal terminal even when both run PowerShell 7?
13. Summary
A PowerShell session sits inside a layered execution system. The terminal presents interaction; a host connects the engine to an environment; an operating-system process owns lifetime and inherited environment; a runspace holds PowerShell session state; the engine parses, resolves, binds, executes, and emits objects or diagnostic streams; native commands create child-process boundaries. This model explains why state persists in one session but disappears—or must be explicitly transferred—across new processes and CI steps.
14. Further reading
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.