๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

PowerShell Constrained Language Mode: Audit and Enforcement in 2026

PowerShell Constrained Language Mode: Audit and Enforcement in 2026
PowerShell Constrained Language Mode - Dargslan 2026

PowerShell is two languages in one: a shell, and a near-complete .NET runtime. Constrained Language Mode (CLM) takes the second one away. With CLM enforced, scripts cannot call arbitrary .NET types, cannot use COM, and cannot use Add-Type. Combined with WDAC or AppLocker in script-mode-enforce, it is the most effective single control against PowerShell-based attacks.

What CLM blocks

The official boundary: in ConstrainedLanguage, the engine refuses to use any .NET type that is not on a small allow-list. Practical consequences: [System.Net.WebClient], [Reflection.Assembly], Add-Type, New-Object -ComObject all fail. Cmdlets and functions still work; they were always cmdlets.

Detect the current mode

$ExecutionContext.SessionState.LanguageMode

The four values are FullLanguage, RestrictedLanguage, NoLanguage and ConstrainedLanguage. On a default Windows install you will see FullLanguage. CLM is the goal.

How CLM is actually triggered

CLM is not a setting on its own. It is automatically applied when the engine detects an application-control policy in enforce mode (WDAC or AppLocker) that covers scripts. The trigger logic, simplified: if AppLocker is in Enforce for script files on the host, or WDAC is in user-mode enforce with script enforcement, the next PowerShell session starts in CLM.

Enforce via WDAC

Windows Defender Application Control is the modern path. A starter audit-mode policy:

$pol = 'C:\Audit.xml'
New-CIPolicy -FilePath $pol -Level Publisher -UserPEs -ScanPath C:\
ConvertFrom-CIPolicy -XmlFilePath $pol -BinaryFilePath C:\Audit.cip
# Apply audit policy:
Copy-Item C:\Audit.cip 'C:\Windows\System32\CodeIntegrity\SiPolicy.p7b'
# Reboot, observe, then flip Option 0 (Enabled:UMCI) and remove audit options.

Enforce via AppLocker

AppLocker is the legacy but still supported path. Configure script rules in Local Security Policy > Application Control Policies > AppLocker > Script Rules. Set a default deny with allow-list for your signed admin scripts. As soon as AppLocker is enforcing scripts, all interactive PowerShell sessions on that host start in ConstrainedLanguage.

Compatibility considerations

The two things that break:

  • Scripts that use Add-Type to compile inline C# โ€” refactor to a signed module.
  • Scripts that call [Reflection.Assembly]::LoadFrom() โ€” same fix.

Most operational scripts (cmdlet-driven) are unaffected. Run a one-week audit pass before flipping enforce.

Fleet-wide audit

One-liner to collect the language mode from every reachable host:

Invoke-Command -ComputerName (Get-ADComputer -Filter * | %{$_.Name}) -ScriptBlock {
    [pscustomobject]@{
        Computer    = $env:COMPUTERNAME
        LangMode    = $ExecutionContext.SessionState.LanguageMode
        AppLocker   = (Get-AppLockerPolicy -Effective).RuleCollections |
                        Where-Object EnforcementMode -eq 'Enabled' |
                        Select-Object -ExpandProperty RuleCollectionType
    }
} | Export-Csv clm-fleet.csv -NoType

Application Control: WDAC and AppLocker as the real enforcement layer

Constrained Language Mode is enforced by PowerShell. The signal that PowerShell uses to know it should enforce CLM comes from a higher authority โ€” Windows Defender Application Control (WDAC) or AppLocker. Without one of those, an attacker can simply set $ExecutionContext.SessionState.LanguageMode = 'FullLanguage' and walk around it.

Audit-mode WDAC policy first

Deploy a WDAC policy in audit mode for two weeks before enforcement. Audit mode logs every binary that would have been blocked to Microsoft-Windows-CodeIntegrity/Operational. Review those logs and add publisher rules for legitimate tooling before flipping to enforcement.

$policyPath = 'C:\wdac\base.xml'

# Start from the Microsoft-recommended template
Copy-Item "$env:windir\schemas\CodeIntegrity\ExamplePolicies\AllowMicrosoft.xml" `
          $policyPath

# Set to audit
Set-RuleOption -FilePath $policyPath -Option 3

# Compile and deploy
ConvertFrom-CIPolicy -XmlFilePath $policyPath -BinaryFilePath 'C:\wdac\base.cip'
Copy-Item 'C:\wdac\base.cip' 'C:\Windows\System32\CodeIntegrity\SiPolicy.p7b'

Reboot, run the workstation through normal usage, then mine the event log:

Get-WinEvent -LogName Microsoft-Windows-CodeIntegrity/Operational |
    Where-Object Id -in 3076,3077 |
    Group-Object { $_.Properties[1].Value } |
    Sort-Object Count -Descending |
    Select-Object -First 20 Count, Name

Trust by publisher, not by hash

Hash-based allow rules look secure but break on every patch. Publisher rules โ€” signed by the vendor's certificate โ€” survive updates and are the maintainable choice. Generate them with New-CIPolicyRule -Level Publisher against a known-good install image.

Verify CLM is actually engaged

A single command tells you what mode the current PowerShell session is in:

$ExecutionContext.SessionState.LanguageMode
# ConstrainedLanguage  -> WDAC/AppLocker enforcement is signalling correctly
# FullLanguage         -> no enforcement, you are unprotected

Run this from a non-admin user in your golden-image VM. If it returns FullLanguage, your application control policy is not deployed or not enforcing.

Common pitfalls

  • Enforcement without an audit phase. Day-one enforcement breaks line-of-business apps and the rollback is painful. Two weeks of audit is the minimum.
  • Allowing powershell_ise.exe in FullLanguage. The ISE bypasses CLM by design. Block it on managed endpoints and force users to use the regular console.
  • Trusting Set-AuthenticodeSignature with a self-signed cert. Operators can re-sign anything with their own cert and run it. Restrict signing certs to a small, audited group.
  • Forgetting Office macros. A signed VBA macro can call System.Diagnostics.Process and bypass CLM entirely. Disable macros from the internet via Group Policy.
  • No rollback plan. A bad WDAC policy can soft-brick a machine. Always keep a known-good SiPolicy.p7b and a documented WinPE recovery procedure.

Rolling out CLM in production: a phased plan

Constrained Language Mode breaks more legitimate work than any other PowerShell hardening control. The trick is a phased deployment that catches the breakage in audit logs before users hit it in their daily flow.

  1. Phase 0 โ€” inventory PowerShell usage. Pull a week of ScriptBlockLogging events from one workstation OU. Identify every script and module in active use. Anything that calls [System.Reflection], COM objects, or unconstrained type accelerators is a CLM hazard.
  2. Phase 1 โ€” sign trusted code. Issue a code-signing certificate. Sign every internal module with Set-AuthenticodeSignature. Add the signing cert to the WDAC publisher allow-list so signed code runs in FullLanguage even on a CLM-constrained host.
  3. Phase 2 โ€” audit-mode WDAC. Deploy WDAC in audit mode for two weeks. Mine the CodeIntegrity log nightly for blocks that would have happened. Add publisher rules for any vendor binary that surfaces.
  4. Phase 3 โ€” pilot enforcement. Flip 10 workstations to enforcement. Help-desk handles the first wave of "PowerShell behaves weirdly" tickets, you turn each one into a publisher rule, and the queue drains within a week.
  5. Phase 4 โ€” fleet enforcement. Promote to the workstation OU GPO. Server OU follows a month later, after server-side scripts have been audited and signed.
  6. Phase 5 โ€” measure. Track the count of FullLanguage sessions in your SIEM. Target zero on enforced hosts, except known exceptions documented in source control.

The most expensive mistake is to flip CLM on without signing internal modules. Operators see arbitrary script breakage with no actionable error, file tickets, lose trust in the security team, and the rollback costs more political capital than the original project earned. Sign first, enforce second.

Sustaining CLM: monitoring, exceptions and the team handoff

Constrained Language Mode rolled out and forgotten about is CLM that quietly disappears. Operators learn the workarounds, exceptions accumulate, and the protection model erodes one approval at a time. The discipline is not technical โ€” it is operational.

Build a weekly compliance dashboard that shows two numbers per OU: the count of hosts reporting FullLanguage in their PowerShell session log, and the count of WDAC audit-mode events. The first should trend toward zero on enforced OUs; any non-zero count is a host where the policy has not deployed correctly, usually because it was excluded from the GPO or has never restarted since the policy applied. The second should trend toward zero across the fleet; persistent audit events flag tooling that needs a publisher rule or signing.

Maintain an exception register in source control. Every host that runs in FullLanguage for a documented business reason โ€” typically a developer workstation or a build agent โ€” gets an entry: hostname, business justification, expiry date, and the change ticket that approved it. Quarterly review the register and either re-justify, sign the binary that needed the exception, or remove the host from the exception list. Without expiry dates, exceptions become permanent; with them, the list naturally shrinks.

The hardest part of long-term CLM is the team handoff. The engineer who built the deployment understands every nuance โ€” which line-of-business app needed which publisher rule, why the vendor's installer is signed by an unfamiliar certificate, what to do when an audit-mode event fires for a binary nobody recognises. Capture all of it in a runbook the next operator can follow with no context. The runbook is the deliverable that makes the deployment survive its first reorganisation.

Finally, integrate CLM signal into the security operations workflow. A script that lands on a host in FullLanguage when the host should be constrained is a high-confidence indicator of policy bypass. Route the event to the SIEM with a real alert priority, not the same noise level as informational logging.

FAQ

Does CLM apply to my admin session too?

Yes โ€” that is the point. Sign your admin scripts and add them to the allow-list.

Can a local admin bypass it?

Only by altering the application-control policy itself. Audit the policy file's ACL.

Does PowerShell 7 honour CLM?

Yes, on Windows. Linux PowerShell does not have CLM at the engine level.

What about powershell.exe -version 2?

PowerShell v2 should be removed from every host. WDAC blocks loading it; AppLocker can deny by file path.

Does CLM affect PowerShell ISE?

The ISE itself ignores CLM, which is why you should remove it from managed endpoints. Use VS Code with the PowerShell extension instead โ€” it honours the language mode of the underlying session.

Will CLM break my existing scripts?

Some, yes. Anything calling .NET reflection, COM objects, or unconstrained type accelerators will fail. The fix is to refactor those calls into a signed module that is allowed to run in FullLanguage, while the rest of the script stays constrained.

Can I enforce CLM without WDAC or AppLocker?

You can set the environment variable __PSLockdownPolicy=4, but it is trivially reversed and not a control you can defend in an audit. Use WDAC or AppLocker.

How does this interact with PowerShell 7?

PowerShell 7 honours WDAC and AppLocker the same way as 5.1. The policies are OS-level, not version-specific.

Related Dargslan resources

Share this article:
Dargslan Editorial Team (Dargslan)
About the Author

Dargslan Editorial Team (Dargslan)

Collective of Software Developers, System Administrators, DevOps Engineers, and IT Authors

Dargslan is an independent technology publishing collective formed by experienced software developers, system administrators, and IT specialists.

The Dargslan editorial team works collaboratively to create practical, hands-on technology books focused on real-world use cases. Each publication is developed, reviewed, and...

Programming Languages Linux Administration Web Development Cybersecurity Networking

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.