If you have ever tried to run a .ps1 script on a fresh Windows Server and got the “running scripts is disabled” error, you have met the execution policy. It is not a security boundary — a determined user can bypass it — but it is a practical guardrail that prevents accidental script execution and enforces a signing workflow. On a Windows VPS exposed to the internet, getting this right matters. This article covers the six policy levels, the recommended configuration for production, and how to set up code signing so you never have to choose between Bypass and a broken deployment.
The Six Execution Policy Levels at a Glance
PowerShell checks the execution policy before running any script. The default differs by OS: Windows Server ships with RemoteSigned, while Windows client editions start with Restricted. Here is what each level actually does:
| Policy | Default On | Behavior |
|---|---|---|
| Restricted | Windows Client | No scripts run at all. Only interactive commands. |
| RemoteSigned | Windows Server | Local scripts run freely. Downloaded scripts must be signed by a trusted publisher or unblocked via Unblock-File. |
| AllSigned | N/A | Every script — local and remote — must be digitally signed by a trusted publisher. |
| Unrestricted | N/A | All scripts run. Downloaded scripts prompt once. |
| Bypass | N/A | Nothing is checked. Use only for one-off bootstrapping, never as a permanent setting. |
| Undefined | N/A | No policy set at this scope. Falls through to the next applicable scope. |
The default RemoteSigned is the right choice for most servers. Local scripts — your automation, deployment, and monitoring scripts — run without friction. Only scripts from the internet (marked with a Zone.Identifier alternate data stream) need explicit trust. That is a sane default: it does not block your own work, and it stops a drive-by download from running without your consent.
How to Check and Set the Effective Policy
The policy is organized into four scopes. The most restrictive wins, so checking the effective policy requires looking at all of them:
# Check the effective policy (the one that actually applies)
Get-ExecutionPolicy
# Check all scopes
Get-ExecutionPolicy -List
# Set machine-wide policy (requires admin)
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine
The LocalMachine scope is the one that matters on a server. Group Policy can override it, so if you manage a fleet, check Computer Configuration → Administrative Templates → Windows Components → Windows PowerShell for the “Turn on Script Execution” policy.
Why RemoteSigned Is the Right Default for Production
Three reasons RemoteSigned beats the alternatives on a Windows VPS:
- Local scripts are not blocked. Your scheduled tasks, deployment scripts, and monitoring loops all run without modification.
- Downloaded scripts are gated. An attacker who drops a script via an exploited web app or misconfigured file share cannot run it without signing or unblocking it.
- It is not
Bypass.Bypassis the “I give up” setting. It is common in CI/CD one-liners, but it should never be the machine-wide default.
If you need to run a specific downloaded script without signing it, use Unblock-File on that file rather than lowering the policy. This removes the Zone.Identifier alternate data stream that marks it as untrusted:
Unblock-File -Path "C:\Scripts\deploy-tool.ps1"
Setting Up Code Signing for Production Scripts
For a team or an automated pipeline, the best approach is to sign your own scripts with a code-signing certificate. This lets you use AllSigned on the server, which blocks everything — including local scripts — unless they are signed by a trusted publisher. The workflow:
- Obtain a code-signing certificate. Purchase one from a public CA (DigiCert, Sectigo, GlobalSign) or issue one from your internal AD CS if you run Active Directory.
- Install it on the signing machine (your development workstation, not the server).
- Sign scripts with
Set-AuthenticodeSignature:
# Get the code-signing certificate from your store
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
# Sign the script
Set-AuthenticodeSignature -FilePath "C:\Scripts\deploy.ps1" -Certificate $cert
- Distribute the signing certificate’s public key to the server via Group Policy or by installing it into the Trusted Publishers store on the server.
- Set the server to AllSigned:
Set-ExecutionPolicy AllSigned -Scope LocalMachine.
This setup means only scripts signed with your certificate will run on the server. An unsigned script dropped by an attacker is rejected immediately, even if the account running it has admin rights.
Common Bypass Techniques and Why They Are Not Security Flaws
Execution policy is a guardrail, not a firewall. A user with sufficient privileges can bypass it in several ways, and that is by design — PowerShell is a shell, not a sandbox. The most common bypasses:
| Bypass | Command | When It Matters |
|---|---|---|
| Inline invocation | powershell -ExecutionPolicy Bypass -File script.ps1 | CI/CD runner invoking a script directly |
| Encoded command | powershell -EncodedCommand <base64> | Obfuscation in attack payloads |
| Copy-paste | Pasting commands into an interactive session | Interactive sessions only; does not affect script execution |
| Scheduled task | schtasks /create ... with a bypass flag | Admins creating tasks; non-admins cannot set bypass |
None of these are vulnerabilities. They are deliberate features that let administrators run scripts when the policy blocks them. The real security layer is execution policy + Constrained Language Mode + AppLocker for environments that need deep lockdown, but for most Windows VPS workloads, RemoteSigned combined with careful NTFS permissions on the script directories is sufficient.
Recommendations by Scenario
| Scenario | Recommended Policy | Reason |
|---|---|---|
| Single developer VPS | RemoteSigned | Local scripts run; downloaded tools need explicit unblock |
| Production web server | RemoteSigned | Deployment scripts run; no risk from random downloads |
| Team-managed server with CI/CD | AllSigned + code signing | Only signed scripts from the build pipeline execute |
| Shared hosting / multi-tenant | AllSigned + AppLocker | Prevent one tenant from running arbitrary scripts |
| Bootstrap / initial setup | Bypass (temporary) | Revert to RemoteSigned after provisioning |
Execution policy is one of those settings that is easy to set and forget once you understand the tradeoffs. After you have configured it, review the Windows VPS feature overview for other server-hardening defaults worth checking. And if you are still provisioning the server itself, compare Windows VPS plans to find a provider that gives you the full administrative access needed to configure these policies.


