Fix: PowerShell Script Cannot Be Loaded Because Running Scripts Is Disabled
Part of: Docker, DevOps & Infrastructure
Quick Answer
How to fix 'cannot be loaded because running scripts is disabled on this system' in PowerShell by changing the execution policy with Set-ExecutionPolicy, fixing Group Policy restrictions, and bypassing for single scripts.
The Error
You try to run a PowerShell script and get:
.\script.ps1 : File C:\Users\you\script.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 : UnauthorizedAccessOr one of these variations:
File C:\path\to\script.ps1 cannot be loaded. The file C:\path\to\script.ps1 is not digitally signed.
The script will not run on the system.
.\deploy.ps1 : File C:\deploy.ps1 cannot be loaded because running scripts is disabled on this system.
Install-Module : Scripts are not allowed to run on this system.This also blocks commands like npm, yarn, ng, vue, or tsc if they rely on .ps1 wrapper scripts in your PATH.
Why This Happens
PowerShell has a built-in security feature called the execution policy. It controls whether PowerShell scripts (.ps1 files) are allowed to run on the system. The execution policy is not a security boundary — Microsoft’s own documentation says it’s designed to prevent users from accidentally running scripts, not to lock down the system against attackers.
There are five execution policy levels:
| Policy | What It Allows |
|---|---|
| Restricted | No scripts can run. This is the default on Windows client machines (Windows 10/11). |
| AllSigned | Only scripts signed by a trusted publisher can run. |
| RemoteSigned | Scripts downloaded from the internet must be signed. Local scripts can run without a signature. This is the default on Windows Server. |
| Unrestricted | All scripts can run, but downloaded scripts prompt for confirmation. |
| Bypass | Nothing is blocked. No warnings or prompts. |
On a fresh Windows 10 or 11 install, the execution policy is set to Restricted. That means every .ps1 script is blocked, including scripts you wrote yourself.
The execution policy can be set at multiple scopes, and they override each other in a specific order:
- MachinePolicy — Set by Group Policy for the computer. Highest priority.
- UserPolicy — Set by Group Policy for the user.
- Process — Applies only to the current PowerShell session.
- CurrentUser — Applies to the current user account. Stored in the registry.
- LocalMachine — Applies to all users on the computer. Stored in the registry.
PowerShell evaluates these scopes from top to bottom and uses the first one that is not Undefined. If a Group Policy scope is set, it overrides everything below it — and you cannot change it with Set-ExecutionPolicy.
Check your current execution policy and all scopes:
Get-ExecutionPolicy
Get-ExecutionPolicy -ListThe output of Get-ExecutionPolicy -List shows all five scopes and their current values. This tells you exactly where the restriction is coming from.
Diagnostic Timeline
Most blog posts tell you to run Set-ExecutionPolicy Unrestricted and move on. That is the wrong answer. Here is the order an experienced Windows engineer actually works through this error.
Minute 0 — The dangerous reflex. Your first instinct is Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force. Do not do this. Unrestricted is flagged by every enterprise security scanner (CIS, STIG, ASR), defeats the purpose of the policy, and breaks the moment you join a domain that pushes a stricter Group Policy. The correct target is almost always RemoteSigned.
Minute 1 — Run Get-ExecutionPolicy -List, not Get-ExecutionPolicy. The plain form returns the effective policy, which hides which scope is causing the block. The -List form returns all five scopes. If MachinePolicy or UserPolicy is set to anything other than Undefined, the policy is coming from Group Policy and Set-ExecutionPolicy will refuse to change it. Stop trying — go straight to Fix 5.
Minute 2 — Identify the script class. Open the file in PowerShell ISE and run Get-AuthenticodeSignature .\script.ps1. There are three outcomes:
NotSigned— local script you wrote.RemoteSignedforCurrentUseris correct.Valid— signed by a publisher already in your trusted store. The block is not coming from the policy; check Fix 7 (PowerShell version mismatch) or the Zone.Identifier alternate data stream.HashMismatchorNotTrusted— the script is signed but the signature is invalid, expired, or the publisher is not trusted. Re-signing or importing the publisher cert is the fix, not a policy change.
Minute 4 — Check the Zone.Identifier. Even with RemoteSigned, an unsigned script downloaded from the internet is blocked. Get-Item .\script.ps1 -Stream Zone.Identifier shows the mark-of-the-web. If it is there, Unblock-File is the entire fix — not a policy change. People skip this step and end up with a more permissive policy than they needed.
Minute 6 — Discriminate user vs machine scope. If Get-ExecutionPolicy -Scope CurrentUser returns Undefined but scripts still fail, the policy is coming from LocalMachine (set by another admin), Group Policy, or MachinePolicy. Changing CurrentUser to RemoteSigned will work because CurrentUser overrides LocalMachine. But it will not override MachinePolicy.
Minute 8 — The wrong-but-tempting last resort: -ExecutionPolicy Bypass. This works for a single script invocation and is fine for CI. But if you put powershell -ExecutionPolicy Bypass -File script.ps1 into a scheduled task or a build pipeline because “nothing else worked,” you are masking a real problem. AppLocker or Windows Defender Application Control (WDAC) can still block the script — those are separate enforcement layers from the execution policy. If Bypass does not work either, you are not fighting the execution policy, you are fighting WDAC. See Fix 8 and the WDAC notes in Still Not Working.
The real root cause, ranked by frequency: (1) Zone.Identifier on a freshly downloaded npm-installed .ps1 wrapper, (2) CurrentUser policy never set on a fresh Windows install, (3) Group Policy enforcement on a domain-joined machine that no user-scope change can override, (4) PowerShell 7 vs Windows PowerShell 5.1 mismatch — the policy was set in one and the script runs in the other. Walk that list before reaching for Unrestricted.
Fix 1: Set the Execution Policy for Your User
The cleanest fix for most developers. This changes the policy for your user account only and persists across sessions:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedYou do not need administrator privileges for this command because CurrentUser scope writes to HKEY_CURRENT_USER in the registry.
RemoteSigned is the recommended policy for developers. It allows you to run any script you create locally while still requiring downloaded scripts to have a valid digital signature. This strikes a good balance between convenience and safety.
After running this command, try your script again:
.\script.ps1If you want to verify the change took effect:
Get-ExecutionPolicy -Scope CurrentUserPro Tip: Avoid setting the policy to
Unrestrictedunless you have a specific reason.RemoteSignedgives you the same flexibility for local scripts while still flagging potentially dangerous downloaded files. Many corporate security audits flagUnrestrictedpolicies as a finding.
Fix 2: Set the Execution Policy for All Users (Requires Admin)
If you need every user account on the machine to run scripts, set the policy at the LocalMachine scope. Open PowerShell as Administrator (right-click the PowerShell icon and select “Run as administrator”):
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSignedThis writes to HKEY_LOCAL_MACHINE in the registry and applies to all users. You must have admin rights because it affects the entire machine.
If you get a prompt asking for confirmation, type Y and press Enter. To skip the prompt in automated scenarios:
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -ForceNote: If a CurrentUser policy is already set, it takes precedence over LocalMachine for that specific user. Check both scopes with Get-ExecutionPolicy -List if your script still fails after this change.
Fix 3: Bypass the Policy for a Single Script
Sometimes you need to run one script without permanently changing any policy. Use the -ExecutionPolicy Bypass flag when launching PowerShell:
powershell -ExecutionPolicy Bypass -File .\script.ps1Or from within an already-open PowerShell session, set the policy for just the current process:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\script.ps1The Process scope only lasts for the current session. Once you close that PowerShell window, the policy reverts to whatever is set at higher scopes. This is useful for one-off tasks or when you do not want to change any persistent settings.
You can also use this approach in batch files, scheduled tasks, or CI/CD pipelines:
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\deploy.ps1"This is similar to how you might handle permission issues in bash where you use sudo or chmod for one-time execution rather than changing system-wide settings.
Fix 4: Unblock a Downloaded Script
When you download a .ps1 file from the internet (via a browser, email attachment, or Invoke-WebRequest), Windows attaches an Alternate Data Stream called Zone.Identifier to the file. This marks the file as coming from the internet. Even with RemoteSigned policy, PowerShell blocks these files unless they are digitally signed.
Check if a file is blocked:
Get-Item .\script.ps1 -Stream Zone.IdentifierIf the stream exists, unblock the file:
Unblock-File .\script.ps1To unblock all .ps1 files in a directory:
Get-ChildItem -Path C:\scripts -Filter *.ps1 | Unblock-FileYou can also unblock via the Windows GUI: right-click the file, select Properties, and check the Unblock checkbox at the bottom of the General tab.
Note: Unblock-File only removes the zone identifier. It does not change the execution policy itself. You still need at least RemoteSigned as your policy.
Fix 5: Fix Group Policy Restrictions
If Get-ExecutionPolicy -List shows a value under MachinePolicy or UserPolicy, the execution policy is being enforced by Group Policy. You cannot override it with Set-ExecutionPolicy:
Scope ExecutionPolicy
----- ---------------
MachinePolicy AllSigned
UserPolicy Undefined
Process Undefined
CurrentUser Undefined
LocalMachine UndefinedIn this scenario, running Set-ExecutionPolicy gives you:
Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is
overridden by a policy defined at a more specific scope.To fix this, you need to change or remove the Group Policy setting:
- Open the Group Policy Editor (
gpedit.msc) as an administrator. - Navigate to Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell.
- Double-click Turn on Script Execution.
- Set it to Not Configured (to remove the restriction) or Enabled and select your preferred policy.
- Run
gpupdate /forcein an elevated command prompt to apply the change immediately.
If your machine is domain-joined, the Group Policy may come from your organization’s domain controller. In that case, contact your IT administrator — you cannot change domain-level policies locally.
As a workaround when Group Policy blocks you but you still need to run a script, use the Process scope bypass from Fix 3. Group Policy applies to MachinePolicy and UserPolicy scopes, but some configurations still allow Process-level overrides. This is analogous to how environment variable issues can stem from system-level settings overriding user-level ones.
Fix 6: Sign Your PowerShell Scripts
If your organization requires AllSigned policy, you need to digitally sign your scripts. This is common in enterprise environments where security policies mandate code signing.
Create a self-signed certificate (for development and internal use):
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=Dev Script Signing" -CertStoreLocation Cert:\CurrentUser\MySign your script with the certificate:
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate $certVerify the signature:
Get-AuthenticodeSignature .\script.ps1The output should show Valid as the status.
For the AllSigned policy to accept a self-signed certificate, you must also add it to the Trusted Publishers and Trusted Root Certification Authorities stores:
Export-Certificate -Cert $cert -FilePath C:\temp\dev-signing.cer
Import-Certificate -FilePath C:\temp\dev-signing.cer -CertStoreLocation Cert:\CurrentUser\TrustedPublisher
Import-Certificate -FilePath C:\temp\dev-signing.cer -CertStoreLocation Cert:\CurrentUser\RootCommon Mistake: If you edit a signed script after signing it, the signature becomes invalid and PowerShell blocks the script again. You must re-sign the script every time you make changes. In active development, this workflow is impractical — consider using
RemoteSignedfor your development machine andAllSignedonly in production.
For production environments, use a certificate from a trusted Certificate Authority (CA) or your organization’s internal PKI instead of a self-signed certificate.
Fix 7: Fix the Policy in VS Code, Windows Terminal, or ISE
Different terminal hosts can have their own execution policy quirks.
VS Code Integrated Terminal
VS Code sometimes sets its own execution policy for the integrated terminal. If scripts work in a standalone PowerShell window but fail in VS Code:
- Open VS Code Settings (
Ctrl+,). - Search for
terminal.integrated.shellArgs.windows. - Add
-ExecutionPolicy RemoteSignedto the shell arguments.
Or add this to your settings.json:
{
"terminal.integrated.profiles.windows": {
"PowerShell": {
"source": "PowerShell",
"args": ["-ExecutionPolicy", "RemoteSigned"]
}
}
}PowerShell Profile Script
Your PowerShell profile ($PROFILE) might be setting the execution policy. Check if a profile exists and what it contains:
Test-Path $PROFILE
Get-Content $PROFILEIf the profile contains Set-ExecutionPolicy Restricted or similar, remove or change that line. The profile path is typically C:\Users\<username>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 for PowerShell 7+ or C:\Users\<username>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 for Windows PowerShell 5.1.
PowerShell ISE vs PowerShell Console
Windows PowerShell ISE and the PowerShell console can have different execution policies. ISE uses the same policy scopes, but check both environments if you see inconsistent behavior:
# In ISE
Get-ExecutionPolicy -List
# In regular PowerShell
Get-ExecutionPolicy -ListFix 8: Fix npm, yarn, and CLI Tool Errors Caused by Execution Policy
Many Node.js CLI tools install .ps1 wrapper scripts in your npm global directory. When PowerShell is set to Restricted, commands like npm, yarn, ng, tsc, vue, vite, and eslint all fail with the “running scripts is disabled” error.
The error looks like this:
ng : File C:\Users\you\AppData\Roaming\npm\ng.ps1 cannot be loaded because running scripts is disabled on this system.The fix is the same as Fix 1 — set the execution policy to RemoteSigned:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedIf the .ps1 files were downloaded via npm (which they were), they may also have the zone identifier attached. Unblock the entire npm scripts directory:
Get-ChildItem -Path "$env:APPDATA\npm" -Filter *.ps1 | Unblock-FileAlternatively, you can delete the .ps1 files and rely on the .cmd wrappers instead. npm installs both:
Remove-Item "$env:APPDATA\npm\ng.ps1"The .cmd version runs without any execution policy restrictions because .cmd files are not subject to PowerShell’s execution policy — they run through cmd.exe. However, this is a workaround, not a fix. The proper solution is setting RemoteSigned as shown above.
This is a different class of problem from npm EACCES permission issues, but it can produce similarly confusing error messages when CLI tools suddenly stop working.
Still Not Working?
If you have tried the fixes above and scripts still fail, work through these checks:
Check Which PowerShell Version You Are Running
Windows ships with Windows PowerShell 5.1 (powershell.exe), but many developers also install PowerShell 7+ (pwsh.exe). These are separate installations with separate execution policies and separate profiles.
$PSVersionTable.PSVersionIf you set the policy in one version but run your script in the other, the change has no effect. Set the policy in both:
# In Windows PowerShell 5.1
powershell -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned"
# In PowerShell 7+
pwsh -Command "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned"Check for Registry Corruption
The execution policy is stored in the registry. Corrupted entries can cause unexpected behavior. Check the registry values directly:
# LocalMachine scope
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" -Name ExecutionPolicy -ErrorAction SilentlyContinue
# CurrentUser scope
Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" -Name ExecutionPolicy -ErrorAction SilentlyContinueIf a value is set to something unexpected, remove it and set the policy again:
Remove-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" -Name ExecutionPolicy
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedCheck for Third-Party Security Software
Antivirus software, endpoint protection tools (CrowdStrike, Carbon Black, Symantec Endpoint Protection), and Windows Defender Application Control (WDAC) can block script execution independently of the PowerShell execution policy. If Get-ExecutionPolicy -List shows the correct settings but scripts still fail:
- Check your antivirus logs for blocked script events.
- Temporarily disable real-time protection to confirm if the AV is the cause.
- Add your script directory to the exclusion list if confirmed.
This is conceptually similar to how SSL certificate errors can come from corporate proxy software intercepting connections rather than from the actual application configuration.
Verify the Script File Is Not Corrupted
If a script runs for other users but not for you, the file itself might have encoding issues or hidden characters:
# Check encoding
Get-Content .\script.ps1 -Encoding Byte | Select-Object -First 10PowerShell scripts should be UTF-8 (with or without BOM) or UTF-16 LE. Other encodings can cause parsing failures that look like execution policy errors.
Run a Direct Policy Test
Create a minimal test script to isolate the problem:
"Hello from test script" | Out-File test-policy.ps1
.\test-policy.ps1
Remove-Item test-policy.ps1If this minimal script works but your actual script fails, the problem is in the script itself, not the execution policy. Check for syntax errors, missing modules, or permission issues on the directories the script accesses.
Check for WDAC and AppLocker Enforcement
Windows Defender Application Control (WDAC) and AppLocker enforce script execution at a layer below the PowerShell execution policy. If Bypass does not unblock you and Get-ExecutionPolicy -List shows nothing restrictive, WDAC is the most likely culprit on enterprise images. Check whether WDAC is in enforcement mode:
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard |
Select-Object SecurityServicesRunning, CodeIntegrityPolicyEnforcementStatusIf CodeIntegrityPolicyEnforcementStatus is 2, WDAC is in enforced mode and unsigned scripts are blocked at the kernel level. The execution policy is irrelevant here — you need to sign the script with a certificate that the WDAC policy trusts, or get IT to add an exception. AppLocker shows similar behavior; check Get-AppLockerPolicy -Effective to see whether script rules are active.
Confirm Constrained Language Mode
In addition to blocking scripts outright, WDAC may downgrade your PowerShell session to Constrained Language Mode. Scripts run, but most useful features (COM objects, dynamic type creation, .NET reflection) are blocked. Check your current mode:
$ExecutionContext.SessionState.LanguageModeIf the result is ConstrainedLanguage, the symptoms look identical to an execution policy block but no policy change will fix them. The script must be signed with a trusted certificate to run in FullLanguage mode under WDAC.
Look at the Real PowerShell Process Tree
If a script works in your interactive shell but fails in a parent process (a Task Scheduler job, a SQL Agent job, a CI runner agent), the parent may be launching a different powershell.exe than you expect — often the 32-bit copy from C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe, which has its own execution policy stored in a different registry hive (HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShell). Set the policy in both 32-bit and 64-bit hosts:
# 64-bit
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command "Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force"
# 32-bit
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -Command "Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force"A surprising number of “intermittent” script execution failures resolve once both hives match.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Electron Forge Not Working — Makers, Code Signing, Native Modules, and Publishers
How to fix Electron Forge errors — forge.config.js makers per OS, code signing on macOS (notarytool) and Windows, native module rebuild via electron-rebuild, Vite/Webpack plugin, auto-updater, and GitHub publisher.
Fix: joblib Not Working — Parallel Backends, Memory Cache, and Pickling Errors
How to fix joblib errors — Parallel n_jobs slower than expected, Memory cache miss, backend loky vs threading vs multiprocessing, pickling lambda not supported, dump load file size, and pytest interference.
Fix: Marshmallow Not Working — Schema Errors, Load vs Dump, and Field Validation
How to fix Marshmallow errors — Schema not validated on dump, ValidationError messages format, unknown field handling, missing vs default, post_load object construction, and Marshmallow 3 to 4 migration.
Fix: Pipenv Not Working — Lock File Generation, Shell Activation, and Dependency Resolution
How to fix Pipenv errors — pipenv lock takes forever, Pipfile.lock not generated, shell activation broken, no virtualenv created, dependency conflict, and migration to uv or Poetry.