- The combination of AppLocker/WDAC with ExecutionPolicy and script signing allows granular control of what code is executed on computers.
- Execution policies are not an absolute security system, but they reduce human error and force the adoption of more secure practices such as AllSigned or RemoteSigned.
- The use of code signing certificates (ideally from a corporate PKI) is key to validating scripts in strict policies and large-scale deployments.
- Real security is achieved by adding several layers: AppLocker/WDAC, ExecutionPolicy, permissions NTFSGPO and good certificate and script management.

In many business environments, the quick fix is still the preferred option: lower the defenses of PowerShell setting the ExecutionPolicy to Unrestricted And that's it. It works, yes, but it opens the door wide for any user to launch potentially dangerous scripts, both on servers and on user computers.
The truth is that Windows It gives us very powerful tools to do things right: Sign scripts, harden execution policies, and complement it with AppLocker or WDAC to control which code runs on the machines. Properly configured, you can have automation, flexibility, and security simultaneously, without resorting to brute force.
AppLocker and WDAC: controlling which scripts run
AppLocker and its evolution, Windows Defender Application Control (WDAC), allow define which scripts and binaries are authorized to run in your systems. They don't replace PowerShell's ExecutionPolicy, but rather complement it as an additional layer of defense.
AppLocker works with separate rule collections (executables, installers, scripts, packaged applications, etc.), and in the specific case of scripts, it only controls a few very specific file types. This is important because many organizations believe they "block everything" when in reality they only filter a few formats.
In the script rules collection, AppLocker only considers the following file formats:
- . Ps1 – PowerShell scripts
- '.bat' – classic batch files cmd
- .cmd – another variant of batch scripts
- .vbs – VBScript scripts
- . Js – JScript scripts
When you enable AppLocker for scripts, if you don't create any "Allow" rules, the default behavior is very clear: Everything is blocked except what the rules explicitly authorize.That's why understanding the predetermined rules is so important.
In a typical policy, AppLocker offers a series of default rules for the script collection that are usually used as a base:
- Allow local administrators to run all scripts
Rule (default): “All scripts”
User: BUILTIN\Administrators
Route condition:*\(equivalent to any route) - Allow all users to run scripts located in the Windows folder
Rule (default): “All scripts located in the Windows folder”
User: Everyone
Path:%windir%\* - Allow all users to run scripts from the Program Files folder
Rule (default): “All scripts located in the Program Files folder”
User: Everyone
Path:%programfiles%\*
With this minimal set you achieve that the operating system and installed applications function without breakingBut you prevent users from launching scripts from arbitrary locations like the Desktop or folder Downloads.
Creating AppLocker rules using GPO
In domain environments, it is common to Manage AppLocker through Group PoliciesThis allows you to apply the same script execution control to hundreds or thousands of computers without having to touch each one.
To configure AppLocker, you work from the Group Policy Administrator on a domain controller or an RSAT console. The AppLocker configuration path is:
Path: Configuración del equipo → Directivas → Configuración de Windows → Configuración de seguridad → Directivas de control de aplicaciones → AppLocker
Within AppLocker you'll find several branches: executables, installers, scripts, and packaged applications. To enforce the use of scripts, you need to focus on the collection of script rules and activate the application of rules by clicking on “Configure the application of rules”.
Once enabled, AppLocker behaves according to whitelisting logic: Block by default and allow only what is defined.To speed up the initial task, you can use the “Automatically generate rules” wizard, which analyzes a folder and creates Allow rules based on:
- Editor: identifies scripts signed by a specific publisher (very useful for corporate software or software from trusted vendors).
- Ruta: allows or blocks scripts in specific paths (for example,
C:\ScriptsSeguros\*). - File hash: it is based on the file's fingerprint, so if the file changes, the rule is no longer valid.
This automatic generation saves a lot of time because Create Allow rules for the software that is already installedThis reduces the risk of rendering systems unusable when AppLocker is activated. You can then supplement this with manual rules for sensitive paths or corporate scripts.
WDAC: Enhanced version of application control
Windows Defender Application Control (WDAC) is, to put it simply, the “supercharged AppLocker” for stricter or modern scenariosIt is designed for environments where you really want to have a serious binary-level whitelisting model. drivers and scripts.
WDAC's logic is similar: You can only execute what a policy explicitly allows.This includes EXEs, DLLs, PowerShell scripts, MSIs, drivers, UWP apps, etc. It's much more granular and powerful than AppLocker, but also more complex to deploy.
Many administrators combine WDAC with PowerShell execution policies and script signing to build a layered defense model: el script It must be permitted by WDAC/AppLocker and also comply with the ExecutionPolicy.If either of these fails, it will not run.
PowerShell execution policies: what they are and what they are not
PowerShell's ExecutionPolicies cause a lot of confusion because many people think they are a kind of “impenetrable security wall”In fact, the official documentation itself makes it clear: they are a lightweight security mechanism designed to prevent human error and accidental execution, not an anti-malware system.
The implementation policy decides, in very brief terms, Under what conditions can PowerShell load scripts and configuration files?Before running a .ps1, PowerShell checks the effective policy and the file's origin (local, remote, downloaded from the Internet, etc.), and decides whether to allow it, block it, or display warnings.
real benefits of implementation policies are:
- They reduce the likelihood that a user will unintentionally run a malicious script received by email or downloaded from the web.
- They promote safer scripting practices, such as digital signature with certificates in AllSigned or RemoteSigned policies.
- They allow different rules to be applied depending on the script's origin (local vs remote), making security more flexible.
- They can be combined with GPO, AppLocker, and WDAC to create a consistent code control policy.
But it's also important to be clear about their limitations:
- They are easy to circumvent for a privileged user, for example by launching
powershell.exe -ExecutionPolicy Bypassor using other scripting languages. - They do not analyze the script's content: A signed script can be malicious if the certificate has been compromised or the author is malicious.
- They only affect PowerShell, not VBScript, Pythonnative binaries, etc., so they do not replace a global application control.
- An excessively lax configuration (“Unrestricted” by default) creates a false sense of security, because it seems that “there is a policy” but in practice it does not block anything relevant.
Types of ExecutionPolicy in PowerShell
PowerShell defines several execution policy modes that determine which scripts can be run and from whereIt's important to understand each one well so as not to overdo it or fall short.
Main types of ExecutionPolicy are:
Restricted
This is the default policy on many Windows clients and means that PowerShell scripts cannot be run at allOnly the following are allowed commands entered interactively into the console or ISE.
This policy is suitable for workstations or servers where automation is not expectedHowever, as soon as you need to schedule tasks or deployments with scripts, you will have to change it or use another policy through GPO.
RemoteSigned
This is the default policy on many Windows servers. Under this mode, Locally created scripts can be executed without a signatureHowever, any script downloaded from the Internet or received from another machine must be signed by a trusted publisher.
This is supported by the mechanism of Mark of the Web (MotW)which labels downloaded files with region information. If you download a .ps1 file from the internet and want to run it unsigned, you'll have to explicitly unlock it with Unblock-File or remove that marking.
AllSigned
With this policy, All scripts and configuration files, including those created locally, must be digitally signed. by a certificate that the system trusts. If one is not trusted or the signature is invalid, it will not run.
Additionally, a warning is typically displayed the first time you run a script signed by a new publisher, so the user can decide whether or not to trust that certificate. This is the ideal policy for corporate environments where PKI exists and there is a real desire to control who can publish scripts.
Unrestricted
This policy allows you to execute all scripts without mandatory signaturesRemote or downloaded scripts may display a warning to the user before running, but nothing a quick click can't fix.
It's tempting to use this policy in development environments or in laboratories because "everything works the first time," but in production it's a ticking time bomb. It should be avoided on physical workstations and servers.except in very justified cases.
Bypass
In Bypass, PowerShell no restrictions apply and no warnings are displayedIt's as if there were no ExecutionPolicy, designed primarily for automation processes, orchestrators, integrations with other applications, or CI/CD pipelines where another layer of security already exists.
Using Bypass as a global policy on user equipment is basically waive any type of protection against scriptsIf it is used, it should be in a controlled manner, limited to specific sessions or very specific processes.
Undefined and Default
The Undefined option indicates that There is no explicit policy configured in that scope. If all scopes are set to Undefined, the default behavior will be Restricted on Windows clients and RemoteSigned on servers.
The Default option is usually used to revert to the product's default setting on that specific system, that is, Restricted for client and RemoteSigned for server.
ExecutionPolicy Scopes and Priority
The same machine can have multiple execution policies defined simultaneouslyeach in a different area. PowerShell determines which one is effective based on a priority hierarchy.
The main scopes are:
- Machine Policy: It is defined by GPO at the computer level; it applies to all users of the machine.
- UserPolicy: also via GPO, but it only affects the specific user to whom the policy applies.
- : It only applies to the current PowerShell session and lives in memory; when you close the console, it disappears.
- LocalMachine: affects all users of the computer and is normally stored in the registry (HKLM).
- CurrentUser: applies only to the current user (HKCU).
La precedence between areas This is key, because it explains why sometimes your changes with Set-ExecutionPolicy "do nothing":
- MachinePolicy (GPO team)
- UserPolicy (GPO user)
- LocalMachine
- CurrentUser
This means that A policy defined via GPO will always override any local changes you try to make.If MachinePolicy says AllSigned and you try to set RemoteSigned to LocalMachine, the system will still apply AllSigned.
How to view and change the ExecutionPolicy
To check which policy is actually active, it's advisable to review all policies by area running:
Get-ExecutionPolicy -List
This command shows you, from top to bottom, the policies in MachinePolicy, UserPolicy, Process, CurrentUser, and LocalMachine, and which one is effective. basic diagnostic tool when “something doesn’t add up”.
To change policy in a specific area, one uses Set-ExecutionPolicySome typical examples:
- Set AllSigned at the team level:
Set-ExecutionPolicy AllSigned -Scope LocalMachine -Force - Allow local scripts but require remote scripts to be signed by the current user:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force - Change only the current session (useful for testing):
Set-ExecutionPolicy Bypass -Scope Process -Force
To modify the policy in the LocalMachine scope, You need to open PowerShell as administratorIf a GPO is defining MachinePolicy or UserPolicy, any attempt to make local changes in lower scopes will return an error indicating that The policy is controlled by a Group Policy Object.
Using GPO to enforce the execution policy
In corporate networks, it's common practice to centralize these configurations using Group Policy Objects (GPOs). This is done using the relevant policy. “Turn on Script Execution”, available in:
Path: Configuración del equipo → Plantillas administrativas → Componentes de Windows → Windows PowerShell
This configuration allow:
- Disable script execution completely (equivalent to Restricted).
- Enable script execution and choose the policy type (for example, RemoteSigned or AllSigned).
- Do not configure it, in which case PowerShell will use local policies (Set-ExecutionPolicy) without the GPO interfering.
Once the GPO is linked to the desired OU, you can force its application on clients with gpupdate /force and verify the result with Get-ExecutionPolicy -ListYou'll see how the MachinePolicy or UserPolicy values take control.
Signing PowerShell scripts: the professional approach
Instead of taking the easy way out by lowering security, the reasonable thing for an organization to do is Leave the ExecutionPolicy set to AllSigned or RemoteSigned and sign all scripts that will circulate through the domain.
For this you need a code signing certificateThere are two options: use a corporate PKI (such as ADCS) or, for testing purposes, generate a self-signed certificate. In a serious domain environment, the best approach is to deploy an internal CA and create a specific code-signing template.
The typical flow with ADCS is
- Install the Active Directory Certificate Services role on a server.
- Create or adjust a Code Signing certificate template, defining which users or groups can request it (for example, administrators or DevOps team).
- Publish the template so that it is available in the CA.
- From the administrator's computer, open the certificate add-in for the current user (personal store) and request a new code signing certificate to the CA.
- Distribute the root certificate and, if applicable, intermediate certificates to all computers using GPO, so that the issuer is trusted throughout the domain.
Once you obtain the certificate, you can locate it in the user's store with:
$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert
With that certified object in hand, Signing a script is as simple as:
Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert
If instead of using the local warehouse you have a .pfx exported (for example, to sign from a build machine), you can load with:
$cert = Get-PfxCertificate -FilePath C:\Certs\MiFirma.pfx
Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert
It is highly recommended for a corporate environment. Include the entire certification chain and a timestamping authorityso that the signature remains valid even if the certificate expires:
Set-AuthenticodeSignature -FilePath C:\Scripts\MiScript.ps1 -Certificate $cert -IncludeChain All -TimestampServer "http://timestamp.globalsign.com/scripts/timstamp.dll"
At any time you can check the signature status from a script with:
Get-AuthenticodeSignature .\MiScript.ps1 | Format-List *
If you need to run quick tests without a real PKI, PowerShell allows you to create a self-signed code signing certificate:
New-SelfSignedCertificate -FriendlyName "Ejemplo firma de código" -CertStoreLocation Cert:\CurrentUser\My -Subject "CN=FirmaScripts" -Type CodeSigningCert
Bypass and other methods to bypass the ExecutionPolicy
To avoid any misunderstandings, it's worth remembering that The enforcement policy can be circumvented in several ways. That's why it shouldn't be your only defense measure, but rather a complement to AppLocker/WDAC, NTFS permissions, and endpoint controls.
The most typical case is that of a system in Restricted or AllSigned where, for whatever reason, you need to run a specific unsigned script. If you have sufficient permissions, you can use:
powershell.exe -ExecutionPolicy Bypass -File .\MiScript.ps1
or in short:
powershell -ep Bypass .\MiScript.ps1
This launches a new instance of PowerShell only for that execution With the Bypass policy, without touching the global configuration. It's convenient, but also the perfect example of why ExecutionPolicies, on their own, don't stop an attacker with interactive access.
There is also a special environment variable, $env:PSExecutionPolicyPreference, which allows temporarily overwriting the policy in the current session:
$env:PSExecutionPolicyPreference = "Bypass"
As long as the variable is set, PowerShell will use that value. You can check it. with:
$env:PSExecutionPolicyPreference
And when you want to return to normal behavior, simply remove the variable:
Remove-Item Env:PSExecutionPolicyPreference
Managing untrusted and blocked scripts
When you download a script from the Internet, Windows usually marks it with Mark of the WebThis causes certain policies (such as RemoteSigned) to treat it as a remote script even though it's on the local disk. The typical symptom is the error message "Script execution is disabled" or "The file is not digitally signed."
If you've reviewed the content and trust it, you can unlock the file without changing the ExecutionPolicy using:
Unblock-File -Path C:\Temp\ScriptDescargado.ps1
once unlockedAnd depending on your policy (for example, RemoteSigned), the script may run if it meets the other requirements. This is a more refined way of working than lowering the policy to Unrestricted.
In environments where AllSigned or RemoteSigned is strictly used, the management of Trust certificates for external publishers This is also important. If you download a script signed by a vendor, you'll need to decide whether to import their certificate as a trusted publisher into the appropriate store, or whether you prefer to re-sign the script with your own internal certificate.
NTFS permissions and Group Policy as reinforcement
In addition to AppLocker, WDAC, and ExecutionPolicy, don't forget the basics: NTFS permissions on the directories where the scripts resideAlthough a user can run a .ps1 file, they should not be able to modify critical scripts that run with high privileges.
Some good practices are:
- Store production scripts in locations where only administrators or a group of operators have write permissions.
- Grant end users only read and execute permissions when absolutely necessary.
- Prevent critical tasks from pulling scripts located in manipulable paths such as Desktop, Downloads, or user profiles.
These restrictions can be reinforced with GPOs that define ACLs on specific foldersas well as with AppLocker policies that only allow scripts to run from controlled paths and, if possible, signed by your corporate CA.
Combining all of this, you get a scenario where Having a malicious .ps1 file is not enough.The file must be in an allowed path, comply with the ExecutionPolicy, pass AppLocker/WDAC, and the attacker also needs sufficient permissions to place or modify it.
Working with this layered approach —script signing, hardened ExecutionPolicy, well-thought-out AppLocker/WDAC, correct NTFS permissions, and some user education— it's perfectly feasible to have intensive automation with PowerShell without turning the environment into the Wild West of scripting.
Passionate writer about the world of bytes and technology in general. I love sharing my knowledge through writing, and that's what I'll do on this blog, show you all the most interesting things about gadgets, software, hardware, tech trends, and more. My goal is to help you navigate the digital world in a simple and entertaining way.