PowerShell Execution Policy: Stop Script-Blocking Errors and Enforce Code Signing on Windows Server

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:

PolicyDefault OnBehavior
RestrictedWindows ClientNo scripts run at all. Only interactive commands.
RemoteSignedWindows ServerLocal scripts run freely. Downloaded scripts must be signed by a trusted publisher or unblocked via Unblock-File.
AllSignedN/AEvery script — local and remote — must be digitally signed by a trusted publisher.
UnrestrictedN/AAll scripts run. Downloaded scripts prompt once.
BypassN/ANothing is checked. Use only for one-off bootstrapping, never as a permanent setting.
UndefinedN/ANo 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. Bypass is 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:

BypassCommandWhen It Matters
Inline invocationpowershell -ExecutionPolicy Bypass -File script.ps1CI/CD runner invoking a script directly
Encoded commandpowershell -EncodedCommand <base64>Obfuscation in attack payloads
Copy-pastePasting commands into an interactive sessionInteractive sessions only; does not affect script execution
Scheduled taskschtasks /create ... with a bypass flagAdmins 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

ScenarioRecommended PolicyReason
Single developer VPSRemoteSignedLocal scripts run; downloaded tools need explicit unblock
Production web serverRemoteSignedDeployment scripts run; no risk from random downloads
Team-managed server with CI/CDAllSigned + code signingOnly signed scripts from the build pipeline execute
Shared hosting / multi-tenantAllSigned + AppLockerPrevent one tenant from running arbitrary scripts
Bootstrap / initial setupBypass (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.

Leave a Comment