Originally published on peateasea.de.
While playing with Raku and rakubrew
on Windows recently, I encountered more than one stumbling block. One stuck out in particular. Did you know that, by default, Windows PowerShell doesnât allow normal users to run scripts? Coming from a Unix background, that surprised me. You might come across this issue when setting up rakubrew
in Windows PowerShell, hence I thought Iâd share my solution.
An unexpected error
Iâll admit it: the issue I describe here doesnât have all that much to do with Raku. It has much more to do with me stumbling over default Windows policies. Itâs something I noticed after installing rakubrew
in Windows PowerShell and hence could affect others. This might stop them from installing rakubrew
and they may well avoid learning Raku. That would be bad.
The issue appeared after installing rakubrew
in PowerShell on Windows:
# install rakubrew
. {iwr -useb https://rakubrew.org/install-on-powershell.ps1 } | iex
# add rakubrew initialisation code to PowerShell startup script
New-Item -Path (Split-Path $PROFILE) -ItemType "Directory" -Force
Add-Content -Force -Path $PROFILE -Value '. "C:\rakubrew\bin\rakubrew.exe" init PowerShell | Out-String | Invoke-Expression'
I started a fresh shell to initialise the rakubrew
environment, yet I received this error message instead:
. : File C:\Users\cochrane\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.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:3
+ . 'C:\Users\cochrane\Documents\WindowsPowerShell\Microsoft.PowerShell ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
This surprised me. Iâd expected the shell to start and provide me with the rakubrew
command so I could get on with my day. Why did the installation and setup instructions (seemingly) not work? The reason had to do with Windows PowerShell execution policies.
A shortcut to a solution
The default execution policy for Windows clients is Restricted
, forbidding users from running scripts. The documentation states that this setting:
- Permits individual commands, but does not allow scripts.
- Prevents running of all script files, including formatting and configuration files (
.ps1xml
), module script files (.psm1
), and PowerShell profiles (.ps1
).
The solution is to allow the current user to run scripts. Giving users the RemoteSigned
permission1 is sufficient for that purpose. To set this policy, run:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Now a mere mortal normal user can run script files. Also, the rakubrew
initialisation now runs without error when Windows PowerShell starts. Yay! \o/
Many details
Of course, the situation is much more nuanced than the terse solution described above. For those so inclined, I provide more background information than you thought you wanted. Details are often critical when dealing with computing systems, hence why I mention them here. And, as usual, when I have the opportunity to investigate something like this, Iâm thorough!
Being direct gets you what you want
Did you notice that we run a script as part of the rakubrew
installation, i.e.
. {iwr -useb https://rakubrew.org/install-on-powershell.ps1 } | iex
but it doesnât cause an error? Doesnât that seem a bit weird to you? Well, technically the script isnât run by Windows PowerShell; its contents are passed to iex
, which executes them. Since weâre running these commands by hand, weâre allowed to run them2. This explains why we donât see an error when installing rakubrew
.
Fear its presence
The remaining rakubrew
installation steps create the Windows PowerShell user directory
New-Item -Path (Split-Path $PROFILE) -ItemType "Directory" -Force
and then the Windows PowerShell profile file
Add-Content -Force -Path $PROFILE -Value '. "C:\rakubrew\bin\rakubrew.exe" init PowerShell | Out-String | Invoke-Expression'
filling it with the content
. "C:\rakubrew\bin\rakubrew.exe" init PowerShell | Out-String | Invoke-Expression
The content isnât important for the execution policy error to occur: itâs the presence of the profile file itself.
You can try this at home. Enable the default execution policy like so:
Set-ExecutionPolicy -ExecutionPolicy Default -Scope CurrentUser
This sets the CurrentUser
âs policy to Restricted
, which the Get-ExecutionPolicy -List
command shows:
PS C:\Users\cochrane> Get-ExecutionPolicy -List
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Restricted
LocalMachine Undefined
Windows PowerShell expects its profile file to be at the location in the $PROFILE
environment variable. You can find out the value of this environment variable by typing its name into the shell:
PS C:\Users\cochrane> $PROFILE
C:\Users\cochrane\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
Now create an empty file at this location e.g. via the New-Item
command:
PS C:\Users\cochrane> New-Item $PROFILE
Directory: C:\Users\cochrane\Documents\WindowsPowerShell
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 26/03/2024 17:21 0 Microsoft.PowerShell_profile.ps1
Starting a fresh Windows PowerShell session greets us with our now familiar error message:
. : File
C:\Users\cochrane\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.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:3
+ . 'C:\Users\cochrane\Documents\WindowsPowerShell\Microsoft.PowerShell ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
Thus only the presence of this file causes the error when a user is Restricted
.
A shell by any other version
One suspicion I had for why this problem appeared was that Windows PowerShell was too old, or perhaps there was a problem with my system.
Checking my Windows PowerShell version metadata showed that it might be a bit old:
PS C:\Users\cochrane> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.4170
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.4170
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Iâm running Windows 10 and the system is completely up-to-date, so things should be ok.
Microsoft also mentions that there are few differences in the PowerShell language between Windows PowerShell and PowerShell. Thus itâs unlikely that the Windows PowerShell version is causing this issue or is in any way special.
Itâs also possible that this is just a case of PEBKAC and I changed some setting ages ago that I now canât recall. Despite that, I rarely use PowerShell and consider this explanation unlikely.
From the information I was able to find, my version of Windows PowerShell seems to be fine.
Different PowerShell, different behaviour
Of course, as soon as I started wondering if the problem was due to an outdated version, I considered upgrading. Because they are two different projects, one can install PowerShell version 7 in parallel to Windows PowerShell, which I then did.
We have to be careful when using the name âPowerShellâ because we need to differentiate between the two shells. Due to the similar names, I use âPowerShellâ to refer to the new shell and âWindows PowerShellâ to refer to the legacy program.3
Get this though: PowerShell has a different default execution policy to Windows PowerShell đ.
Here is PowerShellâs default execution policy list:
PS C:\Users\cochrane> Get-ExecutionPolicy -List
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Undefined
LocalMachine RemoteSigned
And here is Windows PowerShellâs default execution policy list:
PS C:\Users\cochrane> Get-ExecutionPolicy -List
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Restricted
LocalMachine Undefined
Note how PowerShell has the policy RemoteSigned
in the LocalMachine
scope. This means anyone on the local machine can run scripts. I discovered this after a fresh install of PowerShell. The behaviour thus directly contradicts the PowerShell documentation!
So, after installing rakubrew
and adding its startup code to the profile file, PowerShell sessions start without a problem. Itâs what any new rakubrew
user would want.
Grokking the docs (or trying to)
Computer systems are complex, intricate beasts and it can be hard to find relevant information in the documentation. For instance, although the Windows Execution Policy documentation states that
The execution policy for a particular session is stored only in memory and is lost when the session is closed.
the execution policy I set in Windows PowerShell was persistent; even across reboots. I donât know what the docs are trying to tell me here. Call me confused. đ
So what is the default Windows PowerShell execution policy? The introductory (long description) section of the PowerShell documentation states that
On non-Windows computers, the default execution policy is
Unrestricted
and cannot be changed.
But this section doesnât mention what the default execution policy is for Windows computers. Where is that information hiding?
One has to dig a bit harder to find it:
Default
- Sets the default execution policy.
Restricted
for Windows clients.RemoteSigned
for Windows servers.
Thus, one can run scripts on Windows servers (RemoteSigned
) but not as a normal user on Windows client computers (Restricted
).
Further digging uncovered more nuggets of information:
Restricted
- The default execution policy for Windows client computers.
including:
If no execution policy is set in any scope, the effective execution policy is
Restricted
, which is the default for Windows clients.
So, the default execution policy on Windows clients (presumably for the CurrentUser
or the LocalMachine
) is Restricted
. I think it would be helpful to mention this in the introductory description. Even so, taking the time to peruse the documentation did help me form a solution to my permissions problem.
Defaulting on a promise
Setting the Default
policy without setting an explicit scope4 gave this error
PS C:\Users\cochrane> Set-ExecutionPolicy -ExecutionPolicy Default
Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): Y
Set-ExecutionPolicy : Access to the registry key
'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied. To change the execution
policy for the default (LocalMachine) scope, start Windows PowerShell with the "Run as administrator" option. To
change the execution policy for the current user, run "Set-ExecutionPolicy -Scope CurrentUser".
At line:1 char:1
+ Set-ExecutionPolicy -ExecutionPolicy Default
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Set-ExecutionPolicy], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
Even so, itâs possible to set the execution policy for the current user:
PS C:\Users\cochrane> Set-ExecutionPolicy -ExecutionPolicy Default -Scope CurrentUser
Execution Policy Change
The execution policy helps protect you from scripts that you do not trust.
Changing the execution policy might expose you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): Y
Checking the list of execution policies gives the value we expect from the documentation:
PS C:\Users\cochrane> Get-ExecutionPolicy -List
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Restricted
LocalMachine Undefined
Making this change disallowed script execution (as expected) and gave the error that led me down this particular garden path. This leads me to believe that this could have been the setting causing my initial woes. Note also that setting this value to Undefined
leads to the same error. With that set, the default execution policy kicks in, disallowing the user from running any scripts.
Finding use in a jumble of trifles
So, what did we learn?
-
rakubrew
installations on Windows PowerShell can raise unexpected errors, - the execution policy for Windows PowerShell must allow users to run scripts for
rakubrew
to work as expected, - Windows PowerShell and PowerShell are two separate things, seemingly with different default behaviours,
- documentation doesnât always match reality, and
- the devil is always in the details.
I hope this information was useful. Happy hacking!
I find this execution policy name rather opaque. It doesnât explain its purpose clearly: that of allowing scripts to run. After a bit of digging in the documentation, I guess it means âbe able to run stuff like on a Windows serverâ. A Windows server is usually a remote machine and in that environment, one would need to run scripts. Itâs a tenuous theory at best, though. â©
The default execution policy permits users to run individual commands. â©
I.e. bundled as part of Windows 10 and only available up to version 5.x. â©
Setting the
Default
policy for theLocalMachine
scope failed as it must be run as the administrator. Now I have this vague feeling that allExecutionPolicy
scopes had the valueUndefined
when I started down this path. Oh well. â©
Top comments (0)