You double-click or type the name of a PowerShell script and, instead of running, it fails in red:
File C:\Scripts\Get-Report.ps1 cannot be loaded because running scripts is disabled on this system.
For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
It can show up in surprising places: a Python virtual environment’s activate.ps1, an npm or other tool’s .ps1 shim, or your own PowerShell profile that fails to load every time you open a window. The cause is the same each time: the execution policy.
Applies to: Windows 10, Windows 11 and Windows Server, in Windows PowerShell 5.1 (powershell.exe) and PowerShell 7 (pwsh.exe). Execution policy is enforced on Windows only. On Linux and macOS PowerShell it reports Unrestricted and does nothing.
What is going on
PowerShell has a setting called the execution policy that decides whether it loads scripts and profiles at all, and whether they have to be digitally signed. Microsoft describes it as a safety feature that helps stop you running a script by accident. The defaults, from Microsoft’s documentation:
- Windows 10 and 11 (clients):
Restrictedin Windows PowerShell 5.1. You can type commands, but no.ps1script, module script or profile will run. This is the one that produces the error above. - Windows Server:
RemoteSigned. Local scripts run. Scripts downloaded from the internet must be signed or unblocked. - PowerShell 7 (
pwsh): Microsoft’s PowerShell 7 documentation lists the default asRemoteSignedfor Windows clients and servers. Windows PowerShell and PowerShell 7 keep separate settings, so changing one does not change the other. That is why a script can fail in one and work in the other.
The policy is not set in one place. It is evaluated at several scopes, and the first scope that has a value wins. In order of strength:
MachinePolicy: set by Group Policy for every user of the computer. Overrides everything below.UserPolicy: set by Group Policy for the current user.Process: this PowerShell session only, lost when it closes (stored in$Env:PSExecutionPolicyPreference). This is what-ExecutionPolicyon the command line sets.CurrentUser: you only, saved for next time.LocalMachine: everyone on the computer, the default scope ofSet-ExecutionPolicy. Needs an elevated prompt.
If none of them is set, the effective policy falls back to the default above: Restricted on a Windows client.
The execution policy is not a security boundary. Microsoft says so in the documentation: someone who cannot run a script can paste its contents into the console. It protects you from clicking something by mistake, not from a determined user or from malware. That is the reason to choose the narrowest setting that does the job and not to treat any setting as a lock.
Diagnosis
1. Which shell are you in, and what does it think the policy is?
$PSVersionTable.PSVersion
Get-ExecutionPolicy
Get-ExecutionPolicy -List
A Major version of 5 is Windows PowerShell (powershell.exe). 7 or higher is PowerShell 7 (pwsh.exe). Get-ExecutionPolicy alone gives the effective policy. The list shows every scope. Microsoft’s example looks like this:
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser RemoteSigned
LocalMachine AllSigned
Read it top to bottom. The first row that is not Undefined is the effective policy (here RemoteSigned, from CurrentUser, which beats LocalMachine). Two results matter most:
- Everything
Undefined: nothing is set, so you are on the default,Restrictedon a Windows client. Local fix. MachinePolicyorUserPolicyis notUndefined: Group Policy (“Turn on Script Execution”) owns the setting. Your changes at the other scopes will not take effect.
2. Is it “disabled” or “not digitally signed”?
The two messages mean different things:
- “running scripts is disabled on this system”: the policy is
Restricted, so no script runs, signed or not. - “is not digitally signed. The script will not execute on the system”: the policy lets scripts run (
RemoteSignedorAllSigned) but this file does not qualify. WithRemoteSignedthat almost always means the file came from the internet and is marked as such. See the Unblock-File step.
To see whether a file is marked as downloaded (Windows adds a Zone.Identifier alternate data stream, value 3 for the internet):
Get-Item .\script.ps1 -Stream Zone.Identifier -ErrorAction SilentlyContinue
Get-Content .\script.ps1 -Stream Zone.Identifier
No output means the file is not marked.
Root cause
The effective execution policy is Restricted (the Windows client default, or set by Group Policy), so PowerShell will not load the script. Nothing is wrong with the script or with Windows. Fix it at the smallest scope that works: one run, then your own account, then the machine, and never past a Group Policy that is deliberately set.
The fix
Option 1: run this one script once (no setting changes)
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\script.ps1
The -ExecutionPolicy parameter sets the Process scope for that new session and its children. Nothing is saved. For PowerShell 7 use pwsh.exe in place of powershell.exe. It is also the right pattern for scheduled tasks, deployment tools and batch files. Microsoft notes that it does not override a Group Policy.
To change only the PowerShell window you are already in:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
Option 2: allow local scripts for your account (recommended)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
No elevation is needed. Answer Y to the prompt. RemoteSigned lets scripts you wrote or copied locally run, and still requires a signature (or unblocking) for files that came from the internet.
Option 3: for every user on the machine
Open PowerShell with Run as administrator, then:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
LocalMachine is the default scope, so this is the same as leaving -Scope off. Use this on a server or a shared admin machine. For your own PC, option 2 touches less.
If the script is “not digitally signed”: unblock it
Read the script first. Unblock-File removes the internet mark, and from then on PowerShell treats the file as local.
Unblock-File -Path .\script.ps1
# a whole folder you have already reviewed; -WhatIf first shows what it would touch
Get-ChildItem .\Scripts -Filter *.ps1 | Unblock-File -WhatIf
It does not change the execution policy. Microsoft’s page for the cmdlet says it does the same thing as ticking Unblock in the file’s Properties dialog in File Explorer. Files fetched with curl.exe, Invoke-WebRequest or Invoke-RestMethod may not be marked at all.
If Group Policy is setting it
When MachinePolicy or UserPolicy has a value, Set-ExecutionPolicy may print something like “PowerShell updated your local preference successfully, but the setting is overridden by the Group Policy applied to your system” and the effective policy stays as it was. That is Group Policy doing its job. The change belongs in the GPO (“Turn on Script Execution” under Administrative Templates, Windows Components, Windows PowerShell), made by whoever manages it. A script that must run on managed machines should be signed and run under AllSigned or RemoteSigned, not bypassed.
Verification
Get-ExecutionPolicy -List
.\script.ps1
After option 2 the CurrentUser row should read RemoteSigned and the effective policy should match, unless a stronger scope above it overrides. Then the script should simply run. If a command appears to succeed but nothing changes, look at the list again: a more powerful scope (Process, or either Group Policy row) is winning.
Notes
- Do not use
Unrestrictedor edit the registry.RemoteSignedcovers local and unblocked scripts, andSet-ExecutionPolicyis the supported way to change it. In Windows PowerShell the values live underHKLMandHKCU\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell. In PowerShell 7 they are in apowershell.config.jsonfile instead. Editing them by hand gains nothing. - Bypass versus Unrestricted.
Bypassblocks nothing and warns about nothing.Unrestrictedruns everything but warns before running scripts that came from outside the local intranet zone. Neither belongs as a permanent machine setting. - Scripts on a network share. Microsoft notes that where UNC paths are not told apart from internet paths, a script on
\\server\sharemay be refused underRemoteSigned. Copy it locally or sign it. - Logon scripts and Server Core. Checking a file’s zone needs the Windows shell. On Server Core, Nano Server, or a logon script that starts before the desktop is ready, the check can fail with
AuthorizationManager check failed. Microsoft saysBypassorAllSignedavoids the zone check. - Different shell, different setting. If a script worked in Windows PowerShell and fails in
pwsh(or the other way round), compareGet-ExecutionPolicy -Listin both.
Group Policy fighting you, or a signing question? Post the output of Get-ExecutionPolicy -List in The Patch Panel and we will work through it. The best answer earns points on Top of the Stack.