Trying to run a PowerShell script and getting "cannot be loaded because running scripts is disabled on this system"? Here is the exact error, why it happens, and the narrowest fix that actually solves it.
The Error
.\deploy.ps1 : File C:\Users\sean\deploy.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.
At line:1 char:1
+ .\deploy.ps1
+ ~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
PowerShell 7 prints the same sentence in a more compact form, without the CategoryInfo block. The FullyQualifiedErrorId : UnauthorizedAccess line is the giveaway that this is an execution policy refusal and not a file permissions problem.
Quick Fix
Open PowerShell and run:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Answer Y at the confirmation prompt. This does not need an administrator prompt. Now run your script again.
Why This Happens
PowerShell's execution policy decides whether script files are allowed to run at all. On Windows client editions - Windows 10 and Windows 11 - the default is Restricted, which permits interactive commands but blocks every .ps1 file. Windows Server editions default to RemoteSigned instead, which is why the same script runs fine on a server and fails on your laptop.
Nothing about your script triggers this. PowerShell refuses before it parses the file, so a one-line script and a thousand-line script fail identically.
The policy is evaluated across five scopes, in this order of precedence:
- MachinePolicy - set by Group Policy, machine-wide
- UserPolicy - set by Group Policy, per user
- Process - this PowerShell process only, gone when it closes
- CurrentUser - your account, stored in your registry hive
- LocalMachine - all users, requires administrator
The first scope with a value other than Undefined wins. That is why Get-ExecutionPolicy -List is more useful than the bare Get-ExecutionPolicy: it shows you which scope is making the decision.
Fixes, Narrowest First
1. Run one script, change nothing
If you only need this script to run once, override the policy for a single process:
powershell -ExecutionPolicy Bypass -File .\deploy.ps1
Or in PowerShell 7:
pwsh -ExecutionPolicy Bypass -File .\deploy.ps1
The override lives and dies with that process. This is the right choice inside CI pipelines and scheduled tasks, where changing machine state would be a side effect nobody asked for.
2. Allow scripts for this session only
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Useful when you are about to run several scripts in a row while troubleshooting. Close the window and the setting is gone.
3. Allow scripts for your account (the usual answer)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
RemoteSigned is the recommended landing spot. Scripts you write locally run without ceremony; scripts that arrived from the internet still require a valid signature or an explicit unblock. You keep a meaningful check on the files most likely to be hostile and lose the friction on the files you wrote yourself.
4. Unblock a downloaded script
If you set RemoteSigned and a specific script still refuses to run, it carries the Mark of the Web - an alternate data stream Windows attaches to files that came from a browser, an email client, or an extracted archive.
Review the script's contents first, then:
Unblock-File -Path .\deploy.ps1
To unblock a whole folder you have already vetted:
Get-ChildItem -Path .\scripts -Filter *.ps1 -Recurse | Unblock-File
Do not make this a reflex. The mark exists precisely so that a script downloaded five minutes ago gets a second of your attention.
What Not to Do
# Don't do this
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy Bypass
Two problems. It requires elevation, so you are solving a per-user annoyance with a machine-wide change. And Bypass disables the checks entirely and permanently, for every account on the computer, including any future one.
Unrestricted is only marginally better: it runs everything but prompts on internet-sourced files, which trains people to click through the prompt.
It is worth being clear about what execution policy is and is not. Microsoft's own documentation states it is not a security boundary. Anyone determined to run blocked code can read the file and evaluate it directly:
Get-Content .\deploy.ps1 | Invoke-Expression
That is not a reason to disable the policy - it is a reason not to treat it as protection you can trade away casually. The policy's real value is preventing an accidental double-click from executing something, and the narrowest scope preserves that for everyone else on the machine.
Verify the Fix
Get-ExecutionPolicy -List
Expected output after fix 3:
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser RemoteSigned
LocalMachine Undefined
Then confirm a script actually runs:
Write-Output 'Set-Content test' > .\test.ps1
.\test.ps1
Remove-Item .\test.ps1
When Group Policy Is Overriding You
If Set-ExecutionPolicy appears to succeed but nothing changes, you will usually see this warning:
Windows PowerShell updated your execution policy successfully, but the setting is
overridden by a policy defined at a more specific scope.
Check the list:
Get-ExecutionPolicy -List
A value at MachinePolicy or UserPolicy means Group Policy is in charge and no local command will beat it. This is deliberate on managed and domain-joined machines. The path forward is a request to whoever administers the policy - located at Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell > Turn on Script Execution - not a workaround. Note that the Process-scope override in fix 1 also loses to Group Policy, so a -ExecutionPolicy Bypass launch will not rescue you here either.
On Linux and macOS, PowerShell's execution policy is Unrestricted and cannot be changed, so this error is Windows-only.
Prevention
- Set
RemoteSignedatCurrentUserscope once on each machine you work on and forget about it. - In pipelines and scheduled tasks, pass
-ExecutionPolicy Bypasson the command line rather than changing machine state. - Keep scripts in a local repository directory rather than running them out of
Downloads, which sidesteps the Mark of the Web entirely. - For scripts distributed across a team, sign them with a code-signing certificate.
RemoteSignedthen accepts them everywhere with no per-machine change at all.
Related Errors
If the failing command is npm, yarn, pnpm or tsc, you may see the same message naming a shim such as npm.ps1. That is this error, and the CurrentUser + RemoteSigned fix resolves it. If instead PowerShell says The term 'npm' is not recognized as the name of a cmdlet, that is a different problem - a PATH issue rather than an execution policy one.
Summary
- Most cases:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned - One-off or automation:
powershell -ExecutionPolicy Bypass -File .\script.ps1 - Still blocked:
Unblock-File -Path .\script.ps1after reviewing the file - Verify:
Get-ExecutionPolicy -List