PowerShell: Running Scripts Is Disabled on This System
You try to run a script and PowerShell refuses:
File C:\Scripts\demo.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: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnauthorizedAccess
Or this one, which is a different problem with the same cause:
File C:\Scripts\demo.ps1 cannot be loaded. The file C:\Scripts\demo.ps1 is not
digitally signed. You cannot run this script on the current system.
All the answers that you are going to get say to Set-ExecutionPolicy. This might be correct but usually fails, as there are three different entities that generate such errors and only one of those can be solved in this way.
First, what execution policy is not
It’s not even a security boundary. They themselves have stated that in their documents. If someone can run PowerShell, then within 4 key-strokes, one can bypass it. I will teach you how, further in this essay.
It prevents accidental double-clicks by you. Think of it as a seatbelt, not a lock.
The five scopes, and which one is winning
This is the part that catches people. There is not one execution policy, there are five, and they override each other in a fixed order:
MachinePolicy -> UserPolicy -> Process -> CurrentUser -> LocalMachine
Highest wins. So Get-ExecutionPolicy on its own tells you the effective answer but not which scope produced it. Always ask for the list:
Get-ExecutionPolicy -List
Here is my own machine:
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Bypass
CurrentUser Undefined
LocalMachine RemoteSigned
The proper policy setting is Bypass, not RemoteSigned. Whatever it is that is set as Process in this instance is better than LocalMachine.
The MachinePolicy and UserPolicy are defined by Group Policy. If either of these is defined, nothing you enter is going to do any good, and you will have an afternoon proving this point to yourself.
Fix 1 – set it for yourself only
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
-Scope CurrentUser does not need administrator rights, and it does not change the machine for anybody else. On a shared or managed machine that matters.
RemoteSigned is the sensible setting: scripts you wrote locally run, scripts that came from somewhere else need a signature. It is the default on Windows Server for that reason.
Fix 2 – bypass it for one command
If you cannot change the policy, or do not want to:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\demo.ps1
This changes nothing permanently. It applies to that one process and then it is gone.
I ran the same blocked script three ways to show the difference:
-ExecutionPolicy Restricted "running scripts is disabled on this system"
-ExecutionPolicy AllSigned "is not digitally signed"
-ExecutionPolicy Bypass the script ran
Which is also the demonstration that execution policy is not security. Four extra words on the command line and the “blocked” script runs.
Fix 3 – the one nobody mentions
Here’s the example where the perception arises of your policy being flawed.
Your policy is set as RemoteSigned. You have written a script yourself, and it works fine. Someone sends it to you via email or you have downloaded it; it doesn’t run, with “not digitally signed” error this time as compared to “disabled on this system”.
Your policy is absolutely fine. It is the file which is tagged.
Windows puts a hidden alternate data stream on any file that reaches it through the internet or an email client. You can
Get-Item .\demo.ps1 -Stream *
Stream Length
------ ------
:$DATA 30
Zone.Identifier 26
That Zone.Identifier stream is the mark of the web, and RemoteSigned is doing exactly what it says by treating the file as remote.
Remove it:
Unblock-File -Path .\demo.ps1
Same script, same policy, straight afterwards:
the script ran
Do remember, however, that Unblock-File just clears the marker and will neither alter nor dilute your policy, nor will it impact any other file. This is the correct solution for this scenario, while using the execution policy to circumvent it is the incorrect one.
A collection of scripts
Get-ChildItem .\Scripts\*.ps1 -Recurse | Unblock-File
Fix 4 – you fixed the wrong PowerShell
This one took me some time to find out, so I guess you might as well know.
The execution policies of Windows PowerShell 5.1 and PowerShell 7 are stored at two entirely distinct locations. One has no effect on the other.
They are distinct registry keys
Windows PowerShell 5.1 HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
PowerShell 7 (Core) HKLM\SOFTWARE\Microsoft\PowerShellCore\ShellIds\Microsoft.PowerShell
RemoteSigned policy in the LocalMachine and the CurrentUser settings for 5.1 on my computer has RemoteSigned policy setting. The PowerShell 7 keys, however, do not exist at all.
That means that if you opened the blue window, executed Set-ExecutionPolicy command, and then tried running the black window and saw the same error message, this is why. Run $PSVersionTable.PSVersion in the window causing issues.
The order I would check them in
Get-ExecutionPolicy -List– find which scope is actually winning- If MachinePolicy or UserPolicy is set, stop. That is Group Policy and it is not your decision
Get-Item .\script.ps1 -Stream *– if there is a Zone.Identifier, Unblock-File is your fix- Check which PowerShell you are in
- Then, and only then,
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Four out of the five don’t cost you anything and don’t change anything at all. The instinct to click “Set-ExecutionPolicy” first is what leads to having an unblocked PC but still a non-working script.