A PowerShell execution policy is a safety control that determines the conditions under which PowerShell loads configuration profiles and executes script files. Execution policies are designed to prevent administrators from unintentionally running untrusted scripts; they are administrative guardrails rather than strict security boundaries, as users can bypass them through command-line parameters or process-level switches.
Execution Policy Levels
PowerShell defines six distinct execution policy levels that control how local and remote scripts are handled:
| Policy Level | Scripts Allowed | Signed Requirement | Downloaded File Handling |
|---|---|---|---|
Restricted |
No scripts | N/A | Blocks all .ps1 files; allows interactive commands only (Default on Windows client editions). |
AllSigned |
Yes | Yes (All scripts) | Every script (local or remote) must be digitally signed by a trusted publisher. |
RemoteSigned |
Yes | Remote only | Local scripts run freely; scripts downloaded from the internet must be signed (Default on Windows Server). |
Unrestricted |
Yes | No | Runs all scripts; displays an interactive warning prompt before running downloaded files. |
Bypass |
Yes | No | Nothing is blocked; no warning prompts or signatures required. |
Undefined |
Inherited | Inherited | No explicit policy set at the current scope; falls back to lower-precedence scopes or defaults. |
$effectivePolicy = Get-ExecutionPolicy
Write-Output "Effective Execution Policy: $effectivePolicy"Effective Execution Policy: RemoteSignedHow PowerShell Determines "Downloaded"
A file is considered "downloaded from the internet" based on its Zone.Identifier alternate data stream (also known as the Mark of the Web, or MotW), which Windows attaches automatically to files saved by a browser or email client.
Get-Item -Path "C:\Scripts\tool.ps1" -Stream Zone.IdentifierFileName: C:\Scripts\tool.ps1
Stream Length
------ ------
Zone.Identifier 26Under a RemoteSigned execution policy, PowerShell checks for this stream. If present and the file lacks a valid Authenticode digital signature, PowerShell halts execution with an unauthorized access error.
File C:\Scripts\Deploy.ps1 cannot be loaded. The file C:\Scripts\Deploy.ps1 is not
digitally signed. You cannot run this script on the current system.Scopes and Evaluation Precedence
PowerShell determines the effective execution policy by evaluating five hierarchical scopes. The policy configured at the highest-precedence scope overrides all scopes below it.
Get-ExecutionPolicy -List Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Bypass
CurrentUser RemoteSigned
LocalMachine RestrictedPrecedence Hierarchy (Highest to Lowest)
MachinePolicy: Set via Active Directory Group Policy (Computer Configuration). Overrides all other settings; stored in registry underHKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell.UserPolicy: Set via Active Directory Group Policy (User Configuration). Overrides local machine configurations; stored inHKCU:\SOFTWARE\Policies\Microsoft\Windows\PowerShell.Process: Scoped only to the current PowerShell process instance ($env:PSExecutionPolicyPreference). Reverts to default once the session closes.CurrentUser: Applied to the logged-in user profile; stored inHKCU:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell. Requires no administrative rights to change.LocalMachine: Applied globally to all users on the computer; stored inHKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell. Requires an elevated administrator session to modify.
Setting Execution Policy with Set-ExecutionPolicy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUserwhere:
-ExecutionPolicyis the policy level to apply.-Scopeis the scope to apply it at (defaults toLocalMachineif omitted, which requires admin rights).
Setting Policy for a Single Session Only
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope ProcessComplete User Scope Example
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
Get-ExecutionPolicy -Scope CurrentUserRemoteSignedBypassing Execution Policy
Rather than changing the persistent policy, you can override it for a single script invocation or session.
Command-Line Invocation Bypass
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\deploy.ps1"where:
-NoProfileskips loading PowerShell profile files, speeding up startup and avoiding interference from custom user settings.-ExecutionPolicy Bypassoverrides the system execution policy strictly for the lifetime of the spawned process, without modifying any stored setting.-Filespecifies the target script to execute.
In-Session Process Elevation
$previousPolicy = Get-ExecutionPolicy -Scope Process
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
Write-Output "Process execution policy set to: $(Get-ExecutionPolicy)"
& ".\Deploy.ps1"
Set-ExecutionPolicy -ExecutionPolicy $previousPolicy -Scope Process -Force
Write-Output "Process execution policy reverted to: $(Get-ExecutionPolicy)"Process execution policy set to: Bypass
Deployment complete.
Process execution policy reverted to: RemoteSignedUnblocking Downloaded Scripts with Unblock-File
Rather than changing the execution policy, a more targeted fix for a specific downloaded script is to remove its "downloaded from the internet" flag using Unblock-File. This removes the Zone.Identifier stream, marking the script as a trusted local file.
Get-Item -Path ".\Deploy.ps1" -Stream "Zone.Identifier" -ErrorAction SilentlyContinue
Unblock-File -Path ".\Deploy.ps1" -Verbose
Get-Item -Path ".\Deploy.ps1" -Stream "Zone.Identifier" -ErrorAction SilentlyContinueFileName Stream Length
-------- ------ ------
C:\Scripts\Deploy.ps1 Zone.Identifier 68
VERBOSE: Performing the operation "Unblock-File" on target "C:\Scripts\Deploy.ps1".Unblocking Multiple Files at Once
Get-ChildItem -Path "C:\Scripts" -Filter "*.ps1" -Recurse | Unblock-FilePractical Usage
Preparing a Downloaded Script Toolkit for Use
A common onboarding task is safely unblocking a set of downloaded automation scripts before distributing them to a team, verifying policy settings along the way.
param([string]$ToolkitPath)
Write-Output "Current execution policy (effective): $(Get-ExecutionPolicy)"
$blockedFiles = Get-ChildItem -Path $ToolkitPath -Filter "*.ps1" -Recurse |
Where-Object { Get-Item -Path $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue }
if ($blockedFiles) {
Write-Output "Unblocking $($blockedFiles.Count) script(s)..."
$blockedFiles | Unblock-File
} else {
Write-Output "No blocked scripts found."
}
Write-Output "Toolkit ready for use."Current execution policy (effective): RemoteSigned
Unblocking 4 script(s)...
Toolkit ready for use.