| description | ms.date | ms.topic | title |
|---|---|---|---|
Use patterns compatible with Constrained Language Mode |
03/17/2026 |
reference |
UseConstrainedLanguageMode |
Severity Level: Warning
This rule identifies PowerShell patterns that are restricted or not permitted in Constrained Language Mode (CLM).
Constrained Language Mode is a PowerShell security feature that restricts:
- .NET types that can be used
- COM objects that can be instantiated
- Commands that can be executed
- Language features that can be used
CLM is commonly used in:
- Application Control environments (Application Control for Business, AppLocker)
- Just Enough Administration (JEA) endpoints
- Secure environments requiring additional PowerShell restrictions
Signed Script Behavior: Digitally signed scripts from trusted publishers execute in Full Language Mode (FLM) even in CLM environments. The rule detects signature blocks (# SIG # Begin signature block) and adjusts checks accordingly - most restrictions don't apply to signed scripts, but certain checks (dot-sourcing, parameter types, manifest best practices) are always enforced.
Important: The rule performs a simple text check for signature blocks and does NOT validate signature authenticity or certificate trust. Actual signature validation is performed by PowerShell at runtime.
The following are flagged for unsigned scripts:
- Add-Type - Code compilation not permitted
- Disallowed COM Objects - Only Scripting.Dictionary, Scripting.FileSystemObject, VBScript.RegExp allowed
- Disallowed .NET Types - Only ~70 allowed types (string, int, hashtable, pscredential, etc.)
- Type Constraints - On parameters and variables
- Type Expressions - Static type references like
[Type]::Method() - Type Casts - Converting to disallowed types
- Member Invocations - Methods/properties on disallowed types
- PowerShell Classes -
classkeyword not permitted - XAML/WPF - Not permitted
- Invoke-Expression - Restricted
- Dot-Sourcing - May be restricted depending on the file being sourced
- Module Manifest Wildcards - Wildcard exports not recommended
- Module Manifest .ps1 Files - Script modules ending with .ps1 not allowed
Always enforced, even for signed scripts
For scripts with signature blocks, only these are checked:
- Dot-sourcing
- Parameter type constraints
- Module manifest wildcards (.psd1 files)
- Module manifest script modules (.psd1 files)
@{
Rules = @{
PSUseConstrainedLanguageMode = @{
Enable = $true
}
}
}Enable or disable the rule during ScriptAnalyzer invocation. This rule is disabled by default because not all scripts need CLM compatibility.
Control signature detection behavior:
$false(default): Automatically detect signatures. Signed scripts get selective checking, unsigned get full checking.$true: Bypass signature detection. ALL scripts get full CLM checking regardless of signature status.
@{
Rules = @{
PSUseConstrainedLanguageMode = @{
Enable = $true
IgnoreSignatures = $true # Enforce full CLM compliance for all scripts
}
}
}Use IgnoreSignatures = $true when:
- Auditing signed scripts for complete CLM compatibility
- Preparing scripts for untrusted environments
- Enforcing strict CLM compliance organization-wide
- Development/testing to see all potential issues
Use allowed cmdlets or pre-compile assemblies.
Use only allowed COM objects (Scripting.Dictionary, Scripting.FileSystemObject, VBScript.RegExp) or PowerShell cmdlets.
Use allowed type accelerators ([string], [int], [hashtable], etc.) or allowed cmdlets instead of disallowed .NET types.
Use New-Object PSObject with Add-Member or hashtables instead of classes.
Important: [PSCustomObject]@{} syntax is NOT allowed in CLM because it uses type casting.
Don't use WPF/XAML in CLM-compatible scripts.
Use direct execution (&) or safer alternatives.
Use modules with Import-Module instead of dot-sourcing when possible.
- Replace wildcard exports (
*) with explicit lists - Use
.psm1or.dllinstead of.ps1for RootModule/NestedModules - Don't use ScriptsToProcess as it loads in the caller's scope and will be blocked.
Add-Type -TypeDefinition @"
public class Helper {
public static string DoWork() { return "Done"; }
}
"@ # Code sign your scripts/modules using proper signing tools
# (for example, Set-AuthenticodeSignature or external signing processes)
# Use allowed cmdlets instead of Add-Type-defined types where possible
# Or pre-compile, sign, and load the assembly (for example, via Add-Type -Path)$excel = New-Object -ComObject Excel.Application# Use allowed COM object
$dict = New-Object -ComObject Scripting.Dictionary
# Or use PowerShell cmdlets
Import-Excel -Path $file # From ImportExcel module# Type constraint and member invocation flagged
function Download-File {
param([System.Net.WebClient]$Client)
$Client.DownloadString($url)
}
# Type cast and method call flagged
[System.Net.WebClient]$client = New-Object System.Net.WebClient
$data = $client.DownloadData($url)# Use allowed cmdlets
function Download-File {
param([string]$Url)
Invoke-WebRequest -Uri $Url
}
# Use allowed types
function Process-Text {
param([string]$Text)
$upper = $Text.ToUpper() # String methods are allowed
}class MyClass {
[string]$Name
[string]GetInfo() {
return $this.Name
}
}
# Also wrong - uses type cast
$obj = [PSCustomObject]@{
Name = "Test"
}# Option 1: New-Object PSObject with Add-Member
$obj = New-Object PSObject -Property @{
Name = "Test"
}
$obj | Add-Member -MemberType ScriptMethod -Name GetInfo -Value {
return $this.Name
}
Add-Member -InputObject $obj -NotePropertyMembers @{"Number" = 42}
# Option 2: Hashtable
$obj = @{
Name = "Test"
Number = 42
}@{
ModuleVersion = '1.0.0'
RootModule = 'MyModule.ps1' # .ps1 not recommended
FunctionsToExport = '*' # Wildcard not recommended
CmdletsToExport = '*'
}@{
ModuleVersion = '1.0.0'
RootModule = 'MyModule.psm1' # Use .psm1 or .dll
FunctionsToExport = @( # Explicit list
'Get-MyFunction'
'Set-MyFunction'
)
CmdletsToExport = @()
}# Disallowed type in array
param([System.Net.WebClient[]]$Clients)# Allowed types in arrays are fine
param([string[]]$Names)
param([int[]]$Numbers)
param([hashtable[]]$Configuration)Add-Type allows compiling arbitrary C# code and is not permitted in CLM.
Enforced For: Unsigned scripts only
Only three COM objects are allowed:
Scripting.DictionaryScripting.FileSystemObjectVBScript.RegExp
All others (Excel.Application, WScript.Shell, etc.) are flagged.
Enforced For: Unsigned scripts only
Only ~70 allowed types including:
- Primitives:
string,int,bool,byte,char,datetime,decimal,double, etc. - Collections:
hashtable,array,arraylist - PowerShell:
pscredential,psobject,securestring - Utilities:
regex,guid,version,uri,xml - Arrays:
string[],int[][], etc. (array of any allowed type)
The rule checks type usage in:
- Parameter type constraints (always enforced, even for signed scripts)
- Variable type constraints
- New-Object -TypeName
- Type expressions (
[Type]::Method()) - Type casts (
[Type]$variable) - Member invocations on typed variables
Enforced For: Parameter constraints always; others unsigned only
The class keyword is not permitted. Use New-Object PSObject with Add-Member or hashtables.
Note: [PSCustomObject]@{} is also not allowed because it uses type casting.
Enforced For: Unsigned scripts only
XAML and WPF are not permitted in CLM.
Enforced For: Unsigned scripts only
Invoke-Expression is restricted in CLM.
Enforced For: Unsigned scripts only
Dot-sourcing (. $PSScriptRoot\script.ps1) may be restricted depending on source location.
Enforced For: ALL scripts (unsigned and signed)
Don't use * in: FunctionsToExport, CmdletsToExport, AliasesToExport, VariablesToExport
Use explicit lists for security and clarity.
Enforced For: ALL .psd1 files (unsigned and signed)
Don't use .ps1 files in: RootModule, ModuleToProcess, NestedModules
Use .psm1 (script modules) or .dll (binary modules) for better performance and compatibility.
Enforced For: ALL .psd1 files (unsigned and signed)