Skip to main content
powershell/PSUseConstrainedLanguageMode is a PSScriptAnalyzer diagnostic emitted by tally for PowerShell snippets embedded in Dockerfiles.

Description

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
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.

Constrained Language Mode Restrictions

Unsigned Scripts (Full CLM Checking)

The following are flagged for unsigned scripts:
  1. Add-Type - Code compilation not permitted
  2. Disallowed COM Objects - Only Scripting.Dictionary, Scripting.FileSystemObject, VBScript.RegExp allowed
  3. Disallowed .NET Types - Only ~70 allowed types (string, int, hashtable, pscredential, etc.)
  4. Type Constraints - On parameters and variables
  5. Type Expressions - Static type references like [Type]::Method()
  6. Type Casts - Converting to disallowed types
  7. Member Invocations - Methods/properties on disallowed types
  8. PowerShell Classes - class keyword not permitted
  9. XAML/WPF - Not permitted
  10. Invoke-Expression - Restricted
  11. Dot-Sourcing - May be restricted depending on the file being sourced
  12. Module Manifest Wildcards - Wildcard exports not recommended
  13. Module Manifest .ps1 Files - Script modules ending with .ps1 not allowed
Always enforced, even for signed scripts

Signed Scripts (Selective Checking)

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)

Configuration

Basic Configuration

Parameters

Enable: bool (Default value is $false)

Enable or disable the rule during ScriptAnalyzer invocation. This rule is disabled by default because not all scripts need CLM compatibility.

IgnoreSignatures: bool (Default value is $false)

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.
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

How to Fix

Replace Add-Type

Use allowed cmdlets or pre-compile assemblies.

Replace Disallowed COM Objects

Use only allowed COM objects (Scripting.Dictionary, Scripting.FileSystemObject, VBScript.RegExp) or PowerShell cmdlets.

Replace Disallowed Types

Use allowed type accelerators ([string], [int], [hashtable], etc.) or allowed cmdlets instead of disallowed .NET types.

Replace PowerShell Classes

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.

Avoid XAML

Don’t use WPF/XAML in CLM-compatible scripts.

Replace Invoke-Expression

Use direct execution (&) or safer alternatives.

Replace Dot-Sourcing

Use modules with Import-Module instead of dot-sourcing when possible.

Fix Module Manifests

  • Replace wildcard exports (*) with explicit lists.
  • Use .psm1 or .dll instead of .ps1 for RootModule/NestedModules.
  • Don’t use ScriptsToProcess. These scripts are loaded in the caller’s scope and are blocked.

Examples

Example 1: Add-Type

Wrong

Correct

Example 2: COM Objects

Wrong

Correct

Example 3: Disallowed Types

Wrong

Correct

Example 4: PowerShell Classes

Wrong

Correct

Example 5: Module Manifests

Wrong

Correct

Example 6: Array Types

Wrong

Correct

Detailed Restrictions

1. Add-Type

Add-Type allows compiling arbitrary C# code and isn’t permitted in CLM. Enforced For: Unsigned scripts only

2. COM Objects

Only three COM objects are allowed:
  • Scripting.Dictionary
  • Scripting.FileSystemObject
  • VBScript.RegExp
All others (Excel.Application, WScript.Shell, etc.) are flagged. Enforced For: Unsigned scripts only

3. .NET Types

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

4. PowerShell Classes

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

5. XAML/WPF

XAML and WPF are not permitted in CLM. Enforced For: Unsigned scripts only

6. Invoke-Expression

Invoke-Expression is restricted in CLM. Enforced For: Unsigned scripts only

7. Dot-Sourcing

Dot-sourcing (. $PSScriptRoot\script.ps1) may be restricted depending on source location. Enforced For: ALL scripts (unsigned and signed)

8. Module Manifest Best Practices

Wildcard Exports

Don’t use * in: FunctionsToExport, CmdletsToExport, AliasesToExport, VariablesToExport Use explicit lists for security and clarity. Enforced For: ALL .psd1 files (unsigned and signed)

Script Module Files

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)

More Information

Source

This rule documentation is adapted from Microsoft’s PSScriptAnalyzer documentation for UseConstrainedLanguageMode, licensed under CC BY 4.0.