PowerShell execution policy and what it is not
The single most useful sentence about ExecutionPolicy is the one Microsoft puts in its own documentation: it is not a security boundary. It is a seatbelt against double-clicking a script you did not mean to run, nothing more and treating it as a control that keeps an attacker out is a category error that hides the real levers. So the honest way to cover it is to say what it does, prove how little that is and then point at the things that actually constrain what PowerShell can do.
The levels, briefly
| Policy | Effect |
|---|---|
| Restricted | no scripts (client default) |
| AllSigned | every script must be signed |
| RemoteSigned | local scripts run, downloaded ones must be signed |
| Unrestricted | everything runs, with a prompt for downloaded files |
| Bypass | everything runs, no prompt |
| Undefined | inherit from the next scope |
The effective value is the most restrictive across the scopes, which from highest precedence down are the two Group Policy scopes, then Process, CurrentUser and LocalMachine. For a workstation estate, RemoteSigned via Group Policy is the sane default: local automation runs and anything carrying a Mark-of-the-Web from a download or a network share has to be signed first.
Get-ExecutionPolicy -List # see every scope at once
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine
RemoteSigned leans entirely on the Zone.Identifier alternate data stream that the browser or SMB client stamps on a downloaded file. That is the whole mechanism and Unblock-File strips it in one call, which is the first clue about how much weight it can bear.
Why it is not a boundary
Every one of these runs a script regardless of the policy, with no special privilege:
powershell.exe -ExecutionPolicy Bypass -File script.ps1
powershell.exe -Command "& { Get-Content script.ps1 | Invoke-Expression }"
Get-Content script.ps1 | Invoke-Expression
The policy governs how powershell.exe loads a .ps1 from disk and nothing more. It does not govern the interpreter: pipe the text into Invoke-Expression, pass it on the command line, or set -ExecutionPolicy Bypass at launch and the restriction was never in the path. An attacker who can run a PowerShell command can run any PowerShell command, so the policy stops an accident and an unaware user, never an adversary. Build your threat model on that and you will not be surprised.
What actually constrains PowerShell
Three things do real work and none of them is the execution policy.
Constrained Language Mode is the one that bites. It restricts the language itself (no arbitrary .NET types, no Add-Type, no COM) so that even code that runs cannot reach the dangerous surface and WDAC or AppLocker in enforcement mode drives PowerShell into CLM automatically for anything not on the allow-list. That pairing, application control deciding what may run and CLM clipping what the allowed code may do, is the actual boundary the execution policy only pretends to be.
Logging is the other half, because a boundary you cannot see through is worth little. Script-block logging records what was executed, including code that was decoded or generated at runtime, which is exactly what defeats the obfuscation an attacker reaches for. Transcription captures the session. Both are Group-Policy settings, or the registry keys behind them:
$base = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell"
Set-ItemProperty "$base\ScriptBlockLogging" EnableScriptBlockLogging 1
Set-ItemProperty "$base\Transcription" EnableTranscripting 1
Code signing is the third and it is what makes AllSigned meaningful rather than theatrical: a real PKI with a code-signing certificate, scripts signed with Set-AuthenticodeSignature and the private key kept somewhere that is not the build agent. A self-signed cert is fine to learn the mechanics and worthless as a trust anchor.
Where to land
For a managed estate: RemoteSigned by Group Policy so the accident case is covered, WDAC or AppLocker in enforcement so application control and Constrained Language Mode do the actual restricting and script-block logging plus transcription flowing to wherever you keep logs. The execution policy is the seatbelt in that car. It is worth wearing and no one should mistake it for the brakes.