Written August 24, 2021

So you want to run a PowerShell script and you're getting:

echo "echo hi" > test.ps1
.\test.ps1 : File test.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
+ .\test.ps1
+ ~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess`

This is similar to macOS running untrusted binaries, or on Windows running an installer from an unknown developer.

One workaround is to disable the restricted policy...but that reduces the security of your computer.

There is a way to temporarily disable it, for the duration of the parent PowerShell Process.

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
# hi

You'll notice if you run another instance of Windows PowerShell, it won't run the script, so you're only vulnerable within that one window until you close it.

Not bad, but there's got to be a better way...

Signed PowerShell scripts

Sign your own PowerShell scripts so Windows will trust YOUR scripts, but not anyone's scripts.

# check your current execution policy for your user, undefined by default:
Get-ExecutionPolicy -Scope CurrentUser
# change it for your user only, to allow only signed scripts
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy AllSigned -Force

Generate a signing key

# I would be ok doing this once every 3 years
New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My -Type CodeSigningCert -Subject CN=me -NotAfter (Get-Date).AddYears(3)

Export the public certificate

Get-ChildItem Cert:\CurrentUser\My | Where-Object -Property Subject -EQ "CN=me" | Export-Certificate -FilePath public.der

Trust your public certificate

# import the certificate into the root store, i.e. treat it as a Certificate Authority (CA)
Import-Certificate -CertStoreLocation Cert:\CurrentUser\Root .\public.der

You'll be prompted with a warning

install certificate

click Yes.

Sign your PowerShell script

Set-AuthenticodeSignature -Certificate (Get-ChildItem Cert:\CurrentUser\My | Where -Property Subject -EQ "CN=me") -TimestampServer http://timestamp.digicert.com -FilePath .\test.ps1
# need to trust the certificate the first time

Always Run Type A for Always run.

There, now you can run your own PowerShell scripts without compromising your security.

.bashrc equivalent for PowerShell

In bash, there is a .bashrc/.bash_profile script that will run everytime you instantiate a shell. The PowerShell equivalent of bashrc is a file called Profile.ps1 in your Documents\WindowsPowerShell directory.

# create the WindowsPowerShell directory if it doesn't already exist
New-Item -Type Directory "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell" -ErrorAction Ignore
# put stuff you want in there
code "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell\Profile.ps1"
# sign it
Set-AuthenticodeSignature -Certificate (Get-ChildItem Cert:\CurrentUser\My | Where -Property Subject -EQ "CN=me") -TimestampServer http://timestamp.digicert.com -FilePath "$([Environment]::GetFolderPath("MyDocuments"))\WindowsPowerShell\Profile.ps1"