Execution Policy, Script Signing, and What Execution Policy Does Not Do
Understand PowerShell execution policy and Authenticode signing as trust signals and guardrails, not complete security boundaries, with explicit Windows and cross-platform behavior.
Learning objectives
- Explain execution policy as a script-loading safety feature rather than authorization.
- Inspect effective execution-policy scopes without changing host configuration.
- Describe RemoteSigned, AllSigned, Restricted, Bypass, and internet-origin metadata.
- Explain Authenticode integrity, publisher trust, certificates, and signature validation.
- Distinguish Windows execution-policy/signing behavior from cross-platform security controls.
- Choose appropriate trust controls for developer, CI, and managed-host scenarios.
1. Security starts with trust boundaries, not switches
PowerShell can read files, start processes, call APIs, administer systems, and load reusable code. That power means security begins with a question: which code and inputs are trusted to cross which boundary? Execution policy and code signing help answer part of that question, but neither can replace operating-system permissions, application control, least privilege, source review, or secret handling.
flowchart TD
A["Source code"] --> B{"Trust decision"}
B -->|"Policy / signature / provenance"| C["PowerShell host"]
C --> D["OS permissions"]
D --> E["Files, APIs, services, network"]
2. Inspect execution policy before changing anything
On Windows, PowerShell evaluates execution policy by scope. Group Policy can set machine or user policy; a process-scoped policy lasts only for the current PowerShell process; CurrentUser and LocalMachine are persistent local settings. The effective result is what matters.
# Safe, read-only inspection.
Get-ExecutionPolicy
Get-ExecutionPolicy -List
[pscustomobject]@{
Platform = if ($IsWindows) { 'Windows' } elseif ($IsLinux) { 'Linux' } else { 'macOS/Other' }
EffectivePolicy = Get-ExecutionPolicy
Edition = $PSVersionTable.PSEdition
Version = $PSVersionTable.PSVersion.ToString()
}
| Scope | Meaning |
|---|---|
| MachinePolicy | Computer-wide policy delivered through Group Policy; highest precedence. |
| UserPolicy | User policy delivered through Group Policy. |
| Process | Current PowerShell process only; disappears when the process exits. |
| LocalMachine | Persistent local setting affecting all users on Windows. |
| CurrentUser | Persistent local setting for the current user on Windows. |
3. Execution-policy enforcement is a Windows feature
Current PowerShell 7.6 documentation is explicit: execution-policy
enforcement occurs on Windows. On Linux and macOS,
Get-ExecutionPolicy reports Unrestricted,
but the behavior effectively resembles Bypass because
Windows security zones are not implemented there.
Set-ExecutionPolicy is present but reports that the
operation is unsupported.
if ($IsWindows) {
Get-ExecutionPolicy -List
} else {
[pscustomobject]@{
ReportedPolicy = Get-ExecutionPolicy
Enforcement = 'Windows execution-policy enforcement is not implemented here'
}
}
Do not interpret that as “non-Windows PowerShell has no security.” It means the trust controls must come from file ownership/permissions, package provenance, signatures from platform-native tooling where appropriate, application/container policy, CI controls, and least-privileged identities.
4. Restricted, RemoteSigned, AllSigned, and Bypass solve different problems
A policy name is not a security rating. Each policy changes the script-loading rules:
| Policy | Operational meaning | Important limit |
|---|---|---|
| Restricted | Interactive commands are allowed; script files are blocked. | A user who can type commands still has PowerShell capabilities. |
| RemoteSigned | Downloaded scripts need a trusted signature unless their internet-zone mark is removed after review; local scripts need not be signed. | Provenance marking can vary by download mechanism. |
| AllSigned | All scripts/configuration files must be signed by a trusted publisher. | A valid signature does not prove the code is benign. |
| Bypass | No policy warnings or blocking. | This removes a guardrail; it does not create a safer environment. |
# Do not run this as a course "fix":
# Set-ExecutionPolicy Bypass -Scope LocalMachine
# If a narrowly scoped test truly requires a process-only policy,
# document why and let it disappear with the process.
Get-ExecutionPolicy -Scope Process
5. RemoteSigned depends on provenance information
On Windows, downloaded files can carry a
Mark of the Web in the
Zone.Identifier alternate data stream. Under
RemoteSigned, an unsigned script marked as
internet-origin may be blocked. Unblock-File removes
that mark after you have reviewed the file and decided to trust it;
it does not change the system execution policy.
# Windows-only inspection of the Zone.Identifier alternate data stream.
if ($IsWindows) {
Get-Item -LiteralPath '.\candidate.ps1' -Stream Zone.Identifier -ErrorAction SilentlyContinue
}
# Review source/provenance first. Then, if justified:
# Unblock-File -LiteralPath '.\candidate.ps1' -WhatIf
Current documentation also notes that not every download path necessarily writes the same zone metadata. Therefore, “not blocked by RemoteSigned” is not proof that a file is trustworthy.
6. Authenticode binds file content to a signing identity
A digital signature answers two useful questions: “has the signed content changed since signing?” and “which certificate/private key produced this signature?” Authenticode uses a code-signing certificate whose private key signs the file and whose certificate chain can be evaluated against trusted roots/publishers.
A signature does not answer “is this code safe?” A malicious publisher can sign malicious code; a compromised private key can sign unwanted code; and an expired/revoked/untrusted chain affects validation. Signature verification is one input to a broader trust decision.
# Current PowerShell 7.6 Authenticode cmdlets are Windows-only.
if ($IsWindows -and (Get-Command Get-AuthenticodeSignature -ErrorAction SilentlyContinue)) {
Get-AuthenticodeSignature -LiteralPath '.\candidate.ps1' |
Select-Object Status,StatusMessage,SignerCertificate,TimeStamperCertificate,Path
}
7. Optional signing workflow on a disposable file
Signing requires access to a code-signing certificate and its private key. Do not create or install a production signing certificate merely for this lab. If your Windows test machine already has a non-production code-signing certificate, you can sign a temporary script and then inspect the result.
if ($IsWindows) {
$lab = Join-Path ([IO.Path]::GetTempPath()) 'ps-academy-signing-lab.ps1'
'Write-Output "training only"' | Set-Content -LiteralPath $lab -Encoding utf8
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($cert) {
Set-AuthenticodeSignature -FilePath $lab -Certificate $cert |
Select-Object Status,Path
Get-AuthenticodeSignature -LiteralPath $lab |
Select-Object Status,SignerCertificate
} else {
Write-Warning 'No existing non-production code-signing certificate was found; inspection-only path used.'
}
Remove-Item -LiteralPath $lab -Force -ErrorAction SilentlyContinue
}
8. Choose controls according to the host role
A developer workstation, an ephemeral CI runner, and a managed enterprise server have different threat models. The policy should reflect how code arrives, who can modify it, how identities are controlled, and whether application control already enforces a stricter trust policy.
| Environment | Reasonable questions |
|---|---|
| Developer workstation | Who can modify local scripts? Are downloaded scripts reviewed? Is signing required for shared release artifacts? |
| CI runner | Is the runner ephemeral? Are workflow definitions protected? Are dependencies pinned? Does the runner need interactive prompts at all? |
| Managed enterprise host | Can Group Policy/application control enforce approved code? Are publishers managed? Are admins permanent or delegated through JEA? |
9. Lab — build a read-only trust report
This lab changes no execution policy and installs no certificates. It records evidence that an operator can attach to a troubleshooting or compliance ticket.
$report = [ordered]@{
CheckedUtc = [datetime]::UtcNow
Platform = $PSVersionTable.Platform
PSEdition = $PSVersionTable.PSEdition
PSVersion = $PSVersionTable.PSVersion.ToString()
EffectivePolicy = Get-ExecutionPolicy
PolicyByScope = @(Get-ExecutionPolicy -List | ForEach-Object {
[pscustomobject]@{ Scope = $_.Scope; Policy = $_.ExecutionPolicy.ToString() }
})
AuthenticodeCmd = [bool](Get-Command Get-AuthenticodeSignature -ErrorAction SilentlyContinue)
}
[pscustomobject]$report | ConvertTo-Json -Depth 5
Expected observation: Windows can report meaningful configured execution-policy scopes; non-Windows reports the compatibility value but not Windows-style enforcement. The report deliberately distinguishes availability from security guarantees.
10. Common mistakes and why they are dangerous
| Mistake | Why it is wrong | Safer approach |
|---|---|---|
| Set Bypass permanently because a script failed | It removes a guardrail without diagnosing the actual trust/provenance problem. | Inspect policy, provenance, signature, and source first. |
| Assume AllSigned means all signed code is safe | Signatures prove integrity/publisher identity under a trust chain, not intent. | Combine signing with review, protected keys, allowlists, and least privilege. |
| Treat execution policy as authorization | Users can still invoke permitted commands and other executables. | Use OS permissions, application control, JEA, and identity controls. |
| Unblock an entire downloads folder blindly | It discards provenance warnings before review. | Review individual artifacts, source, hash/signature, then unblock only justified files. |
11. Why this matters in DevOps
Build and deployment systems routinely execute code retrieved from repositories, artifacts, package feeds, and generated workspaces. The security objective is not “make PowerShell stop warning.” It is to create a chain of evidence: protected source, reviewed changes, controlled dependencies, authenticated publishers where applicable, constrained identities, and auditable execution.
Signing is especially valuable when organizations need publisher identity and tamper evidence between build and execution. Execution policy can support that workflow on Windows, but it is only one layer.
12. Verification checklist
- You can explain why execution policy is a safety feature rather than a security boundary.
- You know enforcement is Windows-specific in PowerShell 7.6.
- You can inspect policy scopes without changing them.
- You understand RemoteSigned, internet-zone metadata, and the purpose of Unblock-File.
- You can describe what Authenticode signatures prove and what they do not prove.
- You can choose different trust controls for workstations, CI runners, and managed hosts.
13. Knowledge check
Question 1. Is execution policy an authorization or sandbox mechanism?
Question 2. Where is execution-policy enforcement implemented?
Question 3. What does RemoteSigned add for internet-origin scripts?
Question 4. Does a Valid Authenticode status prove that the script is harmless?
Question 5. Why should a course avoid permanently setting Bypass?
14. Summary and next bridge
Execution policy controls script-loading conditions on Windows, while Authenticode provides integrity and publisher evidence. Neither substitutes for authorization, application control, source review, protected signing keys, or least privilege. The next lesson moves from “can we trust this code?” to “how do we keep credentials and secrets from becoming ordinary strings scattered through scripts, logs, and process environments?”
15. Authoritative references
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.