PowerShell Constrained Language Mode Enterprise Deployment: WDAC Integration, JEA, and Bypass Detection

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
PowerShell Constrained Language Mode was blocked from deployment in over 60% of environments that attempted it in 2024, according to Microsoft's deployment data, and the failure mode is almost always the same: someone enables CLM via Group Policy, breaks two monitoring agents, a backup script, and a scheduled task that IT operations depends on, and the CLM policy is revoked before the root cause analysis is finished. The break was not caused by CLM being too aggressive — it was caused by deploying CLM without a compatibility audit.
The correct deployment sequence inverts this order: collect all PowerShell scripts used in the environment, scan for CLM-restricted constructs using automated pattern matching, remediate incompatible scripts or create documented exceptions for vendor scripts that cannot be modified, then deploy CLM in audit mode (which logs violations without enforcing) and monitor for two weeks before switching to enforce mode. Done this way, CLM deployment on 5,000 endpoints typically surfaces 12-20 scripts requiring remediation, takes three to four weeks of engineering time, and produces an environment where the entire class of Add-Type and reflection-based PowerShell attacks fails silently.
Deployment: WDAC-enforced CLM with audit mode rollout
WDAC-enforced CLM is more durable than AppLocker-enforced CLM because WDAC enforcement happens in the kernel before PowerShell initializes, making it significantly harder for attackers with user-mode access to bypass. The WDAC policy authoring process uses New-CIPolicy to scan trusted binaries and build a publisher-based allowlist, which the policy then uses to determine whether a PowerShell script is signed by a trusted publisher (Full Language Mode) or unsigned (Constrained Language Mode). Deploying in audit mode first is the critical step that transforms CLM from a compatibility disaster into a controlled rollout.
Deploy WDAC in audit mode for two weeks and collect Code Integrity event logs before switching to enforce
Run New-CIPolicy -Level Publisher -Fallback Hash -UserPEs -FilePath .\AuditPolicy.xml then set the policy option to audit mode (set policy option 3 in the XML: <Rule><Option>Enabled:Audit Mode</Option></Rule>) before converting to binary and deploying via Group Policy. In audit mode, WDAC logs every script block that would be blocked by CLM enforcement as Event ID 3076 in the Microsoft-Windows-CodeIntegrity/Operational log without actually blocking anything. Collect these events from all endpoints using Windows Event Forwarding for two weeks, extract the unique file names and hash values from the 3076 events, and use them to build the remediation list. After remediation is complete, remove the audit mode option from the policy XML, convert to binary, and deploy the enforcing policy. This two-week audit period catches every compatibility issue before any user impact occurs.
Disable PowerShell v2 immediately to block the most common CLM bypass before deploying CLM
Block the PowerShell v2 downgrade bypass before deploying CLM by disabling the Windows PowerShell 2.0 optional feature across all managed endpoints: use a Group Policy Software Installation policy or a Configuration Manager package that runs Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root which removes the v2 PowerShell engine binary. Verify removal with Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root which should return State: Disabled. Without this step, CLM is bypassed by any attacker who runs powershell.exe -version 2, which is a one-line technique that is documented in every PowerShell red team playbook. Disable v2 before or simultaneously with CLM deployment, never after, since deploying CLM without blocking the v2 downgrade provides false confidence that PowerShell is restricted when the restriction is trivially bypassed.
Detection: script block logging and CLM bypass hunting
PowerShell CLM enforcement without script block logging is a control that blocks attacks but provides no visibility into bypass attempts. Script block logging, enabled via Group Policy and forwarded to the SIEM, provides event-level telemetry on every PowerShell execution including the full script content. This telemetry enables detection of CLM bypass attempts, living-off-the-land techniques, and legitimate scripts that are being misused by attackers who have compromised an authorized account.
Create SIEM correlation rules for the specific Event ID 4104 patterns that indicate CLM bypass or malicious PowerShell
Build four specific SIEM detection rules against Event ID 4104 script block content: alert on [System.Reflection.Assembly] or System.Runtime.InteropServices which indicate reflection-based assembly loading or P/Invoke for shellcode injection; alert on [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace which indicates a new unconstrained runspace being created from within CLM; alert on -version 2 or -v 2 in PowerShell process command lines (from Event ID 4688 or Sysmon Event ID 1) which indicates v2 downgrade before the v2 feature is disabled; and alert on Invoke-Expression combined with Base64Decode or FromBase64String patterns which indicate obfuscated payload execution even under CLM via the allowed Invoke-Expression alternative paths. Tune these rules against your environment's legitimate PowerShell usage during the two-week audit period before CLM enforcement to establish a false positive baseline.
Review JEA transcript logs as a privileged action audit trail for high-value servers
Enable JEA transcript logging in the session configuration file with TranscriptDirectory = 'C:\JEA\Transcripts' which records every command executed in every JEA session to a timestamped transcript file including the connected user's identity, the virtual account used, and every cmdlet invocation with its parameters. Forward transcript content to the SIEM by parsing the transcript files with a file-based log collector or by shipping the transcript directory to a centralized log store. JEA transcripts are the privileged action audit trail for servers where JEA has replaced direct administrator remoting — they provide evidence of what any administrator actually did during a remote session without requiring a session recording solution. Alert on JEA sessions where unexpected cmdlets appear in transcripts, which indicates either a configuration gap in the role capability file or a JEA session being used for tasks outside its intended scope.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
The bottom line
PowerShell Constrained Language Mode deployed correctly is one of the highest-impact endpoint hardening controls available for Windows environments, blocking the entire class of Add-Type, reflection, and COM-based PowerShell attacks that constitute the majority of post-exploitation PowerShell tradecraft. Deploy via WDAC rather than AppLocker for kernel-level enforcement, use a two-week audit mode period to collect all CLM incompatibilities before enforcing, disable PowerShell v2 simultaneously to block the downgrade bypass, and enable script block logging to capture Event ID 4104 telemetry for bypass detection. Pair CLM with JEA endpoints that replace privileged PowerShell remoting sessions with least-privilege role-based access. The deployment takes four to six weeks end-to-end and eliminates PowerShell as an effective post-exploitation tool on enforced endpoints.
Frequently asked questions
How does PowerShell Constrained Language Mode work and what does it block?
PowerShell Constrained Language Mode restricts the PowerShell language to a subset of features that excludes the capabilities attackers rely on for post-exploitation. CLM blocks Add-Type which is used to compile and execute C# or other .NET code at runtime, direct .NET type access via [System.Reflection.Assembly]::Load() and similar reflection APIs which are used to load malicious assemblies, COM object instantiation which is used to invoke WScript.Shell and other automation objects, the Invoke-Expression cmdlet and [scriptblock]::Create() which are used to execute dynamically constructed code, and setting variable values using .NET property setters on restricted types. CLM allows standard pipeline operations, most built-in cmdlets, operators, and variables that legitimate administrative scripts use. The effect is that most attacker-written PowerShell payloads — which typically rely on Add-Type, reflection, or Invoke-Expression to load shellcode or execute .NET malware — fail under CLM, while most legitimate administrative automation continues to function.
How do I deploy PowerShell CLM via WDAC rather than AppLocker?
Deploy PowerShell CLM via Windows Defender Application Control by creating a WDAC code integrity policy that sets the script enforcement level to enforce CLM for unsigned scripts. WDAC-enforced CLM is more robust than AppLocker-enforced CLM because WDAC operates at the kernel level and is significantly harder to bypass. To create a policy, use New-CIPolicy -Level Publisher -FilePath C:\policy.xml -UserPEs -ScanPath C:\Windows to generate a base policy from signed Windows binaries. Modify the policy to add the script enforcement level: add <Rules> with <Rule><Option>Enabled:UMCI</Option></Rule> for user mode code integrity and <Rule><Option>Enabled:Allow Supplemental Policies</Option></Rule>. Convert the XML to binary with ConvertFrom-CIPolicy -XmlFilePath C:\policy.xml -BinaryFilePath C:\policy.bin and deploy via Group Policy by copying the binary to the CodeIntegrity\CiPolicies\Active directory and creating the corresponding registry key. Test on a single device first by verifying $ExecutionContext.SessionState.LanguageMode returns ConstrainedLanguage before deploying via Group Policy to a broader set of endpoints.
How do I configure script block logging to detect CLM bypass attempts?
Configure PowerShell script block logging via Group Policy to capture every PowerShell script block executed, whether in CLM or not, which provides the visibility needed to detect bypass attempts. Enable script block logging via Group Policy: Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell > Turn on PowerShell Script Block Logging, set to Enabled. This writes Event ID 4104 to the Microsoft-Windows-PowerShell/Operational log for every script block executed, with the full script content in the event. Forward these events to your SIEM using a Windows Event Forwarding subscription or the Splunk/Elastic Windows agent. For CLM bypass detection, write SIEM rules that alert on Event ID 4104 events containing indicators of bypass attempts: [System.Reflection.Assembly]::Load (reflection-based assembly loading), Add-Type -TypeDefinition (runtime compilation), [System.Runtime.InteropServices.Marshal] (P/Invoke for shellcode), or powershell.exe -version 2 in the script block content. These patterns appearing in 4104 events indicate either a legitimate compatibility issue or an active bypass attempt, both of which warrant investigation.
How do I configure Just Enough Administration (JEA) to replace privileged PowerShell remoting?
Configure JEA to replace unrestricted administrator PowerShell remoting sessions with least-privilege endpoints that grant specific users access to only the cmdlets they need. Create a role capability file with New-PSRoleCapabilityFile -Path C:\JEA\Roles\NetworkAdmin.psrc which defines the allowed cmdlets (VisibleCmdlets), functions (VisibleFunctions), external commands (VisibleExternalCommands), and aliases for the NetworkAdmin role. Create a session configuration file with New-PSSessionConfigurationFile -Path C:\JEA\NetworkAdmin.pssc -RoleDefinitions @{'DOMAIN\NetworkAdmins' = @{RoleCapabilities = 'NetworkAdmin'}} -RunAsVirtualAccount which maps the domain group to the role and runs the session as a virtual local admin account without requiring the connecting user to have local admin rights. Register the endpoint with Register-PSSessionConfiguration -Name NetworkAdmin -Path C:\JEA\NetworkAdmin.pssc -Force. JEA users connect with Enter-PSSession -ComputerName server1 -ConfigurationName NetworkAdmin and can only execute the cmdlets defined in the role capability file, even though the underlying virtual account has elevated privileges.
What are the most common CLM bypass techniques and how do I detect or prevent them?
The most common PowerShell CLM bypass techniques and their countermeasures: PowerShell v2 downgrade (powershell.exe -version 2) bypasses CLM entirely because v2 predates the feature — prevent by disabling the Windows PowerShell 2.0 Features optional Windows component via DISM: dism /online /Disable-Feature:MicrosoftWindowsPowerShellV2Root. Runspace creation via reflection uses [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() to create an unconstrained runspace from within CLM — detect by alerting on 4104 events containing RunspaceFactory. Bypasses via installutil.exe, regsvcs.exe, and other LOLBins that execute .NET code as a trusted binary sidestepping CLM — prevent with WDAC application control rules that restrict allowed binary execution. Language mode overriding via direct registry manipulation of the HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell key — detect with registry auditing on that key path. All of these bypass techniques generate detectable artifacts in Event ID 4104 or Windows Security events, making script block logging the critical detection layer regardless of which bypass is attempted.
How do I audit existing PowerShell scripts for CLM compatibility before deploying CLM?
Audit existing PowerShell scripts for CLM compatibility by collecting all scripts used in the environment, scanning them for CLM-restricted language constructs, and remediating incompatible scripts before deploying CLM. Collect scripts from common locations: C:\Windows\System32\WindowsPowerShell\v1.0\Modules, C:\Program Files\WindowsPowerShell\Modules, scheduled task scripts, and backup/monitoring agent scripts. Scan for CLM-restricted constructs using a compatibility scanner: grep (or Select-String) for Add-Type, [System.Reflection, Invoke-Expression, [scriptblock]::Create, and COM object creation patterns like New-Object -ComObject. For each match, evaluate whether the script is essential and whether the CLM-restricted construct can be replaced with a cmdlet-based equivalent. Common remediations: replace Invoke-Expression with direct cmdlet calls, replace Add-Type with a pre-compiled DLL loaded via Import-Module, and replace COM object automation with native PowerShell cmdlets. Test the compatibility of critical scripts by creating a WDAC audit mode policy (without enforcement) and reviewing the Code Integrity event log (Event ID 3076) for script violations before switching to enforcement.
How do I handle CLM compatibility failures in vendor and third-party scripts?
Handle CLM compatibility failures in vendor and third-party scripts using code signing or WDAC rule exceptions that allow specific trusted scripts to run in Full Language Mode while maintaining CLM for all unsigned scripts. The cleanest approach is to require vendors to code-sign their scripts with a trusted certificate from your PKI: a WDAC policy can be configured to allow signed scripts from trusted publishers to run in Full Language Mode while applying CLM to all unsigned scripts. For vendor scripts that cannot be signed, create a WDAC supplemental policy that allows the specific script hashes to run without CLM restriction — this maintains CLM for all other scripts while creating a documented exception for the specific vendor scripts that require it. Document each CLM exception in a risk register with the vendor name, script purpose, exception type (signature-based or hash-based), and review date. Hash-based exceptions require updating when the vendor releases new versions, so prefer signature-based exceptions for regularly updated vendor software.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
